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)
{
if (cl->dispatch.free)
cl->dispatch.free(cl);
+ if (cl->dispatch.req_free)
+ cl->dispatch.req_free(cl);
+}
+
+static void client_timeout(struct uloop_timeout *timeout)
+{
+ struct client *cl = container_of(timeout, struct client, timeout);
+
+ cl->state = CLIENT_STATE_CLOSE;
+ uh_connection_close(cl);
+}
+
+static void uh_set_client_timeout(struct client *cl, int timeout)
+{
+ cl->timeout.cb = client_timeout;
+ uloop_timeout_set(&cl->timeout, timeout * 1000);
+}
+
+static void uh_keepalive_poll_cb(struct uloop_timeout *timeout)
+{
+ struct client *cl = container_of(timeout, struct client, timeout);
+ int sec = cl->requests > 0 ? conf.http_keepalive : conf.network_timeout;
+
+ uh_set_client_timeout(cl, sec);
+ cl->us->notify_read(cl->us, 0);
+}
+
+static void uh_poll_connection(struct client *cl)
+{
+ cl->timeout.cb = uh_keepalive_poll_cb;
+ uloop_timeout_set(&cl->timeout, 1);
}
void uh_request_done(struct client *cl)
{
uh_chunk_eof(cl);
uh_dispatch_done(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);
+ cl->requests++;
+ uh_poll_connection(cl);
}
void __printf(4, 5)
uh_connection_close(cl);
}
-static void client_timeout(struct uloop_timeout *timeout)
-{
- struct client *cl = container_of(timeout, struct client, timeout);
-
- cl->state = CLIENT_STATE_CLOSE;
- uh_connection_close(cl);
-}
-
static int find_idx(const char * const *list, int max, const char *str)
{
int i;
req->method = h_method;
req->version = h_version;
+ if (req->version < UH_HTTP_VER_1_1 || req->method == UH_HTTP_MSG_POST ||
+ !conf.http_keepalive)
+ req->connection_close = true;
return CLIENT_STATE_HEADER;
}
if (!newline)
return false;
+ if (newline == buf) {
+ ustream_consume(cl->us, 2);
+ return true;
+ }
+
*newline = 0;
blob_buf_init(&cl->hdr, 0);
cl->state = client_parse_request(cl, buf);
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);
}
} 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 (!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;
}
static void client_ustream_read_cb(struct ustream *s, int bytes)
{
- struct client *cl = container_of(s, struct client, sfd);
+ struct client *cl = container_of(s, struct client, sfd.stream);
uh_client_read_cb(cl);
}
static void client_ustream_write_cb(struct ustream *s, int bytes)
{
- struct client *cl = container_of(s, struct client, sfd);
+ struct client *cl = container_of(s, struct client, sfd.stream);
if (cl->dispatch.write_cb)
cl->dispatch.write_cb(cl);
static void client_notify_state(struct ustream *s)
{
- struct client *cl = container_of(s, struct client, sfd);
+ struct client *cl = container_of(s, struct client, sfd.stream);
uh_client_notify_state(cl);
}
cl->us->string_data = true;
ustream_fd_init(&cl->sfd, sfd);
- cl->timeout.cb = client_timeout;
- uloop_timeout_set(&cl->timeout, conf.network_timeout * 1000);
-
+ uh_poll_connection(cl);
list_add_tail(&cl->list, &clients);
next_client = NULL;
n_clients++;
cl->id = client_id++;
+ cl->tls = tls;
return true;
}