forked from mirrors/kore
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:
parent
8566c32da8
commit
25e8f93331
|
@ -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 *);
|
||||
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
258
src/http.c
258
src/http.c
|
@ -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)
|
||||
{
|
||||
|
|
40
src/utils.c
40
src/utils.c
|
@ -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, ...)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue