Add support for multipart forms.

New API functions (docs need to be updated):
	- http_file_lookup()
	- http_file_add()
	- http_argument_add()
	- kore_strip_chars()
	- kore_mem_find()

- Add an example under the example module on how files can be read.
This commit is contained in:
Joris Vink 2013-09-10 11:02:59 +02:00
parent 8566c32da8
commit 25e8f93331
7 changed files with 325 additions and 37 deletions

View File

@ -37,6 +37,16 @@ struct http_arg {
TAILQ_ENTRY(http_arg) list;
};
struct http_file {
char *name;
char *filename;
u_int8_t *data;
u_int32_t len;
TAILQ_ENTRY(http_file) list;
};
#define HTTP_METHOD_GET 0
#define HTTP_METHOD_POST 1
@ -56,12 +66,14 @@ struct http_request {
struct spdy_stream *stream;
struct kore_buf *post_data;
void *hdlr_extra;
u_int8_t *multipart_body;
TAILQ_HEAD(, http_header) req_headers;
TAILQ_HEAD(, http_header) resp_headers;
TAILQ_HEAD(, http_arg) arguments;
TAILQ_ENTRY(http_request) list;
TAILQ_ENTRY(http_request) olist;
TAILQ_HEAD(, http_header) req_headers;
TAILQ_HEAD(, http_header) resp_headers;
TAILQ_HEAD(, http_arg) arguments;
TAILQ_HEAD(, http_file) files;
TAILQ_ENTRY(http_request) list;
TAILQ_ENTRY(http_request) olist;
};
extern int http_request_count;
@ -81,10 +93,18 @@ int http_argument_urldecode(char *);
int http_header_recv(struct netbuf *);
int http_generic_404(struct http_request *);
char *http_post_data_text(struct http_request *);
u_int8_t *http_post_data_bytes(struct http_request *, u_int32_t *);
int http_populate_arguments(struct http_request *);
int http_populate_multipart_form(struct http_request *, int *);
void http_argument_multiple_free(struct http_arg *);
void http_file_add(struct http_request *, char *, char *,
u_int8_t *, u_int32_t);
void http_argument_add(struct http_request *, char *,
char *, u_int32_t);
int http_argument_lookup(struct http_request *,
const char *, char **);
int http_file_lookup(struct http_request *, char *, char **,
u_int8_t **, u_int32_t *);
int http_argument_multiple_lookup(struct http_request *,
struct http_arg *);

View File

@ -315,9 +315,11 @@ void kore_log(int, const char *, ...);
void kore_strlcpy(char *, const char *, size_t);
void kore_server_disconnect(struct connection *);
int kore_split_string(char *, char *, char **, size_t);
void kore_strip_chars(char *, char, char **);
long long kore_strtonum(const char *, int, long long, long long, int *);
int kore_base64_encode(u_int8_t *, u_int32_t, char **);
int kore_base64_decode(char *, u_int8_t **, u_int32_t *);
void *kore_mem_find(void *, size_t, void *, u_int32_t);
void kore_domain_init(void);
int kore_domain_new(char *);

View File

@ -9,11 +9,13 @@
<div class="content">
<form method="POST" enctype="multipart/form-data">
<input type="input" name="firstname">
<input type="file" name="file">
<input type="submit" value="upload">
</form>
<p style="font-size: 12px; font-weight: normal">$upload$</p>
<p style="font-size: 12px; font-weight: normal">$firstname$</p>
</div>
</body>

View File

@ -1,8 +1,9 @@
# Example Kore configuration
# Server configuration.
bind 127.0.0.1 443
bind ::1 443
#bind 127.0.0.1 443
#bind ::1 443
bind 10.11.1.174 443
# The path worker processes will chroot too after starting.
chroot /home/joris/src/kore
@ -68,7 +69,8 @@ load modules/example/example.module
# handler path module_callback
# Example domain that responds to localhost.
domain localhost {
#domain localhost {
domain 10.11.1.174 {
certfile cert/server.crt
certkey cert/server.key
accesslog /var/log/kore_access.log

View File

@ -126,20 +126,34 @@ int
serve_file_upload(struct http_request *req)
{
int r;
char *p;
u_int8_t *d;
struct kore_buf *b;
u_int32_t len;
char *name, buf[BUFSIZ];
b = kore_buf_create(static_len_html_upload);
kore_buf_append(b, static_html_upload, static_len_html_upload);
if (req->method == HTTP_METHOD_POST) {
p = http_post_data_text(req);
kore_buf_replace_string(b, "$upload$", p, strlen(p));
kore_mem_free(p);
http_populate_multipart_form(req, &r);
if (http_argument_lookup(req, "firstname", &name)) {
kore_buf_replace_string(b, "$firstname$",
name, strlen(name));
kore_mem_free(name);
} else {
kore_buf_replace_string(b, "$firstname$", NULL, 0);
}
if (http_file_lookup(req, "file", &name, &d, &len)) {
snprintf(buf, sizeof(buf), "%s is %d bytes", name, len);
kore_buf_replace_string(b,
"$upload$", buf, strlen(buf));
} else {
kore_buf_replace_string(b, "$upload$", NULL, 0);
}
} else {
kore_buf_replace_string(b, "$upload$", NULL, 0);
kore_buf_replace_string(b, "$firstname$", NULL, 0);
}
d = kore_buf_release(b, &len);

View File

@ -68,12 +68,14 @@ http_request_new(struct connection *c, struct spdy_stream *s, char *host,
req->stream = s;
req->post_data = NULL;
req->hdlr_extra = NULL;
req->multipart_body = NULL;
kore_strlcpy(req->host, host, sizeof(req->host));
kore_strlcpy(req->path, path, sizeof(req->path));
TAILQ_INIT(&(req->resp_headers));
TAILQ_INIT(&(req->req_headers));
TAILQ_INIT(&(req->arguments));
TAILQ_INIT(&(req->files));
if (!strcasecmp(method, "get")) {
req->method = HTTP_METHOD_GET;
@ -175,6 +177,7 @@ http_response_header_add(struct http_request *req, char *header, char *value)
void
http_request_free(struct http_request *req)
{
struct http_file *f, *fnext;
struct http_arg *q, *qnext;
struct http_header *hdr, *next;
@ -203,13 +206,25 @@ http_request_free(struct http_request *req)
TAILQ_REMOVE(&(req->arguments), q, list);
kore_mem_free(q->name);
if (q->value != NULL)
kore_mem_free(q->value);
kore_mem_free(q);
}
for (f = TAILQ_FIRST(&(req->files)); f != NULL; f = fnext) {
fnext = TAILQ_NEXT(f, list);
TAILQ_REMOVE(&(req->files), f, list);
kore_mem_free(f->filename);
kore_mem_free(f->name);
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)
kore_mem_free(req->multipart_body);
if (req->agent != NULL)
kore_mem_free(req->agent);
@ -323,7 +338,7 @@ http_header_recv(struct netbuf *nb)
struct http_request *req;
struct netbuf *nnb;
size_t clen, len;
u_int8_t *end_headers, *end;
u_int8_t *end_headers;
int h, i, v, skip, bytes_left;
char *request[4], *host[3], *hbuf;
char *p, *headers[HTTP_REQ_HEADER_MAX];
@ -334,19 +349,8 @@ http_header_recv(struct netbuf *nb)
if (nb->len < 4)
return (KORE_RESULT_OK);
end = nb->buf + nb->offset;
for (end_headers = nb->buf; end_headers < end; end_headers++) {
if (*end_headers != '\r')
continue;
if ((end - end_headers) < 4)
return (KORE_RESULT_OK);
if (!memcmp(end_headers, "\r\n\r\n", 4))
break;
}
if (end_headers == end)
end_headers = kore_mem_find(nb->buf, nb->offset, "\r\n\r\n", 4);
if (end_headers == NULL)
return (KORE_RESULT_OK);
*end_headers = '\0';
@ -355,8 +359,6 @@ http_header_recv(struct netbuf *nb)
len = end_headers - nb->buf;
hbuf = (char *)nb->buf;
kore_debug("HTTP request:\n'%s'\n", hbuf);
h = kore_split_string(hbuf, "\r\n", headers, HTTP_REQ_HEADER_MAX);
if (h < 2)
return (KORE_RESULT_ERROR);
@ -458,7 +460,7 @@ http_header_recv(struct netbuf *nb)
int
http_populate_arguments(struct http_request *req)
{
struct http_arg *q;
u_int32_t len;
int i, v, c, count;
char *query, *args[HTTP_MAX_QUERY_ARGS], *val[3];
@ -478,13 +480,12 @@ http_populate_arguments(struct http_request *req)
continue;
}
q = kore_malloc(sizeof(*q));
q->name = kore_strdup(val[0]);
if (c == 2)
q->value = kore_strdup(val[1]);
if (val[1] == NULL)
len = 0;
else
q->value = NULL;
TAILQ_INSERT_TAIL(&(req->arguments), q, list);
len = strlen(val[1]);
http_argument_add(req, val[0], val[1], len);
count++;
}
@ -501,6 +502,7 @@ http_argument_lookup(struct http_request *req, const char *name, char **out)
if (!strcmp(q->name, name)) {
if (q->value == NULL)
return (KORE_RESULT_ERROR);
*out = kore_strdup(q->value);
return (KORE_RESULT_OK);
}
@ -564,8 +566,8 @@ http_argument_multiple_lookup(struct http_request *req, struct http_arg *args)
c = 0;
for (i = 0; args[i].name != NULL; i++) {
if (!http_argument_lookup(req,
args[i].name, &(args[i].value))) {
if (!http_argument_lookup(req, args[i].name,
&(args[i].value))) {
args[i].value = NULL;
} else {
c++;
@ -586,6 +588,201 @@ http_argument_multiple_free(struct http_arg *args)
}
}
void
http_argument_add(struct http_request *req, char *name,
char *value, u_int32_t len)
{
struct http_arg *q;
q = kore_malloc(sizeof(struct http_arg));
q->name = kore_strdup(name);
if (len > 0) {
q->value = kore_malloc(len + 1);
kore_strlcpy(q->value, value, len + 1);
} else {
q->value = NULL;
}
TAILQ_INSERT_TAIL(&(req->arguments), q, list);
}
void
http_file_add(struct http_request *req, char *name, char *filename,
u_int8_t *data, u_int32_t len)
{
struct http_file *f;
f = kore_malloc(sizeof(struct http_file));
f->len = len;
f->data = data;
f->name = kore_strdup(name);
f->filename = kore_strdup(filename);
TAILQ_INSERT_TAIL(&(req->files), f, list);
}
int
http_file_lookup(struct http_request *req, char *name, char **fname,
u_int8_t **data, u_int32_t *len)
{
struct http_file *f;
TAILQ_FOREACH(f, &(req->files), list) {
if (!strcmp(f->name, name)) {
*len = f->len;
*data = f->data;
*fname = f->filename;
return (KORE_RESULT_OK);
}
}
return (KORE_RESULT_ERROR);
}
int
http_populate_multipart_form(struct http_request *req, int *v)
{
int h, i, c;
u_int32_t blen, slen, len;
u_int8_t *s, *end, *e, *end_headers, *data;
char *d, *val, *type, *boundary, *fname;
char *headers[5], *args[5], *opt[5], *name;
*v = 0;
if (req->method != HTTP_METHOD_POST)
return (KORE_RESULT_ERROR);
if (!http_request_header_get(req, "content-type", &type))
return (KORE_RESULT_ERROR);
h = kore_split_string(type, ";", args, 3);
if (h != 2) {
kore_mem_free(type);
return (KORE_RESULT_ERROR);
}
if (strcasecmp(args[0], "multipart/form-data")) {
kore_mem_free(type);
return (KORE_RESULT_ERROR);
}
if ((val = strchr(args[1], '=')) == NULL) {
kore_mem_free(type);
return (KORE_RESULT_ERROR);
}
val++;
slen = strlen(val);
boundary = kore_malloc(slen + 3);
snprintf(boundary, slen + 3, "--%s", val);
slen = strlen(boundary);
kore_mem_free(type);
req->multipart_body = http_post_data_bytes(req, &blen);
if (slen < 3 || blen < (slen * 2)) {
kore_mem_free(boundary);
return (KORE_RESULT_ERROR);
}
end = req->multipart_body + blen - 2;
if (end < req->multipart_body || (end - 2) < req->multipart_body) {
kore_mem_free(boundary);
return (KORE_RESULT_ERROR);
}
if (memcmp((end - slen - 2), boundary, slen) ||
memcmp((end - 2), "--", 2)) {
kore_mem_free(boundary);
return (KORE_RESULT_ERROR);
}
v = 0;
s = req->multipart_body + slen + 2;
while (s < end) {
e = kore_mem_find(s, end - s, boundary, slen);
if (e == NULL) {
kore_mem_free(boundary);
return (KORE_RESULT_ERROR);
}
*(e - 2) = '\0';
end_headers = kore_mem_find(s, (e - 2) - s, "\r\n\r\n", 4);
if (end_headers == NULL) {
kore_mem_free(boundary);
return (KORE_RESULT_ERROR);
}
*end_headers = '\0';
data = end_headers + 4;
h = kore_split_string((char *)s, "\r\n", headers, 5);
for (i = 0; i < h; i++) {
c = kore_split_string(headers[i], ":", args, 5);
if (c != 2)
continue;
/* Ignore other headers for now. */
if (strcasecmp(args[0], "content-disposition"))
continue;
for (d = args[1]; isspace(*d); d++)
;
c = kore_split_string(d, ";", opt, 5);
if (strcasecmp(opt[0], "form-data"))
continue;
if ((val = strchr(opt[1], '=')) == NULL)
continue;
if (strlen(val) < 3)
continue;
val++;
kore_strip_chars(val, '"', &name);
if (opt[2] == NULL) {
http_argument_add(req, name,
(char *)data, (e - 2) - data);
kore_mem_free(name);
continue;
}
for (d = opt[2]; isspace(*d); d++)
;
len = MIN(strlen("filename="), strlen(d));
if (!strncasecmp(d, "filename=", len)) {
if ((val = strchr(d, '=')) == NULL) {
kore_mem_free(name);
continue;
}
val++;
kore_strip_chars(val, '"', &fname);
if (strlen(fname) > 0) {
http_file_add(req, name, fname,
data, (e - 2) - data);
}
kore_mem_free(fname);
} else {
kore_debug("got unknown: %s", opt[2]);
}
kore_mem_free(name);
}
s = e + slen + 2;
}
kore_mem_free(boundary);
return (KORE_RESULT_OK);
}
char *
http_post_data_text(struct http_request *req)
{
@ -604,6 +801,17 @@ http_post_data_text(struct http_request *req)
return (text);
}
u_int8_t *
http_post_data_bytes(struct http_request *req, u_int32_t *len)
{
u_int8_t *data;
data = kore_buf_release(req->post_data, len);
req->post_data = NULL;
return (data);
}
int
http_generic_404(struct http_request *req)
{

View File

@ -138,6 +138,26 @@ kore_split_string(char *input, char *delim, char **out, size_t ele)
return (count);
}
void
kore_strip_chars(char *in, char strip, char **out)
{
u_int32_t len;
char *s, *p;
len = strlen(in);
*out = kore_malloc(len + 1);
p = *out;
for (s = in; s < (in + len); s++) {
if (*s == strip)
continue;
*p++ = *s;
}
*p = '\0';
}
time_t
kore_date_to_time(char *http_date)
{
@ -348,6 +368,26 @@ kore_base64_decode(char *in, u_int8_t **out, u_int32_t *olen)
return (KORE_RESULT_OK);
}
void *
kore_mem_find(void *src, size_t slen, void *needle, u_int32_t len)
{
u_int8_t *p, *end;
end = (u_int8_t *)src + slen;
for (p = src; p < end; p++) {
if (*p != *(u_int8_t *)needle)
continue;
if ((end - p) < len)
return (NULL);
if (!memcmp(p, needle, len))
return (p);
}
return (NULL);
}
void
fatal(const char *fmt, ...)
{