Add the on_body_chunk handler for routes.

If set, will call a given handler with the prototype of

`void body_chunk(struct http_request *req, const void *data, size_t len);`

for each chunk of the received HTTP body, allowing a developer to handle
it in their own way.

The incoming body is still being handled and retained in the same way
as before (in a kore_buf or temporary file).

While here, allow HTTP_STATUS_CONTINUE to work via http_response() and
make the handling of incoming HTTP header data a bit better.
This commit is contained in:
Joris Vink 2021-09-17 19:28:06 +02:00
parent f5a58368b7
commit 351eec7eb4
4 changed files with 71 additions and 48 deletions

View File

@ -287,6 +287,8 @@ struct kore_runtime {
int type;
#if !defined(KORE_NO_HTTP)
int (*http_request)(void *, struct http_request *);
void (*http_body_chunk)(void *,
struct http_request *, const void *, size_t);
int (*validator)(void *, struct http_request *, const void *);
void (*wsconnect)(void *, struct connection *);
void (*wsdisconnect)(void *, struct connection *);
@ -986,6 +988,8 @@ void kore_runtime_connect(struct kore_runtime_call *, struct connection *);
#if !defined(KORE_NO_HTTP)
int kore_runtime_http_request(struct kore_runtime_call *,
struct http_request *);
void kore_runtime_http_body_chunk(struct kore_runtime_call *,
struct http_request *, const void *, size_t);
int kore_runtime_validator(struct kore_runtime_call *,
struct http_request *, const void *);
void kore_runtime_wsconnect(struct kore_runtime_call *, struct connection *);

View File

@ -132,6 +132,7 @@ static int http_release_buffer(struct netbuf *);
static void http_error_response(struct connection *, int);
static int http_data_convert(void *, void **, void *, int);
static void http_write_response_cookie(struct http_cookie *);
static int http_body_update(struct http_request *, const void *, size_t);
static void http_argument_add(struct http_request *, char *, char *,
int, int);
static int http_check_redirect(struct http_request *,
@ -806,10 +807,8 @@ http_header_recv(struct netbuf *nb)
{
struct connection *c;
size_t len;
ssize_t ret;
struct http_header *hdr;
struct http_request *req;
u_int64_t bytes_left;
u_int8_t *end_headers;
int h, i, v, skip, l;
char *headers[HTTP_REQ_HEADER_MAX];
@ -821,6 +820,11 @@ http_header_recv(struct netbuf *nb)
if (nb->b_len < 4)
return (KORE_RESULT_OK);
if (!isalpha(nb->buf[0])) {
http_error_response(c, HTTP_STATUS_BAD_REQUEST);
return (KORE_RESULT_ERROR);
}
skip = 4;
end_headers = kore_mem_find(nb->buf, nb->s_off, "\r\n\r\n", 4);
if (end_headers == NULL) {
@ -962,47 +966,19 @@ http_header_recv(struct netbuf *nb)
HTTP_STATUS_INTERNAL_ERROR);
return (KORE_RESULT_OK);
}
ret = write(req->http_body_fd,
end_headers, (nb->s_off - len));
if (ret == -1 || (size_t)ret != (nb->s_off - len)) {
req->flags |= HTTP_REQUEST_DELETE;
http_error_response(req->owner,
HTTP_STATUS_INTERNAL_ERROR);
return (KORE_RESULT_OK);
}
} else {
req->http_body_fd = -1;
req->http_body = kore_buf_alloc(req->content_length);
kore_buf_append(req->http_body, end_headers,
(nb->s_off - len));
}
SHA256_Init(&req->hashctx);
SHA256_Update(&req->hashctx, end_headers, (nb->s_off - len));
c->http_timeout = http_body_timeout * 1000;
bytes_left = req->content_length - (nb->s_off - len);
if (bytes_left > 0) {
kore_debug("%ld/%ld (%ld - %ld) more bytes for body",
bytes_left, req->content_length, nb->s_off, len);
net_recv_reset(c,
MIN(bytes_left, NETBUF_SEND_PAYLOAD_MAX),
http_body_recv);
c->rnb->extra = req;
http_request_sleep(req);
req->content_length = bytes_left;
c->http_timeout = http_body_timeout * 1000;
} else {
c->http_timeout = 0;
req->flags |= HTTP_REQUEST_COMPLETE;
req->flags &= ~HTTP_REQUEST_EXPECT_BODY;
SHA256_Final(req->http_body_digest, &req->hashctx);
if (!http_body_rewind(req)) {
req->flags |= HTTP_REQUEST_DELETE;
http_error_response(req->owner,
HTTP_STATUS_INTERNAL_ERROR);
return (KORE_RESULT_OK);
}
if (!http_body_update(req, end_headers, nb->s_off - len)) {
req->flags |= HTTP_REQUEST_DELETE;
http_error_response(req->owner,
HTTP_STATUS_INTERNAL_ERROR);
return (KORE_RESULT_OK);
}
} else {
c->http_timeout = 0;
@ -1505,7 +1481,6 @@ http_start_recv(struct connection *c)
c->http_start = kore_time_ms();
c->http_timeout = http_header_timeout * 1000;
net_recv_reset(c, http_header_max, http_header_recv);
(void)net_recv_flush(c);
}
void
@ -2360,22 +2335,29 @@ http_argument_add(struct http_request *req, char *name, char *value, int qs,
static int
http_body_recv(struct netbuf *nb)
{
ssize_t ret;
u_int64_t bytes_left;
struct http_request *req = (struct http_request *)nb->extra;
SHA256_Update(&req->hashctx, nb->buf, nb->s_off);
return (http_body_update(req, nb->buf, nb->s_off));
}
static int
http_body_update(struct http_request *req, const void *data, size_t len)
{
ssize_t ret;
u_int64_t bytes_left;
SHA256_Update(&req->hashctx, data, len);
if (req->http_body_fd != -1) {
ret = write(req->http_body_fd, nb->buf, nb->s_off);
if (ret == -1 || (size_t)ret != nb->s_off) {
ret = write(req->http_body_fd, data, len);
if (ret == -1 || (size_t)ret != len) {
req->flags |= HTTP_REQUEST_DELETE;
http_error_response(req->owner,
HTTP_STATUS_INTERNAL_ERROR);
return (KORE_RESULT_ERROR);
}
} else if (req->http_body != NULL) {
kore_buf_append(req->http_body, nb->buf, nb->s_off);
kore_buf_append(req->http_body, data, len);
} else {
req->flags |= HTTP_REQUEST_DELETE;
http_error_response(req->owner,
@ -2383,10 +2365,10 @@ http_body_recv(struct netbuf *nb)
return (KORE_RESULT_ERROR);
}
req->content_length -= nb->s_off;
req->content_length -= len;
if (req->content_length == 0) {
nb->extra = NULL;
req->owner->rnb->extra = NULL;
http_request_wakeup(req);
req->flags |= HTTP_REQUEST_COMPLETE;
req->flags &= ~HTTP_REQUEST_EXPECT_BODY;
@ -2398,12 +2380,17 @@ http_body_recv(struct netbuf *nb)
return (KORE_RESULT_ERROR);
}
SHA256_Final(req->http_body_digest, &req->hashctx);
net_recv_reset(nb->owner, http_header_max, http_header_recv);
} else {
bytes_left = req->content_length;
net_recv_reset(nb->owner,
net_recv_reset(req->owner,
MIN(bytes_left, NETBUF_SEND_PAYLOAD_MAX),
http_body_recv);
req->owner->rnb->extra = req;
}
if (req->rt->on_body_chunk != NULL && len > 0) {
kore_runtime_http_body_chunk(req->rt->on_body_chunk,
req, data, len);
}
return (KORE_RESULT_OK);
@ -2442,7 +2429,6 @@ http_response_normal(struct http_request *req, struct connection *c,
send_body = 1;
text = http_status_text(status);
kore_buf_init(&buf, 1024);
kore_buf_reset(header_buf);
if (req != NULL) {
@ -2456,6 +2442,13 @@ http_response_normal(struct http_request *req, struct connection *c,
kore_buf_appendf(header_buf, "HTTP/1.%c %d %s\r\n",
version, status, text);
if (status == 100) {
kore_buf_append(header_buf, "\r\n", 2);
net_send_queue(c, header_buf->data, header_buf->offset);
return;
}
kore_buf_append(header_buf, http_version, http_version_len);
if ((c->flags & CONN_CLOSE_EMPTY) ||
@ -2474,6 +2467,8 @@ http_response_normal(struct http_request *req, struct connection *c,
}
}
kore_buf_init(&buf, 1024);
/* Note that req CAN be NULL. */
if (req == NULL || req->owner->proto != CONN_PROTO_WEBSOCKET) {
if (http_keepalive_time && connection_close == 0) {

View File

@ -292,6 +292,9 @@ net_recv_flush(struct connection *c)
if (c->rnb->buf == NULL)
return (KORE_RESULT_OK);
if ((c->rnb->b_len - c->rnb->s_off) == 0)
return (KORE_RESULT_OK);
if (!c->read(c, &r))
return (KORE_RESULT_ERROR);
if (!(c->evt.flags & KORE_EVENT_READ))

View File

@ -33,6 +33,8 @@ static void native_runtime_connect(void *, struct connection *);
static void native_runtime_configure(void *, int, char **);
#if !defined(KORE_NO_HTTP)
static int native_runtime_http_request(void *, struct http_request *);
static void native_runtime_http_body_chunk(void *, struct http_request *,
const void *, size_t);
static int native_runtime_validator(void *, struct http_request *,
const void *);
@ -44,6 +46,7 @@ struct kore_runtime kore_native_runtime = {
KORE_RUNTIME_NATIVE,
#if !defined(KORE_NO_HTTP)
.http_request = native_runtime_http_request,
.http_body_chunk= native_runtime_http_body_chunk,
.validator = native_runtime_validator,
.wsconnect = native_runtime_connect,
.wsmessage = native_runtime_wsmessage,
@ -105,6 +108,13 @@ kore_runtime_http_request(struct kore_runtime_call *rcall,
return (rcall->runtime->http_request(rcall->addr, req));
}
void
kore_runtime_http_body_chunk(struct kore_runtime_call *rcall,
struct http_request *req, const void *data, size_t len)
{
rcall->runtime->http_body_chunk(rcall->addr, req, data, len);
}
int
kore_runtime_validator(struct kore_runtime_call *rcall,
struct http_request *req, const void *data)
@ -178,6 +188,17 @@ native_runtime_http_request(void *addr, struct http_request *req)
return (cb(req));
}
static void
native_runtime_http_body_chunk(void *addr, struct http_request *req,
const void *data, size_t len)
{
void (*cb)(struct http_request *, const void *, size_t);
*(void **)&(cb) = addr;
cb(req, data, len);
}
static int
native_runtime_validator(void *addr, struct http_request *req, const void *data)
{