X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuhttpd.git;a=blobdiff_plain;f=client.c;h=9b8fb07025037a30c52548f1e006bb4042a01c81;hp=6faa21cb56c79357bc413f914a06ebaae00169eb;hb=b965b8cc10f094ec0408d57b1bc9aeee0bca501c;hpb=3f5ee063743e34d73c449b0e183f7e8a7a620f4a diff --git a/client.c b/client.c index 6faa21c..9b8fb07 100644 --- a/client.c +++ b/client.c @@ -1,20 +1,20 @@ /* * uhttpd - Tiny single-threaded httpd * - * Copyright (C) 2010-2012 Jo-Philipp Wich - * Copyright (C) 2012 Felix Fietkau + * Copyright (C) 2010-2013 Jo-Philipp Wich + * Copyright (C) 2013 Felix Fietkau * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include @@ -24,6 +24,7 @@ #include "tls.h" static LIST_HEAD(clients); +static bool client_done = false; int n_clients = 0; struct config conf = {}; @@ -38,24 +39,29 @@ const char * const http_methods[] = { [UH_HTTP_MSG_GET] = "GET", [UH_HTTP_MSG_POST] = "POST", [UH_HTTP_MSG_HEAD] = "HEAD", + [UH_HTTP_MSG_OPTIONS] = "OPTIONS", }; 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) @@ -69,22 +75,51 @@ static void uh_dispatch_done(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) @@ -112,14 +147,6 @@ static void uh_header_error(struct client *cl, int code, const char *summary) 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; @@ -155,6 +182,9 @@ 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 || req->method == UH_HTTP_MSG_POST || + !conf.http_keepalive) + req->connection_close = true; return CLIENT_STATE_HEADER; } @@ -167,6 +197,11 @@ static bool client_init_cb(struct client *cl, char *buf, int len) 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); @@ -193,12 +228,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 +292,37 @@ 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 (!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, "Chrome/")) + r->ua = UH_UA_CHROME; + else if (strstr(val, "Safari/") && strstr(val, "Mac OS X")) + r->ua = UH_UA_SAFARI; + else if (strstr(val, "Gecko/")) + r->ua = UH_UA_GECKO; + else if (strstr(val, "Konqueror")) + r->ua = UH_UA_KONQUEROR; } @@ -363,6 +444,7 @@ void uh_client_read_cb(struct client *cl) char *str; int len; + client_done = false; do { str = ustream_get_read_buf(us, &len); if (!str || !len) @@ -377,11 +459,17 @@ void uh_client_read_cb(struct client *cl) uh_header_error(cl, 413, "Request Entity Too Large"); break; } - } while(1); + } while (!client_done); } static void client_close(struct client *cl) { + if (cl->refcount) { + cl->state = CLIENT_STATE_CLEANUP; + return; + } + + client_done = true; n_clients--; uh_dispatch_done(cl); uloop_timeout_cancel(&cl->timeout); @@ -400,7 +488,7 @@ void uh_client_notify_state(struct client *cl) { struct ustream *s = cl->us; - if (!s->write_error) { + if (!s->write_error && cl->state != CLIENT_STATE_CLEANUP) { if (cl->state == CLIENT_STATE_DATA) return; @@ -413,14 +501,14 @@ void uh_client_notify_state(struct client *cl) 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); @@ -428,7 +516,7 @@ static void client_ustream_write_cb(struct ustream *s, int bytes) 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); } @@ -484,14 +572,13 @@ bool uh_accept_client(int fd, bool tls) 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; }