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