nghttpx: Write API request body in temporary file

This commit is contained in:
Tatsuhiro Tsujikawa 2017-12-02 13:35:00 +09:00
parent a941699962
commit 03f7ec0f60
5 changed files with 62 additions and 24 deletions

View File

@ -302,6 +302,7 @@ check_type_size("time_t" SIZEOF_TIME_T)
include(CheckFunctionExists) include(CheckFunctionExists)
check_function_exists(_Exit HAVE__EXIT) check_function_exists(_Exit HAVE__EXIT)
check_function_exists(accept4 HAVE_ACCEPT4) check_function_exists(accept4 HAVE_ACCEPT4)
check_function_exists(mkostemp HAVE_MKOSTEMP)
include(CheckSymbolExists) include(CheckSymbolExists)
# XXX does this correctly detect initgroups (un)availability on cygwin? # XXX does this correctly detect initgroups (un)availability on cygwin?

View File

@ -34,6 +34,9 @@
/* Define to 1 if you have the `accept4` function. */ /* Define to 1 if you have the `accept4` function. */
#cmakedefine HAVE_ACCEPT4 1 #cmakedefine HAVE_ACCEPT4 1
/* Define to 1 if you have the `mkostemp` function. */
#cmakedefine HAVE_MKOSTEMP 1
/* Define to 1 if you have the `initgroups` function. */ /* Define to 1 if you have the `initgroups` function. */
#cmakedefine01 HAVE_DECL_INITGROUPS #cmakedefine01 HAVE_DECL_INITGROUPS

View File

@ -713,6 +713,7 @@ AC_CHECK_FUNCS([ \
memchr \ memchr \
memmove \ memmove \
memset \ memset \
mkostemp \
socket \ socket \
sqrt \ sqrt \
strchr \ strchr \

View File

@ -23,6 +23,12 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#include "shrpx_api_downstream_connection.h" #include "shrpx_api_downstream_connection.h"
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstdlib>
#include "shrpx_client_handler.h" #include "shrpx_client_handler.h"
#include "shrpx_upstream.h" #include "shrpx_upstream.h"
#include "shrpx_downstream.h" #include "shrpx_downstream.h"
@ -64,9 +70,13 @@ constexpr StringRef API_METHOD_STRING[] = {
} // namespace } // namespace
APIDownstreamConnection::APIDownstreamConnection(Worker *worker) APIDownstreamConnection::APIDownstreamConnection(Worker *worker)
: worker_(worker), api_(nullptr), shutdown_read_(false) {} : worker_(worker), api_(nullptr), fd_(-1), shutdown_read_(false) {}
APIDownstreamConnection::~APIDownstreamConnection() {} APIDownstreamConnection::~APIDownstreamConnection() {
if (fd_ != -1) {
close(fd_);
}
}
int APIDownstreamConnection::attach_downstream(Downstream *downstream) { int APIDownstreamConnection::attach_downstream(Downstream *downstream) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
@ -233,6 +243,28 @@ int APIDownstreamConnection::push_request_headers() {
return 0; return 0;
} }
switch (req.method) {
case HTTP_POST:
case HTTP_PUT: {
char tempname[] = "/tmp/nghttpx-api.XXXXXX";
#ifdef HAVE_MKOSTEMP
fd_ = mkostemp(tempname, O_CLOEXEC);
#else // !HAVE_MKOSTEMP
fd_ = mkstemp(tempname);
#endif // !HAVE_MKOSTEMP
if (fd_ == -1) {
send_reply(500, API_FAILURE);
return 0;
}
#ifndef HAVE_MKOSTEMP
util::make_socket_closeonexec(fd_);
#endif // HAVE_MKOSTEMP
unlink(tempname);
break;
}
}
return 0; return 0;
} }
@ -275,17 +307,25 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
return 0; return 0;
} }
auto output = downstream_->get_request_buf(); auto &req = downstream_->request();
auto &apiconf = get_config()->api; auto &apiconf = get_config()->api;
if (output->rleft() + datalen > apiconf.max_request_body) { if (static_cast<size_t>(req.recv_body_length) > apiconf.max_request_body) {
send_reply(413, API_FAILURE); send_reply(413, API_FAILURE);
return 0; return 0;
} }
output->append(data, datalen); ssize_t nwrite;
while ((nwrite = write(fd_, data, datalen)) == -1 && errno == EINTR)
;
if (nwrite == -1) {
auto error = errno;
LOG(ERROR) << "Could not write API request body: errno=" << error;
send_reply(500, API_FAILURE);
return 0;
}
// We don't have to call Upstream::resume_read() here, because // We don't have to call Upstream::resume_read() here, because
// request buffer is effectively unlimited. Actually, we cannot // request buffer is effectively unlimited. Actually, we cannot
@ -303,30 +343,21 @@ int APIDownstreamConnection::end_upload_data() {
} }
int APIDownstreamConnection::handle_backendconfig() { int APIDownstreamConnection::handle_backendconfig() {
auto output = downstream_->get_request_buf(); auto &req = downstream_->request();
std::array<struct iovec, 2> iov; if (req.recv_body_length == 0) {
auto iovcnt = output->riovec(iov.data(), 2);
if (iovcnt == 0) {
send_reply(200, API_SUCCESS); send_reply(200, API_SUCCESS);
return 0; return 0;
} }
std::unique_ptr<uint8_t[]> large_buf; auto rp = mmap(nullptr, req.recv_body_length, PROT_READ, MAP_SHARED, fd_, 0);
if (rp == reinterpret_cast<void *>(-1)) {
// If data spans in multiple chunks, pull them up into one send_reply(500, API_FAILURE);
// contiguous buffer.
if (iovcnt > 1) {
large_buf = make_unique<uint8_t[]>(output->rleft());
auto len = output->rleft();
output->remove(large_buf.get(), len);
iov[0].iov_base = large_buf.get();
iov[0].iov_len = len;
} }
auto unmapper = defer(munmap, rp, req.recv_body_length);
Config new_config{}; Config new_config{};
new_config.conn.downstream = std::make_shared<DownstreamConfig>(); new_config.conn.downstream = std::make_shared<DownstreamConfig>();
const auto &downstreamconf = new_config.conn.downstream; const auto &downstreamconf = new_config.conn.downstream;
@ -344,8 +375,8 @@ int APIDownstreamConnection::handle_backendconfig() {
std::set<StringRef> include_set; std::set<StringRef> include_set;
std::map<StringRef, size_t> pattern_addr_indexer; std::map<StringRef, size_t> pattern_addr_indexer;
for (auto first = reinterpret_cast<const uint8_t *>(iov[0].iov_base), for (auto first = reinterpret_cast<const uint8_t *>(rp),
last = first + iov[0].iov_len; last = first + req.recv_body_length;
first != last;) { first != last;) {
auto eol = std::find(first, last, '\n'); auto eol = std::find(first, last, '\n');
if (eol == last) { if (eol == last) {

View File

@ -96,6 +96,8 @@ private:
Worker *worker_; Worker *worker_;
// This points to the requested APIEndpoint struct. // This points to the requested APIEndpoint struct.
const APIEndpoint *api_; const APIEndpoint *api_;
// The file descriptor for temporary file to store request body.
int fd_;
// true if we stop reading request body. // true if we stop reading request body.
bool shutdown_read_; bool shutdown_read_;
}; };