Add PUT/DELETE/HEAD methods (finally).

This commit renames certain POST centric variable and configuration
naming to the correct HTTP body stuff.

API changes include http_postbody_text() and http_postbody_bytes() to
have become http_body_text() and http_body_bytes().

The developer is still responsible for validating the method their
page handler is called with. Hopefully this becomes a configuration
option soon enough.
This commit is contained in:
Joris Vink 2014-10-08 11:03:14 +02:00
parent 3741e8ca3f
commit a603b77e24
8 changed files with 96 additions and 72 deletions

View File

@ -26,7 +26,7 @@ workers 4
# HTTP specific settings.
# http_header_max Maximum size of HTTP headers (in bytes).
#
# http_postbody_max Maximum size of an HTTP POST body (in bytes).
# http_body_max Maximum size of an HTTP body (in bytes).
#
# http_keepalive_time Maximum seconds an HTTP connection can be
# kept alive by the browser.
@ -36,7 +36,7 @@ workers 4
# all responses. Parameter is the age.
# (Set to 0 to disable sending this header).
#http_header_max 4096
#http_postbody_max 10240000
#http_body_max 10240000
#http_keepalive_time 0
#http_hsts_enable 31536000

View File

@ -14,9 +14,11 @@ page(struct http_request *req)
char eb[1024];
const char *path[] = { "foo", "bar", NULL };
/* We only allow POST methods. */
if (req->method != HTTP_METHOD_POST) {
http_response(req, 400, NULL, 0);
/* We only allow POST/PUT methods. */
if (req->method != HTTP_METHOD_POST &&
req->method != HTTP_METHOD_PUT) {
http_response_header(req, "allow", "POST, PUT");
http_response(req, HTTP_STATUS_METHOD_NOT_ALLOWED, NULL, 0);
return (KORE_RESULT_OK);
}
@ -24,7 +26,7 @@ page(struct http_request *req)
* Grab the entire body we received as text (NUL-terminated).
* Note: this can return NULL and the result MUST be freed.
*/
if ((body = http_post_data_text(req)) == NULL) {
if ((body = http_body_text(req)) == NULL) {
http_response(req, 400, NULL, 0);
return (KORE_RESULT_OK);
}

View File

@ -20,7 +20,7 @@
#define HTTP_KEEPALIVE_TIME 20
#define HTTP_HSTS_ENABLE 31536000
#define HTTP_HEADER_MAX_LEN 4096
#define HTTP_POSTBODY_MAX_LEN 10240000
#define HTTP_BODY_MAX_LEN 10240000
#define HTTP_URI_LEN 2000
#define HTTP_USERAGENT_LEN 256
#define HTTP_REQ_HEADER_MAX 25
@ -152,11 +152,15 @@ struct http_file {
#define HTTP_METHOD_GET 0
#define HTTP_METHOD_POST 1
#define HTTP_METHOD_PUT 2
#define HTTP_METHOD_DELETE 3
#define HTTP_METHOD_HEAD 4
#define HTTP_REQUEST_COMPLETE 0x01
#define HTTP_REQUEST_DELETE 0x02
#define HTTP_REQUEST_SLEEPING 0x04
#define HTTP_REQUEST_PGSQL_QUEUE 0x10
#define HTTP_REQUEST_EXPECT_BODY 0x20
struct kore_task;
@ -173,7 +177,7 @@ struct http_request {
char *agent;
struct connection *owner;
struct spdy_stream *stream;
struct kore_buf *post_data;
struct kore_buf *http_body;
void *hdlr_extra;
char *query_string;
u_int8_t *multipart_body;
@ -197,7 +201,7 @@ struct http_state {
extern int http_request_count;
extern u_int16_t http_header_max;
extern u_int64_t http_postbody_max;
extern u_int64_t http_body_max;
extern u_int64_t http_hsts_enable;
extern u_int16_t http_keepalive_time;
@ -208,9 +212,9 @@ time_t http_date_to_time(char *);
void http_request_free(struct http_request *);
void http_request_sleep(struct http_request *);
void http_request_wakeup(struct http_request *);
char *http_post_data_text(struct http_request *);
char *http_body_text(struct http_request *);
void http_process_request(struct http_request *, int);
u_int8_t *http_post_data_bytes(struct http_request *, u_int32_t *);
u_int8_t *http_body_bytes(struct http_request *, u_int32_t *);
void http_response(struct http_request *, int, void *, u_int32_t);
void http_response_stream(struct http_request *, int, void *,
u_int64_t, int (*cb)(struct netbuf *), void *);

View File

@ -101,6 +101,15 @@ kore_accesslog_wait(void)
case HTTP_METHOD_POST:
method = "POST";
break;
case HTTP_METHOD_PUT:
method = "PUT";
break;
case HTTP_METHOD_DELETE:
method = "DELETE";
break;
case HTTP_METHOD_HEAD:
method = "HEAD";
break;
default:
method = "UNKNOWN";
break;

View File

@ -49,7 +49,7 @@ static int configure_ssl_dhparam(char **);
static int configure_ssl_no_compression(char **);
static int configure_spdy_idle_time(char **);
static int configure_http_header_max(char **);
static int configure_http_postbody_max(char **);
static int configure_http_body_max(char **);
static int configure_http_hsts_enable(char **);
static int configure_http_keepalive_time(char **);
static int configure_validator(char **);
@ -94,7 +94,7 @@ static struct {
{ "certkey", configure_certkey },
{ "require_client_cert", configure_require_client_cert },
{ "http_header_max", configure_http_header_max },
{ "http_postbody_max", configure_http_postbody_max },
{ "http_body_max", configure_http_body_max },
{ "http_hsts_enable", configure_http_hsts_enable },
{ "http_keepalive_time", configure_http_keepalive_time },
{ "validator", configure_validator },
@ -584,21 +584,21 @@ configure_http_header_max(char **argv)
}
static int
configure_http_postbody_max(char **argv)
configure_http_body_max(char **argv)
{
int err;
if (argv[1] == NULL)
return (KORE_RESULT_ERROR);
if (http_postbody_max != HTTP_POSTBODY_MAX_LEN) {
kore_debug("http_postbody_max already set");
if (http_body_max != HTTP_BODY_MAX_LEN) {
kore_debug("http_body_max already set");
return (KORE_RESULT_ERROR);
}
http_postbody_max = kore_strtonum(argv[1], 10, 1, LONG_MAX, &err);
http_body_max = kore_strtonum(argv[1], 10, 1, LONG_MAX, &err);
if (err != KORE_RESULT_OK) {
printf("bad http_postbody_max value: %s\n", argv[1]);
printf("bad http_body_max value: %s\n", argv[1]);
return (KORE_RESULT_ERROR);
}

View File

@ -31,7 +31,7 @@
#include "tasks.h"
#endif
static int http_post_data_recv(struct netbuf *);
static int http_body_recv(struct netbuf *);
static void http_error_response(struct connection *,
struct spdy_stream *, int);
static void http_argument_add(struct http_request *, const char *,
@ -54,7 +54,7 @@ int http_request_count;
u_int64_t http_hsts_enable = HTTP_HSTS_ENABLE;
u_int16_t http_header_max = HTTP_HEADER_MAX_LEN;
u_int16_t http_keepalive_time = HTTP_KEEPALIVE_TIME;
u_int64_t http_postbody_max = HTTP_POSTBODY_MAX_LEN;
u_int64_t http_body_max = HTTP_BODY_MAX_LEN;
void
http_init(void)
@ -104,11 +104,20 @@ http_request_new(struct connection *c, struct spdy_stream *s, const char *host,
if (!strcasecmp(method, "get")) {
m = HTTP_METHOD_GET;
flags = HTTP_REQUEST_COMPLETE;
} else if (!strcasecmp(method, "delete")) {
m = HTTP_METHOD_DELETE;
flags = HTTP_REQUEST_COMPLETE;
} else if (!strcasecmp(method, "post")) {
flags = 0;
m = HTTP_METHOD_POST;
flags = HTTP_REQUEST_EXPECT_BODY;
} else if (!strcasecmp(method, "put")) {
m = HTTP_METHOD_PUT;
flags = HTTP_REQUEST_EXPECT_BODY;
} else if (!strcasecmp(method, "head")) {
m = HTTP_METHOD_HEAD;
flags = HTTP_REQUEST_COMPLETE;
} else {
http_error_response(c, s, 405);
http_error_response(c, s, 400);
return (KORE_RESULT_ERROR);
}
@ -124,7 +133,7 @@ http_request_new(struct connection *c, struct spdy_stream *s, const char *host,
req->agent = NULL;
req->flags = flags;
req->fsm_state = 0;
req->post_data = NULL;
req->http_body = NULL;
req->hdlr_extra = NULL;
req->query_string = NULL;
req->multipart_body = NULL;
@ -389,9 +398,9 @@ http_request_free(struct http_request *req)
kore_mem_free(f);
}
if (req->method == HTTP_METHOD_POST && req->post_data != NULL)
kore_buf_free(req->post_data);
if (req->method == HTTP_METHOD_POST && req->multipart_body != NULL)
if (req->http_body != NULL)
kore_buf_free(req->http_body);
if (req->multipart_body != NULL)
kore_mem_free(req->multipart_body);
if (req->agent != NULL)
@ -438,8 +447,10 @@ http_response_stream(struct http_request *req, int status, void *base,
break;
}
net_send_stream(req->owner, base, len, req->stream, cb, &nb);
nb->extra = arg;
if (req->method != HTTP_METHOD_HEAD) {
net_send_stream(req->owner, base, len, req->stream, cb, &nb);
nb->extra = arg;
}
}
int
@ -568,9 +579,9 @@ http_header_recv(struct netbuf *nb)
req->agent = kore_strdup(hdr->value);
}
if (req->method == HTTP_METHOD_POST) {
if (req->flags & HTTP_REQUEST_EXPECT_BODY) {
if (!http_request_header(req, "content-length", &p)) {
kore_debug("POST but no content-length");
kore_debug("expected body but no content-length");
req->flags |= HTTP_REQUEST_DELETE;
http_error_response(req->owner, NULL, 411);
return (KORE_RESULT_OK);
@ -589,30 +600,31 @@ http_header_recv(struct netbuf *nb)
if (clen == 0) {
req->flags |= HTTP_REQUEST_COMPLETE;
req->flags &= ~HTTP_REQUEST_EXPECT_BODY;
return (KORE_RESULT_OK);
}
if (clen > http_postbody_max) {
kore_log(LOG_NOTICE, "POST data too large (%ld > %ld)",
clen, http_postbody_max);
if (clen > http_body_max) {
kore_log(LOG_NOTICE, "body too large (%ld > %ld)",
clen, http_body_max);
req->flags |= HTTP_REQUEST_DELETE;
http_error_response(req->owner, NULL, 411);
return (KORE_RESULT_OK);
}
req->post_data = kore_buf_create(clen);
kore_buf_append(req->post_data, end_headers,
req->http_body = kore_buf_create(clen);
kore_buf_append(req->http_body, end_headers,
(nb->s_off - len));
bytes_left = clen - (nb->s_off - len);
if (bytes_left > 0) {
kore_debug("%ld/%ld (%ld - %ld) more bytes for POST",
kore_debug("%ld/%ld (%ld - %ld) more bytes for body",
bytes_left, clen, nb->s_off, len);
net_recv_queue(c, bytes_left,
0, &nnb, http_post_data_recv);
net_recv_queue(c, bytes_left, 0, &nnb, http_body_recv);
nnb->extra = req;
} else if (bytes_left == 0) {
req->flags |= HTTP_REQUEST_COMPLETE;
req->flags &= ~HTTP_REQUEST_EXPECT_BODY;
} else {
kore_debug("bytes_left would become zero (%ld)", clen);
http_error_response(req->owner, NULL, 500);
@ -630,9 +642,9 @@ http_populate_arguments(struct http_request *req)
char *query, *args[HTTP_MAX_QUERY_ARGS], *val[3];
if (req->method == HTTP_METHOD_POST) {
if (req->post_data == NULL)
if (req->http_body == NULL)
return (0);
query = http_post_data_text(req);
query = http_body_text(req);
} else {
if (req->query_string == NULL)
return (0);
@ -825,7 +837,7 @@ http_populate_multipart_form(struct http_request *req, int *v)
slen = l;
kore_mem_free(type);
req->multipart_body = http_post_data_bytes(req, &blen);
req->multipart_body = http_body_bytes(req, &blen);
if (slen < 3 || blen < (slen * 2)) {
kore_mem_free(boundary);
return (KORE_RESULT_ERROR);
@ -943,17 +955,17 @@ http_generic_404(struct http_request *req)
}
char *
http_post_data_text(struct http_request *req)
http_body_text(struct http_request *req)
{
u_int32_t len;
u_int8_t *data;
char *text;
if (req->post_data == NULL)
if (req->http_body == NULL)
return (NULL);
data = kore_buf_release(req->post_data, &len);
req->post_data = NULL;
data = kore_buf_release(req->http_body, &len);
req->http_body = NULL;
len++;
text = kore_malloc(len);
@ -964,15 +976,15 @@ http_post_data_text(struct http_request *req)
}
u_int8_t *
http_post_data_bytes(struct http_request *req, u_int32_t *len)
http_body_bytes(struct http_request *req, u_int32_t *len)
{
u_int8_t *data;
if (req->post_data == NULL)
if (req->http_body == NULL)
return (NULL);
data = kore_buf_release(req->post_data, len);
req->post_data = NULL;
data = kore_buf_release(req->http_body, len);
req->http_body = NULL;
return (data);
}
@ -1073,14 +1085,16 @@ http_file_add(struct http_request *req, const char *name, const char *filename,
}
static int
http_post_data_recv(struct netbuf *nb)
http_body_recv(struct netbuf *nb)
{
struct http_request *req = (struct http_request *)nb->extra;
kore_buf_append(req->post_data, nb->buf, nb->s_off);
req->flags |= HTTP_REQUEST_COMPLETE;
kore_buf_append(req->http_body, nb->buf, nb->s_off);
kore_debug("post complete for request %p", req);
req->flags |= HTTP_REQUEST_COMPLETE;
req->flags &= ~HTTP_REQUEST_EXPECT_BODY;
kore_debug("received all body data for request %p", req);
return (KORE_RESULT_OK);
}
@ -1128,9 +1142,6 @@ http_response_spdy(struct http_request *req, struct connection *c,
KORE_VERSION_STATE);
spdy_header_block_add(hblock, ":server", sbuf);
if (status == HTTP_STATUS_METHOD_NOT_ALLOWED)
spdy_header_block_add(hblock, ":allow", "get, post");
if (http_hsts_enable) {
(void)snprintf(sbuf, sizeof(sbuf),
"max-age=%" PRIu64, http_hsts_enable);
@ -1153,7 +1164,7 @@ http_response_spdy(struct http_request *req, struct connection *c,
net_send_queue(c, htext, hlen, NULL, NETBUF_LAST_CHAIN);
kore_mem_free(htext);
if (len > 0) {
if (len > 0 && req != NULL && req->method != HTTP_METHOD_HEAD) {
s->send_size += len;
s->flags |= SPDY_DATAFRAME_PRELUDE;
@ -1181,9 +1192,6 @@ http_response_normal(struct http_request *req, struct connection *c,
KORE_NAME_STRING, KORE_VERSION_MAJOR, KORE_VERSION_MINOR,
KORE_VERSION_STATE);
if (status == HTTP_STATUS_METHOD_NOT_ALLOWED)
kore_buf_appendf(header_buf, "allow: GET, POST\r\n");
if (c->flags & CONN_CLOSE_EMPTY)
connection_close = 1;
else
@ -1227,7 +1235,7 @@ http_response_normal(struct http_request *req, struct connection *c,
net_send_queue(c, header_buf->data, header_buf->offset,
NULL, NETBUF_LAST_CHAIN);
if (d != NULL)
if (d != NULL && req != NULL && req->method != HTTP_METHOD_HEAD)
net_send_queue(c, d, len, NULL, NETBUF_LAST_CHAIN);
if (!(c->flags & CONN_CLOSE_EMPTY)) {

View File

@ -306,7 +306,6 @@ pgsql_schedule(struct kore_pgsql *pgsql, struct http_request *req)
kore_platform_schedule_read(fd, pgsql->conn);
pgsql->state = KORE_PGSQL_STATE_WAIT;
kore_debug("query '%s' for %p sent on %p", query, req, pgsql->conn);
}
static void

View File

@ -781,16 +781,16 @@ spdy_data_frame_recv(struct netbuf *nb)
}
req = (struct http_request *)s->httpreq;
if (req == NULL || req->method != HTTP_METHOD_POST) {
if (req == NULL || !(req->flags & HTTP_REQUEST_EXPECT_BODY)) {
kore_debug("data frame for non post received");
/* stream error */
return (KORE_RESULT_ERROR);
}
if (req->post_data == NULL) {
if (req->http_body == NULL) {
if (!spdy_stream_get_header(s->hblock,
"content-length", &content)) {
kore_debug("no content-length found for post");
kore_debug("no content-length found for body");
return (KORE_RESULT_ERROR);
}
@ -805,28 +805,29 @@ spdy_data_frame_recv(struct netbuf *nb)
if (s->post_size == 0) {
req->flags |= HTTP_REQUEST_COMPLETE;
req->flags &= ~HTTP_REQUEST_EXPECT_BODY;
return (KORE_RESULT_OK);
}
if (s->post_size > http_postbody_max) {
kore_log(LOG_NOTICE, "POST data too large (%ld > %ld)",
s->post_size, http_postbody_max);
if (s->post_size > http_body_max) {
kore_log(LOG_NOTICE, "body data too large (%ld > %ld)",
s->post_size, http_body_max);
return (KORE_RESULT_ERROR);
}
req->post_data = kore_buf_create(s->post_size);
req->http_body = kore_buf_create(s->post_size);
}
if ((req->post_data->offset + data.length) > s->post_size) {
if ((req->http_body->offset + data.length) > s->post_size) {
kore_debug("POST would grow too large");
return (KORE_RESULT_ERROR);
}
kore_buf_append(req->post_data, (nb->buf + SPDY_FRAME_SIZE),
kore_buf_append(req->http_body, (nb->buf + SPDY_FRAME_SIZE),
data.length);
if (data.flags & FLAG_FIN) {
if (req->post_data->offset != s->post_size) {
if (req->http_body->offset != s->post_size) {
kore_debug("FLAG_FIN before all POST data received");
return (KORE_RESULT_ERROR);
}
@ -834,6 +835,7 @@ spdy_data_frame_recv(struct netbuf *nb)
s->post_size = 0;
s->flags |= FLAG_FIN;
req->flags |= HTTP_REQUEST_COMPLETE;
req->flags &= ~HTTP_REQUEST_EXPECT_BODY;
}
/*