limit default max_requests to 3
[project/uhttpd.git] / client.c
1 /*
2  * uhttpd - Tiny single-threaded httpd
3  *
4  *   Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
5  *   Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
6  *
7  *  Licensed under the Apache License, Version 2.0 (the "License");
8  *  you may not use this file except in compliance with the License.
9  *  You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  *  Unless required by applicable law or agreed to in writing, software
14  *  distributed under the License is distributed on an "AS IS" BASIS,
15  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  *  See the License for the specific language governing permissions and
17  *  limitations under the License.
18  */
19
20 #include <libubox/blobmsg.h>
21 #include <ctype.h>
22
23 #include "uhttpd.h"
24
25 static LIST_HEAD(clients);
26
27 int n_clients = 0;
28 struct config conf = {};
29
30 static const char *http_versions[] = {
31         [UH_HTTP_VER_0_9] = "HTTP/0.9",
32         [UH_HTTP_VER_1_0] = "HTTP/1.0",
33         [UH_HTTP_VER_1_1] = "HTTP/1.1",
34 };
35
36 void uh_http_header(struct client *cl, int code, const char *summary)
37 {
38         const char *enc = "Transfer-Encoding: chunked\r\n";
39         const char *conn;
40
41         if (!uh_use_chunked(cl))
42                 enc = "";
43
44         if (cl->request.version != UH_HTTP_VER_1_1)
45                 conn = "Connection: close";
46         else
47                 conn = "Connection: keep-alive";
48
49         ustream_printf(cl->us, "%s %03i %s\r\n%s\r\n%s",
50                 http_versions[cl->request.version],
51                 code, summary, conn, enc);
52 }
53
54 static void uh_client_error_header(struct client *cl, int code, const char *summary)
55 {
56         uh_http_header(cl, code, summary);
57         ustream_printf(cl->us, "Content-Type: text/plain\r\n\r\n");
58 }
59
60 static void uh_connection_close(struct client *cl)
61 {
62         cl->state = CLIENT_STATE_DONE;
63         cl->us->eof = true;
64         ustream_state_change(cl->us);
65 }
66
67 static void uh_dispatch_done(struct client *cl)
68 {
69         if (cl->dispatch_free)
70                 cl->dispatch_free(cl);
71         cl->dispatch_free = NULL;
72 }
73
74 void uh_request_done(struct client *cl)
75 {
76         uh_chunk_eof(cl);
77         uh_dispatch_done(cl);
78         cl->us->notify_write = NULL;
79         memset(&cl->data, 0, sizeof(cl->data));
80
81         if (cl->request.version != UH_HTTP_VER_1_1 || !conf.http_keepalive) {
82                 uh_connection_close(cl);
83                 return;
84         }
85
86         cl->state = CLIENT_STATE_INIT;
87         uloop_timeout_set(&cl->timeout, conf.http_keepalive * 1000);
88 }
89
90 void __printf(4, 5)
91 uh_client_error(struct client *cl, int code, const char *summary, const char *fmt, ...)
92 {
93         va_list arg;
94
95         uh_client_error_header(cl, code, summary);
96
97         va_start(arg, fmt);
98         uh_chunk_vprintf(cl, fmt, arg);
99         va_end(arg);
100
101         uh_request_done(cl);
102 }
103
104 static void uh_header_error(struct client *cl, int code, const char *summary)
105 {
106         uh_client_error(cl, code, summary, "%s", summary);
107         uh_connection_close(cl);
108 }
109
110 static void client_timeout(struct uloop_timeout *timeout)
111 {
112         struct client *cl = container_of(timeout, struct client, timeout);
113
114         cl->state = CLIENT_STATE_CLOSE;
115         uh_connection_close(cl);
116 }
117
118 static int client_parse_request(struct client *cl, char *data)
119 {
120         struct http_request *req = &cl->request;
121         char *type, *path, *version;
122         int i;
123
124         type = strtok(data, " ");
125         path = strtok(NULL, " ");
126         version = strtok(NULL, " ");
127         if (!type || !path || !version)
128                 return CLIENT_STATE_DONE;
129
130         req->url = path;
131         if (!strcmp(type, "GET"))
132                 req->method = UH_HTTP_MSG_GET;
133         else if (!strcmp(type, "POST"))
134                 req->method = UH_HTTP_MSG_POST;
135         else if (!strcmp(type, "HEAD"))
136                 req->method = UH_HTTP_MSG_HEAD;
137         else
138                 return CLIENT_STATE_DONE;
139
140         cl->request.version = -1;
141         i = array_size(http_versions);
142         while (i--) {
143                 if (!strcmp(version, http_versions[i])) {
144                         cl->request.version = i;
145                         break;
146                 }
147         }
148         if (cl->request.version < 0)
149                 return CLIENT_STATE_DONE;
150
151         return CLIENT_STATE_HEADER;
152 }
153
154 static bool client_init_cb(struct client *cl, char *buf, int len)
155 {
156         char *newline;
157
158         newline = strstr(buf, "\r\n");
159         if (!newline)
160                 return false;
161
162         *newline = 0;
163         blob_buf_init(&cl->hdr, 0);
164         blobmsg_add_string(&cl->hdr, "REQUEST", buf);
165         ustream_consume(cl->us, newline + 2 - buf);
166         cl->state = client_parse_request(cl, (char *) blobmsg_data(blob_data(cl->hdr.head)));
167         if (cl->state == CLIENT_STATE_DONE)
168                 uh_header_error(cl, 400, "Bad Request");
169
170         return true;
171 }
172
173 static void client_header_complete(struct client *cl)
174 {
175         uh_handle_file_request(cl);
176 }
177
178 static int client_parse_header(struct client *cl, char *data)
179 {
180         char *name;
181         char *val;
182
183         if (!*data) {
184                 uloop_timeout_cancel(&cl->timeout);
185                 client_header_complete(cl);
186                 return CLIENT_STATE_DATA;
187         }
188
189         val = strchr(data, ':');
190         if (!val)
191                 return CLIENT_STATE_DONE;
192
193         *val = 0;
194         val++;
195
196         while (isspace(*val))
197                 val++;
198
199         for (name = data; *name; name++)
200                 if (isupper(*name))
201                         *name = tolower(*name);
202
203         blobmsg_add_string(&cl->hdr, data, val);
204
205         return CLIENT_STATE_HEADER;
206 }
207
208 static bool client_data_cb(struct client *cl, char *buf, int len)
209 {
210         return false;
211 }
212
213 static bool client_header_cb(struct client *cl, char *buf, int len)
214 {
215         char *newline;
216         int line_len;
217
218         newline = strstr(buf, "\r\n");
219         if (!newline)
220                 return false;
221
222         *newline = 0;
223         cl->state = client_parse_header(cl, buf);
224         line_len = newline + 2 - buf;
225         ustream_consume(cl->us, line_len);
226         if (cl->state == CLIENT_STATE_DATA)
227                 client_data_cb(cl, newline + 2, len - line_len);
228
229         return true;
230 }
231
232 typedef bool (*read_cb_t)(struct client *cl, char *buf, int len);
233 static read_cb_t read_cbs[] = {
234         [CLIENT_STATE_INIT] = client_init_cb,
235         [CLIENT_STATE_HEADER] = client_header_cb,
236         [CLIENT_STATE_DATA] = client_data_cb,
237 };
238
239 static void client_read_cb(struct client *cl)
240 {
241         struct ustream *us = cl->us;
242         char *str;
243         int len;
244
245         do {
246                 str = ustream_get_read_buf(us, &len);
247                 if (!str)
248                         break;
249
250                 if (cl->state >= array_size(read_cbs) || !read_cbs[cl->state])
251                         break;
252
253                 if (!read_cbs[cl->state](cl, str, len)) {
254                         if (len == us->r.buffer_len)
255                                 uh_header_error(cl, 413, "Request Entity Too Large");
256                         break;
257                 }
258         } while(1);
259 }
260
261 static void client_close(struct client *cl)
262 {
263         uh_dispatch_done(cl);
264         uloop_timeout_cancel(&cl->timeout);
265         ustream_free(&cl->sfd.stream);
266         close(cl->sfd.fd.fd);
267         list_del(&cl->list);
268         free(cl);
269
270         uh_unblock_listeners();
271 }
272
273 static void client_ustream_read_cb(struct ustream *s, int bytes)
274 {
275         struct client *cl = container_of(s, struct client, sfd);
276
277         client_read_cb(cl);
278 }
279
280 static void client_ustream_write_cb(struct ustream *s, int bytes)
281 {
282         struct client *cl = container_of(s, struct client, sfd);
283
284         if (cl->dispatch_write_cb)
285                 cl->dispatch_write_cb(cl);
286 }
287
288 static void client_notify_state(struct ustream *s)
289 {
290         struct client *cl = container_of(s, struct client, sfd);
291
292         if (cl->state == CLIENT_STATE_CLOSE ||
293                 (s->eof && !s->w.data_bytes) || s->write_error)
294                 return client_close(cl);
295 }
296
297 void uh_accept_client(int fd)
298 {
299         static struct client *next_client;
300         struct client *cl;
301         unsigned int sl;
302         int sfd;
303         static int client_id = 0;
304
305         if (!next_client)
306                 next_client = calloc(1, sizeof(*next_client));
307
308         cl = next_client;
309
310         sl = sizeof(cl->peeraddr);
311         sfd = accept(fd, (struct sockaddr *) &cl->peeraddr, &sl);
312         if (sfd < 0)
313                 return;
314
315         sl = sizeof(cl->servaddr);
316         getsockname(fd, (struct sockaddr *) &cl->servaddr, &sl);
317         cl->us = &cl->sfd.stream;
318         cl->us->string_data = true;
319         cl->us->notify_read = client_ustream_read_cb;
320         cl->us->notify_write = client_ustream_write_cb;
321         cl->us->notify_state = client_notify_state;
322         ustream_fd_init(&cl->sfd, sfd);
323
324         cl->timeout.cb = client_timeout;
325         uloop_timeout_set(&cl->timeout, conf.network_timeout * 1000);
326
327         list_add_tail(&cl->list, &clients);
328
329         next_client = NULL;
330         n_clients++;
331         cl->id = client_id++;
332 }