Avoid copying header strings for http_response

http_response has an array of http_header key/value pairs, and most of
the time these use constant strings that do not need to be copied and
re-allocated. This change adds a flag tracking which values need to be
copied, were copied and need to be freed.
master
Jessie Murray 3 years ago
parent 93e96565a6
commit c7c6fc010f
No known key found for this signature in database
GPG Key ID: E7E4D57EDDA744C5

@ -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);

@ -26,23 +26,23 @@ 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"); 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;
} }
@ -67,7 +67,7 @@ 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);
@ -77,8 +77,8 @@ http_response_set_header(struct http_response *r, const char *k, const char *v)
if(strncmp(r->headers[i].key, k, key_sz) == 0) { if(strncmp(r->headers[i].key, k, key_sz) == 0) {
pos = i; pos = i;
/* free old value before replacing it. */ /* free old value before replacing it. */
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);
break; break;
} }
} }
@ -90,16 +90,27 @@ http_response_set_header(struct http_response *r, const char *k, const char *v)
r->header_count++; 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 +137,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 +217,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);
} else { } else {
http_response_set_header(r, "Content-Length", "0"); http_response_set_header(r, "Content-Length", "0", HEADER_COPY_NONE);
} }
} }
@ -290,7 +301,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 +328,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 +342,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,20 @@
struct http_client; struct http_client;
struct worker; struct worker;
typedef enum {
HEADER_COPY_NONE = 0,
HEADER_COPY_KEY = 1,
HEADER_COPY_VALUE = 2
} 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;
}; };
@ -49,7 +57,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