<!--toc--> |
|
## Introduction |
|
For serving static files the web server it the best option, lighttpd, nginx or Apache would do much better job then you probably can. |
|
However, cppcms can be used to serve static files so that the same permission logic can be used as other dynamic contents. |
However, when you want to apply the same permission logic to the static files as used for other dynamic contents, |
you can't use the web server directly. |
|
When serving static files you should be aware of two very important factors: |
|
|
1. Security |
2. Performance |
|
## Security |
|
|
Let's start from the first. I assume you want to let user |
to download certain files that you are controlling. |
|
For url like |
|
/some/files/[...] <- these are allowed |
|
Things you should **never do:** |
|
class my_server { |
|
my_server(...) |
{ |
dispatcher().assign("/some/files/(.*)",&my_server::serve_file,this,1) |
} |
void serve_file(std::string file_name) |
{ |
|
std::ifstream f(("some_dir_name/" + file_name).c_str()); |
if(!f) { |
response().status(404) |
} |
else { |
response().content_type("application/octet-stream") |
response().out() << f.rdbuf(); |
} |
} |
... |
} |
|
|
Because attacker can create path |
|
/some/files/../../../etc/some_secret.txt |
|
And read it... It would be **very bad**. |
|
|
If you want to serve a file create a very strict **white** list of allowed names like: |
|
|
/some/files/([a-z_0-9\.]+\.txt) |
|
Or even better convert the name to sha1 or md5 representation and save files using names generated |
from their hash. |
|
For example: |
|
name = some_dir_per_user + "/" + cppcms::util::md5hex(file_name) + ".dat"; |
|
std::ifstream f(name.c_str()); |
|
|
## Performance |
|
For small files (several K or even MB) |
|
response().out() << f.rdbuf(); |
|
Is fine and works very well. |
|
For the big files it is better to use headers like X-Send-File that are |
supported by lighttpd, apache and nginx. |
|
<http://redmine.lighttpd.net/projects/1/wiki/X-LIGHTTPD-send-file> |
|
So instead of calling |
|
|
response().content_type("application/octet-stream") |
response().out() << f.rdbuf(); |
|
You call: |
|
response().content_type("application/octet-stream") |
response().set_header("X-Lighttpd-Send-File",pysical_file_name); |
|
|
This is specially critical for big files were the |
server may for example save the entire FastCGI output |
to temporary file and before serving it (Nginx) or |
cache entire (huge) buffer in the memory (Lighttpd) |
|
`X-SendFile` usually much more effective than |
serving files via FastCGI as it reduces the data transfer |
over IPC and sometimes does not even copy file |
data to the user space. |
|
|
|
---------------- |