nghttpd: Add --echo-upload option to send back request body
This commit is contained in:
parent
260131966d
commit
5da49989f8
|
@ -91,7 +91,8 @@ Config::Config()
|
||||||
session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1),
|
session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1),
|
||||||
max_concurrent_streams(100), header_table_size(-1), port(0),
|
max_concurrent_streams(100), header_table_size(-1), port(0),
|
||||||
verbose(false), daemon(false), verify_client(false), no_tls(false),
|
verbose(false), daemon(false), verify_client(false), no_tls(false),
|
||||||
error_gzip(false), early_response(false), hexdump(false) {
|
error_gzip(false), early_response(false), hexdump(false),
|
||||||
|
echo_upload(false) {
|
||||||
nghttp2_option_new(&session_option);
|
nghttp2_option_new(&session_option);
|
||||||
nghttp2_option_set_recv_client_preface(session_option, 1);
|
nghttp2_option_set_recv_client_preface(session_option, 1);
|
||||||
}
|
}
|
||||||
|
@ -282,7 +283,7 @@ private:
|
||||||
|
|
||||||
Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
Stream::Stream(Http2Handler *handler, int32_t stream_id)
|
||||||
: handler(handler), file_ent(nullptr), body_length(0), body_offset(0),
|
: handler(handler), file_ent(nullptr), body_length(0), body_offset(0),
|
||||||
stream_id(stream_id) {
|
stream_id(stream_id), echo_upload(false) {
|
||||||
auto config = handler->get_config();
|
auto config = handler->get_config();
|
||||||
ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
|
ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
|
||||||
ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
|
ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
|
||||||
|
@ -918,6 +919,49 @@ void prepare_status_response(Stream *stream, Http2Handler *hd, int status) {
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void prepare_echo_response(Stream *stream, Http2Handler *hd) {
|
||||||
|
auto length = lseek(stream->file_ent->fd, 0, SEEK_END);
|
||||||
|
if (length == -1) {
|
||||||
|
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stream->body_length = length;
|
||||||
|
if (lseek(stream->file_ent->fd, 0, SEEK_SET) == -1) {
|
||||||
|
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nghttp2_data_provider data_prd;
|
||||||
|
data_prd.source.fd = stream->file_ent->fd;
|
||||||
|
data_prd.read_callback = file_read_callback;
|
||||||
|
|
||||||
|
Headers headers;
|
||||||
|
headers.emplace_back("nghttpd-response", "echo");
|
||||||
|
headers.emplace_back("content-length", util::utos(length));
|
||||||
|
|
||||||
|
hd->submit_response("200", stream->stream_id, headers, &data_prd);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
|
||||||
|
auto sessions = hd->get_sessions();
|
||||||
|
|
||||||
|
char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
|
||||||
|
auto fd = mkstemp(tempfn);
|
||||||
|
if (fd == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
unlink(tempfn);
|
||||||
|
// 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));
|
||||||
|
stream->echo_upload = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void prepare_redirect_response(Stream *stream, Http2Handler *hd,
|
void prepare_redirect_response(Stream *stream, Http2Handler *hd,
|
||||||
const std::string &path, int status) {
|
const std::string &path, int status) {
|
||||||
|
@ -972,8 +1016,14 @@ void prepare_response(Stream *stream, Http2Handler *hd,
|
||||||
url = reqpath;
|
url = reqpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
url = util::percentDecode(url.begin(), url.end());
|
auto sessions = hd->get_sessions();
|
||||||
|
|
||||||
|
url = util::percentDecode(std::begin(url), std::end(url));
|
||||||
if (!util::check_path(url)) {
|
if (!util::check_path(url)) {
|
||||||
|
if (stream->file_ent) {
|
||||||
|
sessions->release_fd(stream->file_ent->path);
|
||||||
|
stream->file_ent = nullptr;
|
||||||
|
}
|
||||||
prepare_status_response(stream, hd, 404);
|
prepare_status_response(stream, hd, 404);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -987,12 +1037,18 @@ void prepare_response(Stream *stream, Http2Handler *hd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string path = hd->get_config()->htdocs + url;
|
std::string path = hd->get_config()->htdocs + url;
|
||||||
if (path[path.size() - 1] == '/') {
|
if (path[path.size() - 1] == '/') {
|
||||||
path += DEFAULT_HTML;
|
path += DEFAULT_HTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sessions = hd->get_sessions();
|
if (stream->echo_upload) {
|
||||||
|
assert(stream->file_ent);
|
||||||
|
prepare_echo_response(stream, hd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto file_ent = sessions->get_cached_fd(path);
|
auto file_ent = sessions->get_cached_fd(path);
|
||||||
|
|
||||||
if (file_ent == nullptr) {
|
if (file_ent == nullptr) {
|
||||||
|
@ -1122,7 +1178,7 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
|
||||||
|
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
remove_stream_read_timeout(stream);
|
remove_stream_read_timeout(stream);
|
||||||
if (!hd->get_config()->early_response) {
|
if (stream->echo_upload || !hd->get_config()->early_response) {
|
||||||
prepare_response(stream, hd);
|
prepare_response(stream, hd);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1146,14 +1202,22 @@ int hd_on_frame_recv_callback(nghttp2_session *session,
|
||||||
hd->submit_non_final_response("100", frame->hd.stream_id);
|
hd->submit_non_final_response("100", frame->hd.stream_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hd->get_config()->early_response) {
|
auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD,
|
||||||
|
stream->headers)->value;
|
||||||
|
if (hd->get_config()->echo_upload &&
|
||||||
|
(method == "POST" || method == "PUT")) {
|
||||||
|
if (!prepare_upload_temp_store(stream, hd)) {
|
||||||
|
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else if (hd->get_config()->early_response) {
|
||||||
prepare_response(stream, hd);
|
prepare_response(stream, hd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
remove_stream_read_timeout(stream);
|
remove_stream_read_timeout(stream);
|
||||||
if (!hd->get_config()->early_response) {
|
if (stream->echo_upload || !hd->get_config()->early_response) {
|
||||||
prepare_response(stream, hd);
|
prepare_response(stream, hd);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1299,6 +1363,21 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stream->echo_upload) {
|
||||||
|
assert(stream->file_ent);
|
||||||
|
while (len) {
|
||||||
|
ssize_t n;
|
||||||
|
while ((n = write(stream->file_ent->fd, data, len)) == -1 &&
|
||||||
|
errno == EINTR)
|
||||||
|
;
|
||||||
|
if (n == -1) {
|
||||||
|
hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
len -= n;
|
||||||
|
data += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
// TODO Handle POST
|
// TODO Handle POST
|
||||||
|
|
||||||
add_stream_read_timeout(stream);
|
add_stream_read_timeout(stream);
|
||||||
|
|
|
@ -73,6 +73,7 @@ struct Config {
|
||||||
bool error_gzip;
|
bool error_gzip;
|
||||||
bool early_response;
|
bool early_response;
|
||||||
bool hexdump;
|
bool hexdump;
|
||||||
|
bool echo_upload;
|
||||||
Config();
|
Config();
|
||||||
~Config();
|
~Config();
|
||||||
};
|
};
|
||||||
|
@ -101,6 +102,7 @@ struct Stream {
|
||||||
int64_t body_offset;
|
int64_t body_offset;
|
||||||
int32_t stream_id;
|
int32_t stream_id;
|
||||||
http2::HeaderIndex hdidx;
|
http2::HeaderIndex hdidx;
|
||||||
|
bool echo_upload;
|
||||||
Stream(Http2Handler *handler, int32_t stream_id);
|
Stream(Http2Handler *handler, int32_t stream_id);
|
||||||
~Stream();
|
~Stream();
|
||||||
};
|
};
|
||||||
|
|
|
@ -153,6 +153,8 @@ Options:
|
||||||
--hexdump Display the incoming traffic in hexadecimal (Canonical
|
--hexdump Display the incoming traffic in hexadecimal (Canonical
|
||||||
hex+ASCII display). If SSL/TLS is used, decrypted data
|
hex+ASCII display). If SSL/TLS is used, decrypted data
|
||||||
are used.
|
are used.
|
||||||
|
--echo-upload
|
||||||
|
Send back uploaded content if method is POST or PUT.
|
||||||
--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.
|
||||||
|
|
||||||
|
@ -188,6 +190,7 @@ int main(int argc, char **argv) {
|
||||||
{"early-response", no_argument, &flag, 5},
|
{"early-response", no_argument, &flag, 5},
|
||||||
{"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},
|
||||||
{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,
|
||||||
|
@ -310,6 +313,10 @@ int main(int argc, char **argv) {
|
||||||
// hexdump option
|
// hexdump option
|
||||||
config.hexdump = true;
|
config.hexdump = true;
|
||||||
break;
|
break;
|
||||||
|
case 8:
|
||||||
|
// echo-upload option
|
||||||
|
config.echo_upload = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in New Issue