summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2013-01-19 13:10:53 +0100
committerFelix Fietkau <nbd@openwrt.org>2013-01-19 13:21:54 +0100
commit7635644189f73847d9a56911c8883cb7728a34cc (patch)
tree0c2363524d891575f71aa5104545ead8c1b557fc
parent1f4c517bb889413e55ccee466d9dfe79156092e8 (diff)
add user agent detection for working around keepalive issues and add support for the connection: close|keep-alive header
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
-rw-r--r--client.c65
-rw-r--r--uhttpd.h14
2 files changed, 72 insertions, 7 deletions
diff --git a/client.c b/client.c
index d3d5048..b493e7f 100644
--- a/client.c
+++ b/client.c
@@ -42,20 +42,24 @@ const char * const http_methods[] = {
void uh_http_header(struct client *cl, int code, const char *summary)
{
+ struct http_request *r = &cl->request;
const char *enc = "Transfer-Encoding: chunked\r\n";
const char *conn;
if (!uh_use_chunked(cl))
enc = "";
- if (cl->request.version != UH_HTTP_VER_1_1)
+ if (r->connection_close)
conn = "Connection: close";
else
- conn = "Connection: keep-alive";
+ conn = "Connection: Keep-Alive";
ustream_printf(cl->us, "%s %03i %s\r\n%s\r\n%s",
http_versions[cl->request.version],
code, summary, conn, enc);
+
+ if (!r->connection_close)
+ ustream_printf(cl->us, "Keep-Alive: timeout=%d\r\n", conf.http_keepalive);
}
static void uh_connection_close(struct client *cl)
@@ -78,10 +82,8 @@ void uh_request_done(struct client *cl)
cl->us->notify_write = NULL;
memset(&cl->dispatch, 0, sizeof(cl->dispatch));
- if (cl->request.version != UH_HTTP_VER_1_1 || !conf.http_keepalive) {
- uh_connection_close(cl);
- return;
- }
+ if (!conf.http_keepalive || cl->request.connection_close)
+ return uh_connection_close(cl);
cl->state = CLIENT_STATE_INIT;
uloop_timeout_set(&cl->timeout, conf.http_keepalive * 1000);
@@ -155,6 +157,8 @@ static int client_parse_request(struct client *cl, char *data)
req->method = h_method;
req->version = h_version;
+ if (req->version < UH_HTTP_VER_1_1 || !conf.http_keepalive)
+ req->connection_close = true;
return CLIENT_STATE_HEADER;
}
@@ -193,12 +197,27 @@ static bool rfc1918_filter_check(struct client *cl)
static void client_header_complete(struct client *cl)
{
+ struct http_request *r = &cl->request;
+
if (!rfc1918_filter_check(cl))
return;
- if (cl->request.expect_cont)
+ if (r->expect_cont)
ustream_printf(cl->us, "HTTP/1.1 100 Continue\r\n\r\n");
+ switch(r->ua) {
+ case UH_UA_MSIE_OLD:
+ if (r->method != UH_HTTP_MSG_POST)
+ break;
+
+ /* fall through */
+ case UH_UA_SAFARI:
+ r->connection_close = true;
+ break;
+ default:
+ break;
+ }
+
uh_handle_request(cl);
}
@@ -242,6 +261,38 @@ static void client_parse_header(struct client *cl, char *data)
} else if (!strcmp(data, "transfer-encoding")) {
if (!strcmp(val, "chunked"))
r->transfer_chunked = true;
+ } else if (!strcmp(data, "connection")) {
+ if (!strcasecmp(val, "close"))
+ r->connection_close = true;
+ else if (!strcasecmp(val, "keep-alive"))
+ r->connection_close = false;
+ } else if (!strcmp(data, "user-agent")) {
+ char *str;
+
+ if (strstr(val, "Opera"))
+ r->ua = UH_UA_OPERA;
+ else if ((str = strstr(val, "MSIE ")) != NULL) {
+ r->ua = UH_UA_MSIE_NEW;
+ if (str[5] && str[6] == '.') {
+ switch (str[5]) {
+ case '6':
+ if (strstr(str, "SV1"))
+ break;
+ /* fall through */
+ case '5':
+ case '4':
+ r->ua = UH_UA_MSIE_OLD;
+ break;
+ }
+ }
+ } else if (strstr(val, "Safari/") && strstr(val, "Mac OS X"))
+ r->ua = UH_UA_SAFARI;
+ else if (strstr(val, "Chrome/"))
+ r->ua = UH_UA_CHROME;
+ else if (strstr(val, "Gecko/"))
+ r->ua = UH_UA_GECKO;
+ else if (strstr(val, "Konqueror"))
+ r->ua = UH_UA_KONQUEROR;
}
diff --git a/uhttpd.h b/uhttpd.h
index 3226f8d..e73e235 100644
--- a/uhttpd.h
+++ b/uhttpd.h
@@ -86,12 +86,26 @@ enum http_version {
UH_HTTP_VER_1_1,
};
+enum http_user_agent {
+ UH_UA_UNKNOWN,
+ UH_UA_GECKO,
+ UH_UA_CHROME,
+ UH_UA_SAFARI,
+ UH_UA_MSIE,
+ UH_UA_KONQUEROR,
+ UH_UA_OPERA,
+ UH_UA_MSIE_OLD,
+ UH_UA_MSIE_NEW,
+};
+
struct http_request {
enum http_method method;
enum http_version version;
+ enum http_user_agent ua;
int redirect_status;
int content_length;
bool expect_cont;
+ bool connection_close;
uint8_t transfer_chunked;
const struct auth_realm *realm;
};