nghttpd: Read /etc/mime.types to set content-type header field

User can change file name using --mime-types-file option.
This commit is contained in:
Tatsuhiro Tsujikawa 2015-10-29 00:21:36 +09:00
parent ecb4a208fb
commit f29ccc9c20
5 changed files with 96 additions and 13 deletions

View File

@ -100,11 +100,12 @@ template <typename Array> void append_nv(Stream *stream, const Array &nva) {
} // namespace } // namespace
Config::Config() Config::Config()
: stream_read_timeout(1_min), stream_write_timeout(1_min), : mime_types_file("/etc/mime.types"), stream_read_timeout(1_min),
data_ptr(nullptr), padding(0), num_worker(1), max_concurrent_streams(100), stream_write_timeout(1_min), data_ptr(nullptr), padding(0), num_worker(1),
header_table_size(-1), port(0), verbose(false), daemon(false), max_concurrent_streams(100), header_table_size(-1), port(0),
verify_client(false), no_tls(false), error_gzip(false), verbose(false), daemon(false), verify_client(false), no_tls(false),
early_response(false), hexdump(false), echo_upload(false) {} error_gzip(false), early_response(false), hexdump(false),
echo_upload(false) {}
Config::~Config() {} Config::~Config() {}
@ -739,6 +740,7 @@ int Http2Handler::verify_npn_result() {
int Http2Handler::submit_file_response(const std::string &status, int Http2Handler::submit_file_response(const std::string &status,
Stream *stream, time_t last_modified, Stream *stream, time_t last_modified,
off_t file_length, off_t file_length,
const std::string *content_type,
nghttp2_data_provider *data_prd) { nghttp2_data_provider *data_prd) {
std::string content_length = util::utos(file_length); std::string content_length = util::utos(file_length);
std::string last_modified_str; std::string last_modified_str;
@ -747,12 +749,16 @@ int Http2Handler::submit_file_response(const std::string &status,
http2::make_nv_ls("content-length", content_length), http2::make_nv_ls("content-length", content_length),
http2::make_nv_ll("cache-control", "max-age=3600"), http2::make_nv_ll("cache-control", "max-age=3600"),
http2::make_nv_ls("date", sessions_->get_cached_date()), http2::make_nv_ls("date", sessions_->get_cached_date()),
http2::make_nv_ll("", ""), http2::make_nv_ll("", "")); http2::make_nv_ll("", ""), http2::make_nv_ll("", ""),
http2::make_nv_ll("", ""));
size_t nvlen = 5; size_t nvlen = 5;
if (last_modified != 0) { if (last_modified != 0) {
last_modified_str = util::http_date(last_modified); last_modified_str = util::http_date(last_modified);
nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str); nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str);
} }
if (content_type) {
nva[nvlen++] = http2::make_nv_ls("content-type", *content_type);
}
auto &trailer = get_config()->trailer; auto &trailer = get_config()->trailer;
std::string trailer_names; std::string trailer_names;
if (!trailer.empty()) { if (!trailer.empty()) {
@ -974,7 +980,7 @@ bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
// Ordinary request never start with "echo:". The length is 0 for // Ordinary request never start with "echo:". The length is 0 for
// now. We will update it when we get whole request body. // now. We will update it when we get whole request body.
stream->file_ent = sessions->cache_fd(std::string("echo:") + tempfn, stream->file_ent = sessions->cache_fd(std::string("echo:") + tempfn,
FileEntry(tempfn, 0, 0, fd)); FileEntry(tempfn, 0, 0, fd, nullptr));
stream->echo_upload = true; stream->echo_upload = true;
return true; return true;
} }
@ -1100,8 +1106,28 @@ void prepare_response(Stream *stream, Http2Handler *hd,
return; return;
} }
const std::string *content_type = nullptr;
if (path[path.size() - 1] == '/') {
static const std::string TEXT_HTML = "text/html";
content_type = &TEXT_HTML;
} else {
auto ext = path.c_str() + path.size() - 1;
for (; path.c_str() < ext && *ext != '.' && *ext != '/'; --ext)
;
if (*ext == '.') {
++ext;
const auto &mime_types = hd->get_config()->mime_types;
auto content_type_itr = mime_types.find(ext);
if (content_type_itr != std::end(mime_types)) {
content_type = &(*content_type_itr).second;
}
}
}
file_ent = sessions->cache_fd( file_ent = sessions->cache_fd(
path, FileEntry(path, buf.st_size, buf.st_mtime, file)); path, FileEntry(path, buf.st_size, buf.st_mtime, file, content_type));
} }
stream->file_ent = file_ent; stream->file_ent = file_ent;
@ -1116,7 +1142,7 @@ void prepare_response(Stream *stream, Http2Handler *hd,
stream->headers)->value; stream->headers)->value;
if (method == "HEAD") { if (method == "HEAD") {
hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length, hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length,
nullptr); file_ent->content_type, nullptr);
return; return;
} }
@ -1128,7 +1154,7 @@ void prepare_response(Stream *stream, Http2Handler *hd,
data_prd.read_callback = file_read_callback; data_prd.read_callback = file_read_callback;
hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length, hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length,
&data_prd); file_ent->content_type, &data_prd);
} }
} // namespace } // namespace
@ -1625,7 +1651,7 @@ FileEntry make_status_body(int status, uint16_t port) {
assert(0); assert(0);
} }
return FileEntry(util::utos(status), nwrite, 0, fd); return FileEntry(util::utos(status), nwrite, 0, fd, nullptr);
} }
} // namespace } // namespace

View File

@ -51,6 +51,7 @@ namespace nghttp2 {
struct Config { struct Config {
std::map<std::string, std::vector<std::string>> push; std::map<std::string, std::vector<std::string>> push;
std::map<std::string, std::string> mime_types;
Headers trailer; Headers trailer;
std::string htdocs; std::string htdocs;
std::string host; std::string host;
@ -58,6 +59,7 @@ struct Config {
std::string cert_file; std::string cert_file;
std::string dh_param_file; std::string dh_param_file;
std::string address; std::string address;
std::string mime_types_file;
ev_tstamp stream_read_timeout; ev_tstamp stream_read_timeout;
ev_tstamp stream_write_timeout; ev_tstamp stream_write_timeout;
void *data_ptr; void *data_ptr;
@ -81,13 +83,15 @@ struct Config {
class Http2Handler; class Http2Handler;
struct FileEntry { struct FileEntry {
FileEntry(std::string path, int64_t length, int64_t mtime, int fd) FileEntry(std::string path, int64_t length, int64_t mtime, int fd,
const std::string *content_type)
: path(std::move(path)), length(length), mtime(mtime), dlprev(nullptr), : path(std::move(path)), length(length), mtime(mtime), dlprev(nullptr),
dlnext(nullptr), fd(fd), usecount(1) {} dlnext(nullptr), content_type(content_type), fd(fd), usecount(1) {}
std::string path; std::string path;
int64_t length; int64_t length;
int64_t mtime; int64_t mtime;
FileEntry *dlprev, *dlnext; FileEntry *dlprev, *dlnext;
const std::string *content_type;
int fd; int fd;
int usecount; int usecount;
}; };
@ -123,6 +127,7 @@ public:
int submit_file_response(const std::string &status, Stream *stream, int submit_file_response(const std::string &status, Stream *stream,
time_t last_modified, off_t file_length, time_t last_modified, off_t file_length,
const std::string *content_type,
nghttp2_data_provider *data_prd); nghttp2_data_provider *data_prd);
int submit_response(const std::string &status, int32_t stream_id, int submit_response(const std::string &status, int32_t stream_id,

View File

@ -160,6 +160,10 @@ Options:
are used. are used.
--echo-upload --echo-upload
Send back uploaded content if method is POST or PUT. Send back uploaded content if method is POST or PUT.
--mime-types-file=<PATH>
Path to file that contains MIME media types and the
extensions that represent them.
Default: )" << config.mime_types_file << R"(
--version Display version information and exit. --version Display version information and exit.
-h, --help Display this help and exit. -h, --help Display this help and exit.
@ -202,6 +206,7 @@ int main(int argc, char **argv) {
{"trailer", required_argument, &flag, 6}, {"trailer", required_argument, &flag, 6},
{"hexdump", no_argument, &flag, 7}, {"hexdump", no_argument, &flag, 7},
{"echo-upload", no_argument, &flag, 8}, {"echo-upload", no_argument, &flag, 8},
{"mime-types-file", required_argument, &flag, 9},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:", long_options, int c = getopt_long(argc, argv, "DVb:c:d:ehm:n:p:va:", long_options,
@ -328,6 +333,10 @@ int main(int argc, char **argv) {
// echo-upload option // echo-upload option
config.echo_upload = true; config.echo_upload = true;
break; break;
case 9:
// mime-types-file option
config.mime_types_file = optarg;
break;
} }
break; break;
default: default:
@ -366,6 +375,8 @@ int main(int argc, char **argv) {
config.htdocs = "./"; config.htdocs = "./";
} }
config.mime_types = util::read_mime_types(config.mime_types_file.c_str());
set_color_output(color || isatty(fileno(stdout))); set_color_output(color || isatty(fileno(stdout)));
struct sigaction act {}; struct sigaction act {};

View File

@ -52,6 +52,7 @@
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <fstream>
#include <nghttp2/nghttp2.h> #include <nghttp2/nghttp2.h>
@ -1217,6 +1218,43 @@ uint64_t get_uint64(const uint8_t *data) {
return n; return n;
} }
std::map<std::string, std::string> read_mime_types(const char *filename) {
std::map<std::string, std::string> res;
std::ifstream infile(filename);
if (!infile) {
std::cerr << "Could not open mime types file: " << filename << std::endl;
return res;
}
auto delim_pred = [](char c) { return c == ' ' || c == '\t'; };
std::string line;
while (std::getline(infile, line)) {
if (line.empty() || line[0] == '#') {
continue;
}
auto type_end = std::find_if(std::begin(line), std::end(line), delim_pred);
if (type_end == std::begin(line)) {
continue;
}
auto ext_end = type_end;
for (;;) {
auto ext_start = std::find_if_not(ext_end, std::end(line), delim_pred);
if (ext_start == std::end(line)) {
break;
}
ext_end = std::find_if(ext_start, std::end(line), delim_pred);
res.emplace(std::string(ext_start, ext_end),
std::string(std::begin(line), type_end));
}
}
return res;
}
} // namespace util } // namespace util
} // namespace nghttp2 } // namespace nghttp2

View File

@ -44,6 +44,7 @@
#include <sstream> #include <sstream>
#include <memory> #include <memory>
#include <chrono> #include <chrono>
#include <map>
#include "http-parser/http_parser.h" #include "http-parser/http_parser.h"
@ -706,6 +707,8 @@ uint32_t get_uint32(const uint8_t *data);
// order and returns it in host byte order. // order and returns it in host byte order.
uint64_t get_uint64(const uint8_t *data); uint64_t get_uint64(const uint8_t *data);
std::map<std::string, std::string> read_mime_types(const char *filename);
} // namespace util } // namespace util
} // namespace nghttp2 } // namespace nghttp2