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
Config::Config()
: stream_read_timeout(1_min), stream_write_timeout(1_min),
data_ptr(nullptr), padding(0), num_worker(1), max_concurrent_streams(100),
header_table_size(-1), port(0), verbose(false), daemon(false),
verify_client(false), no_tls(false), error_gzip(false),
early_response(false), hexdump(false), echo_upload(false) {}
: mime_types_file("/etc/mime.types"), stream_read_timeout(1_min),
stream_write_timeout(1_min), data_ptr(nullptr), padding(0), num_worker(1),
max_concurrent_streams(100), header_table_size(-1), port(0),
verbose(false), daemon(false), verify_client(false), no_tls(false),
error_gzip(false), early_response(false), hexdump(false),
echo_upload(false) {}
Config::~Config() {}
@ -739,6 +740,7 @@ int Http2Handler::verify_npn_result() {
int Http2Handler::submit_file_response(const std::string &status,
Stream *stream, time_t last_modified,
off_t file_length,
const std::string *content_type,
nghttp2_data_provider *data_prd) {
std::string content_length = util::utos(file_length);
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_ll("cache-control", "max-age=3600"),
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;
if (last_modified != 0) {
last_modified_str = util::http_date(last_modified);
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;
std::string trailer_names;
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
// now. We will update it when we get whole request body.
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;
return true;
}
@ -1100,8 +1106,28 @@ void prepare_response(Stream *stream, Http2Handler *hd,
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(
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;
@ -1116,7 +1142,7 @@ void prepare_response(Stream *stream, Http2Handler *hd,
stream->headers)->value;
if (method == "HEAD") {
hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length,
nullptr);
file_ent->content_type, nullptr);
return;
}
@ -1128,7 +1154,7 @@ void prepare_response(Stream *stream, Http2Handler *hd,
data_prd.read_callback = file_read_callback;
hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length,
&data_prd);
file_ent->content_type, &data_prd);
}
} // namespace
@ -1625,7 +1651,7 @@ FileEntry make_status_body(int status, uint16_t port) {
assert(0);
}
return FileEntry(util::utos(status), nwrite, 0, fd);
return FileEntry(util::utos(status), nwrite, 0, fd, nullptr);
}
} // namespace

View File

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

View File

@ -160,6 +160,10 @@ Options:
are used.
--echo-upload
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.
-h, --help Display this help and exit.
@ -202,6 +206,7 @@ int main(int argc, char **argv) {
{"trailer", required_argument, &flag, 6},
{"hexdump", no_argument, &flag, 7},
{"echo-upload", no_argument, &flag, 8},
{"mime-types-file", required_argument, &flag, 9},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
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
config.echo_upload = true;
break;
case 9:
// mime-types-file option
config.mime_types_file = optarg;
break;
}
break;
default:
@ -366,6 +375,8 @@ int main(int argc, char **argv) {
config.htdocs = "./";
}
config.mime_types = util::read_mime_types(config.mime_types_file.c_str());
set_color_output(color || isatty(fileno(stdout)));
struct sigaction act {};

View File

@ -52,6 +52,7 @@
#include <cstdio>
#include <cstring>
#include <iostream>
#include <fstream>
#include <nghttp2/nghttp2.h>
@ -1217,6 +1218,43 @@ uint64_t get_uint64(const uint8_t *data) {
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 nghttp2

View File

@ -44,6 +44,7 @@
#include <sstream>
#include <memory>
#include <chrono>
#include <map>
#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.
uint64_t get_uint64(const uint8_t *data);
std::map<std::string, std::string> read_mime_types(const char *filename);
} // namespace util
} // namespace nghttp2