Merge pull request #205 from jessie-murray/http-improvements

master
Nicolas Favre-Felix 3 years ago committed by GitHub
commit 74d4092ac6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -85,11 +85,11 @@ format_send_reply(struct cmd *cmd, const char *p, size_t sz, const char *content
resp = http_response_init(cmd->w, 200, "OK"); resp = http_response_init(cmd->w, 200, "OK");
resp->http_version = cmd->http_version; resp->http_version = cmd->http_version;
if(cmd->filename) { if(cmd->filename) {
http_response_set_header(resp, "Content-Disposition", cmd->filename); http_response_set_header(resp, "Content-Disposition", cmd->filename, HEADER_COPY_VALUE);
} }
http_response_set_header(resp, "Content-Type", ct); http_response_set_header(resp, "Content-Type", ct, HEADER_COPY_VALUE);
http_response_set_keep_alive(resp, 1); http_response_set_keep_alive(resp, 1);
http_response_set_header(resp, "Transfer-Encoding", "chunked"); http_response_set_header(resp, "Transfer-Encoding", "chunked", HEADER_COPY_NONE);
http_response_set_body(resp, p, sz); http_response_set_body(resp, p, sz);
http_response_write(resp, cmd->fd); http_response_write(resp, cmd->fd);
} else { } else {
@ -109,10 +109,10 @@ format_send_reply(struct cmd *cmd, const char *p, size_t sz, const char *content
} else { } else {
resp = http_response_init(cmd->w, 200, "OK"); resp = http_response_init(cmd->w, 200, "OK");
if(cmd->filename) { if(cmd->filename) {
http_response_set_header(resp, "Content-Disposition", cmd->filename); http_response_set_header(resp, "Content-Disposition", cmd->filename, HEADER_COPY_VALUE);
} }
http_response_set_header(resp, "Content-Type", ct); http_response_set_header(resp, "Content-Type", ct, HEADER_COPY_VALUE);
http_response_set_header(resp, "ETag", etag); http_response_set_header(resp, "ETag", etag, HEADER_COPY_VALUE);
http_response_set_body(resp, p, sz); http_response_set_body(resp, p, sz);
} }
resp->http_version = cmd->http_version; resp->http_version = cmd->http_version;

@ -63,7 +63,7 @@ custom_type_reply(redisAsyncContext *c, void *r, void *privdata) {
/* couldn't make sense of what the client wanted. */ /* couldn't make sense of what the client wanted. */
resp = http_response_init(cmd->w, 400, "Bad Request"); resp = http_response_init(cmd->w, 400, "Bad Request");
http_response_set_header(resp, "Content-Length", "0"); http_response_set_header(resp, "Content-Length", "0", HEADER_COPY_NONE);
http_response_set_keep_alive(resp, cmd->keep_alive); http_response_set_keep_alive(resp, cmd->keep_alive);
http_response_write(resp, cmd->fd); http_response_write(resp, cmd->fd);

@ -11,6 +11,14 @@
/* HTTP Response */ /* HTTP Response */
#define DEFAULT_HEADERS_ARRAY_SIZE 9
static void
http_response_allocate_headers(struct http_response *r) {
r->headers_array_size = DEFAULT_HEADERS_ARRAY_SIZE;
r->headers = calloc(r->headers_array_size, sizeof(struct http_header));
}
struct http_response * struct http_response *
http_response_init(struct worker *w, int code, const char *msg) { http_response_init(struct worker *w, int code, const char *msg) {
@ -26,23 +34,31 @@ http_response_init(struct worker *w, int code, const char *msg) {
r->w = w; r->w = w;
r->keep_alive = 0; /* default */ r->keep_alive = 0; /* default */
http_response_set_header(r, "Server", "Webdis"); /* pre-allocate array for headers */
http_response_allocate_headers(r);
if(!r->headers) {
if(w && w->s) slog(w->s, WEBDIS_ERROR, "Failed to allocate http_response headers", 0);
free(r);
return NULL;
}
http_response_set_header(r, "Server", "Webdis", HEADER_COPY_NONE);
/* Cross-Origin Resource Sharing, CORS. */ /* Cross-Origin Resource Sharing, CORS. */
http_response_set_header(r, "Allow", "GET,POST,PUT,OPTIONS"); http_response_set_header(r, "Allow", "GET,POST,PUT,OPTIONS", HEADER_COPY_NONE);
/* /*
Chrome doesn't support Allow and requires Chrome doesn't support Allow and requires
Access-Control-Allow-Methods Access-Control-Allow-Methods
*/ */
http_response_set_header(r, "Access-Control-Allow-Methods", "GET,POST,PUT,OPTIONS"); http_response_set_header(r, "Access-Control-Allow-Methods", "GET,POST,PUT,OPTIONS", HEADER_COPY_NONE);
http_response_set_header(r, "Access-Control-Allow-Origin", "*"); http_response_set_header(r, "Access-Control-Allow-Origin", "*", HEADER_COPY_NONE);
/* /*
According to According to
http://www.w3.org/TR/cors/#access-control-allow-headers-response-header http://www.w3.org/TR/cors/#access-control-allow-headers-response-header
Access-Control-Allow-Headers cannot be a wildcard and must be set Access-Control-Allow-Headers cannot be a wildcard and must be set
with explicit names with explicit names
*/ */
http_response_set_header(r, "Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization"); http_response_set_header(r, "Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Authorization", HEADER_COPY_NONE);
return r; return r;
} }
@ -57,6 +73,14 @@ http_response_init_with_buffer(struct worker *w, char *data, size_t data_sz, int
} }
r->w = w; r->w = w;
/* pre-allocate array for headers */
http_response_allocate_headers(r);
if(!r->headers) {
if(w && w->s) slog(w->s, WEBDIS_ERROR, "Failed to allocate http_response headers", 0);
free(r);
return NULL;
}
/* provide buffer directly */ /* provide buffer directly */
r->out = data; r->out = data;
r->out_sz = data_sz; r->out_sz = data_sz;
@ -67,39 +91,54 @@ http_response_init_with_buffer(struct worker *w, char *data, size_t data_sz, int
void void
http_response_set_header(struct http_response *r, const char *k, const char *v) { http_response_set_header(struct http_response *r, const char *k, const char *v, header_copy copy) {
int i, pos = r->header_count; int i, pos = r->header_count;
size_t key_sz = strlen(k); size_t key_sz = strlen(k);
size_t val_sz = strlen(v); size_t val_sz = strlen(v);
for(i = 0; i < r->header_count; ++i) { if(copy & HEADER_CHECK_DUPE) {
if(strncmp(r->headers[i].key, k, key_sz) == 0) { for(i = 0; i < r->header_count; ++i) {
pos = i; if(strncmp(r->headers[i].key, k, key_sz) == 0) {
/* free old value before replacing it. */ pos = i;
free(r->headers[i].key); /* free old value before replacing it. */
free(r->headers[i].val); if(r->headers[i].copy & HEADER_COPY_KEY) free(r->headers[i].key);
break; if(r->headers[i].copy & HEADER_COPY_VALUE) free(r->headers[i].val);
break;
}
} }
} }
/* extend array */ /* extend array */
if(pos == r->header_count) { if(pos == r->headers_array_size) {
/* FIXME: allocation could fail */
r->headers = realloc(r->headers, r->headers = realloc(r->headers,
sizeof(struct http_header)*(r->header_count + 1)); sizeof(struct http_header)*(r->headers_array_size + 1));
r->header_count++; r->headers_array_size++;
} }
r->header_count++;
/* copy key */ /* copy key if needed */
r->headers[pos].key = calloc(key_sz + 1, 1); if(copy & HEADER_COPY_KEY) {
memcpy(r->headers[pos].key, k, key_sz); r->headers[pos].key = calloc(key_sz + 1, 1);
memcpy(r->headers[pos].key, k, key_sz);
} else {
r->headers[pos].key = (char *)k;
}
r->headers[pos].key_sz = key_sz; r->headers[pos].key_sz = key_sz;
/* copy val */ /* copy val */
r->headers[pos].val = calloc(val_sz + 1, 1); if(copy & HEADER_COPY_VALUE) {
memcpy(r->headers[pos].val, v, val_sz); r->headers[pos].val = calloc(val_sz + 1, 1);
memcpy(r->headers[pos].val, v, val_sz);
} else {
r->headers[pos].val = (char *)v;
}
r->headers[pos].val_sz = val_sz; r->headers[pos].val_sz = val_sz;
/* track what was copied */
r->headers[pos].copy = copy;
if(!r->chunked && !strcmp(k, "Transfer-Encoding") && !strcmp(v, "chunked")) { if(!r->chunked && !strcmp(k, "Transfer-Encoding") && !strcmp(v, "chunked")) {
r->chunked = 1; r->chunked = 1;
} }
@ -126,8 +165,8 @@ http_response_cleanup(struct http_response *r, int fd, int success) {
/* cleanup response object */ /* cleanup response object */
for(i = 0; i < r->header_count; ++i) { for(i = 0; i < r->header_count; ++i) {
free(r->headers[i].key); if(r->headers[i].copy & HEADER_COPY_KEY) free(r->headers[i].key);
free(r->headers[i].val); if(r->headers[i].copy & HEADER_COPY_VALUE) free(r->headers[i].val);
} }
free(r->headers); free(r->headers);
@ -206,9 +245,9 @@ http_response_write(struct http_response *r, int fd) {
if(r->code == 200 && r->body) { if(r->code == 200 && r->body) {
char content_length[22]; char content_length[22];
sprintf(content_length, "%zd", r->body_len); sprintf(content_length, "%zd", r->body_len);
http_response_set_header(r, "Content-Length", content_length); http_response_set_header(r, "Content-Length", content_length, HEADER_COPY_VALUE | HEADER_CHECK_DUPE);
} else { } else {
http_response_set_header(r, "Content-Length", "0"); http_response_set_header(r, "Content-Length", "0", HEADER_COPY_NONE | HEADER_CHECK_DUPE);
} }
} }
@ -290,7 +329,7 @@ http_crossdomain(struct http_client *c) {
resp->http_version = c->http_version; resp->http_version = c->http_version;
http_response_set_connection_header(c, resp); http_response_set_connection_header(c, resp);
http_response_set_header(resp, "Content-Type", "application/xml"); http_response_set_header(resp, "Content-Type", "application/xml", HEADER_COPY_NONE);
http_response_set_body(resp, out, sizeof(out)-1); http_response_set_body(resp, out, sizeof(out)-1);
http_response_write(resp, c->fd); http_response_write(resp, c->fd);
@ -317,9 +356,9 @@ void
http_response_set_keep_alive(struct http_response *r, int enabled) { http_response_set_keep_alive(struct http_response *r, int enabled) {
r->keep_alive = enabled; r->keep_alive = enabled;
if(enabled) { if(enabled) {
http_response_set_header(r, "Connection", "Keep-Alive"); http_response_set_header(r, "Connection", "Keep-Alive", HEADER_COPY_NONE);
} else { } else {
http_response_set_header(r, "Connection", "Close"); http_response_set_header(r, "Connection", "Close", HEADER_COPY_NONE);
} }
} }
@ -331,8 +370,8 @@ http_send_options(struct http_client *c) {
resp->http_version = c->http_version; resp->http_version = c->http_version;
http_response_set_connection_header(c, resp); http_response_set_connection_header(c, resp);
http_response_set_header(resp, "Content-Type", "text/html"); http_response_set_header(resp, "Content-Type", "text/html", HEADER_COPY_NONE);
http_response_set_header(resp, "Content-Length", "0"); http_response_set_header(resp, "Content-Length", "0", HEADER_COPY_NONE);
http_response_write(resp, c->fd); http_response_write(resp, c->fd);
http_client_reset(c); http_client_reset(c);

@ -7,12 +7,22 @@
struct http_client; struct http_client;
struct worker; struct worker;
/* bit flags */
typedef enum {
HEADER_COPY_NONE = 0, /* don't strdup key or value */
HEADER_COPY_KEY = 1, /* strdup key only */
HEADER_COPY_VALUE = 2, /* strdup value only */
HEADER_CHECK_DUPE = 4 /* replace duplicate when adding header */
} header_copy;
struct http_header { struct http_header {
char *key; char *key;
size_t key_sz; size_t key_sz;
char *val; char *val;
size_t val_sz; size_t val_sz;
header_copy copy;
}; };
@ -24,7 +34,8 @@ struct http_response {
const char *msg; const char *msg;
struct http_header *headers; struct http_header *headers;
int header_count; int header_count; /* actual count in array */
int headers_array_size; /* allocated size */
const char *body; const char *body;
size_t body_len; size_t body_len;
@ -49,7 +60,7 @@ struct http_response *
http_response_init_with_buffer(struct worker *w, char *data, size_t data_sz, int keep_alive); http_response_init_with_buffer(struct worker *w, char *data, size_t data_sz, int keep_alive);
void void
http_response_set_header(struct http_response *r, const char *k, const char *v); http_response_set_header(struct http_response *r, const char *k, const char *v, header_copy copy);
void void
http_response_set_body(struct http_response *r, const char *body, size_t body_len); http_response_set_body(struct http_response *r, const char *body, size_t body_len);

Loading…
Cancel
Save