Files
uwsgi/plugins/http/keepalive.c
T
2014-05-21 21:35:54 +02:00

188 lines
5.5 KiB
C

#include "common.h"
extern struct uwsgi_http uhttp;
#ifdef UWSGI_ZLIB
static char gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
#endif
int http_response_parse(struct http_session *hr, struct uwsgi_buffer *ub, size_t len) {
size_t i;
size_t next = 0;
char *buf = ub->buf;
int found = 0;
// protocol
for(i=0;i<len;i++) {
if (buf[i] == ' ') {
if (hr->session.can_keepalive && uwsgi_strncmp("HTTP/1.1", 8, buf, i)) {
goto end;
}
if (i+1 >= len) return -1;;
next = i+1;
found = 1;
break;
}
}
if (!found) goto end;
// status
found = 0;
for(i=next;i<len;i++) {
if (buf[i] == '\r' || buf[i] == '\n') {
// status ready
if (i+1 >= len) return -1;
next = i + 1;
found = 1;
break;
}
}
if (!found) goto end;
char *key = NULL;
// find first header position
for(i=next;i<len;i++) {
if (buf[i] != '\r' && buf[i] != '\n') {
key = buf + i;
next = i;
break;
}
}
if (!key) goto end;
uint32_t h_len = 0;
int has_size = 0;
int has_connection = 0;
for(i=next;i<len;i++) {
if (key) {
if (buf[i] == '\r' || buf[i] == '\n') {
char *colon = memchr(key, ':', h_len);
if (!colon) return -1;
// security check
if (colon+2 >= buf+len) return -1;
#ifdef UWSGI_ZLIB
if (hr->session.can_keepalive || (uhttp.auto_gzip && hr->can_gzip)) {
#else
if (hr->session.can_keepalive) {
#endif
if (!uwsgi_strnicmp(key, colon-key, "Connection", 10)) {
has_connection = 1;
if (!uwsgi_strnicmp(colon+2, h_len-((colon-key)+2), "close", 5)) {
goto end;
}
}
else if (!uwsgi_strnicmp(key, colon-key, "Trailers", 8)) {
goto end;
}
else if (!uwsgi_strnicmp(key, colon-key, "Content-Length", 14)) {
has_size = 1;
}
else if (!uwsgi_strnicmp(key, colon-key, "Transfer-Encoding", 17)) {
has_size = 1;
}
}
#ifdef UWSGI_ZLIB
if (uhttp.auto_gzip && hr->can_gzip) {
if (!uwsgi_strnicmp(key, colon-key, "Content-Encoding", 16)) {
hr->can_gzip = 0;
}
else if (!uwsgi_strnicmp(key, colon-key, "uWSGI-Encoding", 14)) {
if (!uwsgi_strnicmp(colon+2, h_len-((colon-key)+2), "gzip", 4)) {
hr->has_gzip = 1;
}
}
}
#endif
key = NULL;
h_len = 0;
}
else {
h_len++;
}
}
else {
if (buf[i] != '\r' && buf[i] != '\n') {
key = buf+i;
h_len = 1;
}
}
}
if (!has_size) {
#ifdef UWSGI_ZLIB
if (hr->has_gzip) {
hr->force_gzip = 1;
if (uwsgi_deflate_init(&hr->z, NULL, 0)) {
hr->force_gzip = 0;
goto end;
}
hr->gzip_crc32 = 0;
uwsgi_crc32(&hr->gzip_crc32, NULL, 0);
hr->gzip_size = 0;
char cr = buf[len-2];
char nl = buf[len-1];
if (cr == '\r' && nl == '\n') {
if (uwsgi_buffer_insert(ub, len-2, "Transfer-Encoding: chunked\r\n", 28)) return -1;
if (uwsgi_buffer_insert(ub, len-2+28, "Content-Encoding: gzip\r\n", 24)) return -1;
size_t remains = ub->pos - (len+28+24);
if (remains > 0) {
size_t zlen = 0;
char *ptr = ub->buf + (ub->pos - remains);
char *gzipped = uwsgi_deflate(&hr->z, ptr, remains, &zlen);
if (!gzipped) return -1;
uwsgi_crc32(&hr->gzip_crc32, ptr, remains);
hr->gzip_size += remains;
ub->pos = len + 28 + 24;
if (uwsgi_buffer_append_chunked(ub, zlen + 10)) {free(gzipped); return -1;}
if (uwsgi_buffer_append(ub, gzheader, 10)) {free(gzipped); return -1;}
if (uwsgi_buffer_append(ub, gzipped, zlen)) {free(gzipped); return -1;}
free(gzipped);
if (uwsgi_buffer_append(ub, "\r\n", 2)) return -1;
}
else {
if (uwsgi_buffer_append_chunked(ub, 10)) return -1;
if (uwsgi_buffer_append(ub, gzheader, 10)) return -1;
if (uwsgi_buffer_append(ub, "\r\n", 2)) return -1;
}
}
} else
#endif
if (hr->session.can_keepalive) {
if (uhttp.auto_chunked) {
char cr = buf[len-2];
char nl = buf[len-1];
if (cr == '\r' && nl == '\n') {
if (uwsgi_buffer_insert(ub, len-2, "Transfer-Encoding: chunked\r\n", 28)) return -1;
size_t remains = ub->pos - (len+28);
if (remains > 0) {
if (uwsgi_buffer_insert_chunked(ub, len + 28, remains)) return -1;
if (uwsgi_buffer_append(ub, "\r\n", 2)) return -1;
}
hr->force_chunked = 1;
return 0;
}
}
// avoid iOS making mess...
if (!has_connection) {
if (uwsgi_buffer_insert(ub, len-2, "Connection: close\r\n", 19)) return -1;
}
hr->session.can_keepalive = 0;
}
}
return 0;
end:
hr->session.can_keepalive = 0;
return 0;
}