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:
parent
ecb4a208fb
commit
f29ccc9c20
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {};
|
||||
|
|
38
src/util.cc
38
src/util.cc
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue