commit 351eec7eb44998093ffd90f2e0aa0ecdbacf2330
parent f5a58368b7ea9bf99e95ae24fcb235babc80dafc
Author: Joris Vink <joris@coders.se>
Date: Fri, 17 Sep 2021 19:28:06 +0200
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.
Diffstat:
4 files changed, 71 insertions(+), 48 deletions(-)
diff --git a/include/kore/kore.h b/include/kore/kore.h
@@ -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 *);
diff --git a/src/http.c b/src/http.c
@@ -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));
-
- 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);
- }
+ c->http_timeout = http_body_timeout * 1000;
+
+ 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)
{
+ struct http_request *req = (struct http_request *)nb->extra;
+
+ 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;
- struct http_request *req = (struct http_request *)nb->extra;
- SHA256_Update(&req->hashctx, nb->buf, nb->s_off);
+ 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) {
diff --git a/src/net.c b/src/net.c
@@ -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))
diff --git a/src/runtime.c b/src/runtime.c
@@ -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)
{