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

@ -26,23 +26,23 @@ 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");
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;
}
@ -67,7 +67,7 @@ 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);
@ -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) {
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;
}
}
@ -90,16 +90,27 @@ http_response_set_header(struct http_response *r, const char *k, const char *v)
r->header_count++;
}
/* copy key */
r->headers[pos].key = calloc(key_sz + 1, 1);
memcpy(r->headers[pos].key, k, key_sz);
/* 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 */
r->headers[pos].val = calloc(val_sz + 1, 1);
memcpy(r->headers[pos].val, v, val_sz);
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 +137,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 +217,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);
} 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;
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 +328,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 +342,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,20 @@
struct http_client;
struct worker;
typedef enum {
HEADER_COPY_NONE = 0,
HEADER_COPY_KEY = 1,
HEADER_COPY_VALUE = 2
} header_copy;
struct http_header {
char *key;
size_t key_sz;
char *val;
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);
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