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_version = cmd->http_version;
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_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_write(resp, cmd->fd);
} else {
@ -109,10 +109,10 @@ format_send_reply(struct cmd *cmd, const char *p, size_t sz, const char *content
} else {
resp = http_response_init(cmd->w, 200, "OK");
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, "ETag", etag);
http_response_set_header(resp, "Content-Type", ct, HEADER_COPY_VALUE);
http_response_set_header(resp, "ETag", etag, HEADER_COPY_VALUE);
http_response_set_body(resp, p, sz);
}
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. */
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_write(resp, cmd->fd);

@ -11,6 +11,14 @@
/* 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 *
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->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. */
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
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-Origin", "*");
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", "*", HEADER_COPY_NONE);
/*
According to
http://www.w3.org/TR/cors/#access-control-allow-headers-response-header
Access-Control-Allow-Headers cannot be a wildcard and must be set
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;
}
@ -57,6 +73,14 @@ http_response_init_with_buffer(struct worker *w, char *data, size_t data_sz, int
}
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 */
r->out = data;
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
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;
size_t key_sz = strlen(k);
size_t val_sz = strlen(v);
if(copy & HEADER_CHECK_DUPE) {
for(i = 0; i < r->header_count; ++i) {
if(strncmp(r->headers[i].key, k, key_sz) == 0) {
pos = i;
/* free old value before replacing it. */
free(r->headers[i].key);
free(r->headers[i].val);
if(r->headers[i].copy & HEADER_COPY_KEY) free(r->headers[i].key);
if(r->headers[i].copy & HEADER_COPY_VALUE) free(r->headers[i].val);
break;
}
}
}
/* extend array */
if(pos == r->header_count) {
if(pos == r->headers_array_size) {
/* FIXME: allocation could fail */
r->headers = realloc(r->headers,
sizeof(struct http_header)*(r->header_count + 1));
r->header_count++;
sizeof(struct http_header)*(r->headers_array_size + 1));
r->headers_array_size++;
}
r->header_count++;
/* copy key */
/* copy key if needed */
if(copy & HEADER_COPY_KEY) {
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;
/* copy val */
if(copy & HEADER_COPY_VALUE) {
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;
/* track what was copied */
r->headers[pos].copy = copy;
if(!r->chunked && !strcmp(k, "Transfer-Encoding") && !strcmp(v, "chunked")) {
r->chunked = 1;
}
@ -126,8 +165,8 @@ http_response_cleanup(struct http_response *r, int fd, int success) {
/* cleanup response object */
for(i = 0; i < r->header_count; ++i) {
free(r->headers[i].key);
free(r->headers[i].val);
if(r->headers[i].copy & HEADER_COPY_KEY) free(r->headers[i].key);
if(r->headers[i].copy & HEADER_COPY_VALUE) free(r->headers[i].val);
}
free(r->headers);
@ -206,9 +245,9 @@ http_response_write(struct http_response *r, int fd) {
if(r->code == 200 && r->body) {
char content_length[22];
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 {
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;
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_write(resp, c->fd);
@ -317,9 +356,9 @@ void
http_response_set_keep_alive(struct http_response *r, int enabled) {
r->keep_alive = enabled;
if(enabled) {
http_response_set_header(r, "Connection", "Keep-Alive");
http_response_set_header(r, "Connection", "Keep-Alive", HEADER_COPY_NONE);
} 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;
http_response_set_connection_header(c, resp);
http_response_set_header(resp, "Content-Type", "text/html");
http_response_set_header(resp, "Content-Length", "0");
http_response_set_header(resp, "Content-Type", "text/html", HEADER_COPY_NONE);
http_response_set_header(resp, "Content-Length", "0", HEADER_COPY_NONE);
http_response_write(resp, c->fd);
http_client_reset(c);

@ -7,12 +7,22 @@
struct http_client;
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 {
char *key;
size_t key_sz;
char *val;
size_t val_sz;
header_copy copy;
};
@ -24,7 +34,8 @@ struct http_response {
const char *msg;
struct http_header *headers;
int header_count;
int header_count; /* actual count in array */
int headers_array_size; /* allocated size */
const char *body;
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);
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
http_response_set_body(struct http_response *r, const char *body, size_t body_len);

Loading…
Cancel
Save