0c978d7670f23b049d37543c265f5590541c0b21
[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_connection_close(struct client *cl)
55 {
56         cl->state = CLIENT_STATE_DONE;
57         cl->us->eof = true;
58         ustream_state_change(cl->us);
59 }
60
61 static void uh_dispatch_done(struct client *cl)
62 {
63         if (cl->dispatch.free)
64                 cl->dispatch.free(cl);
65 }
66
67 void uh_request_done(struct client *cl)
68 {
69         uh_chunk_eof(cl);
70         uh_dispatch_done(cl);
71         cl->us->notify_write = NULL;
72         memset(&cl->dispatch, 0, sizeof(cl->dispatch));
73
74         if (cl->request.version != UH_HTTP_VER_1_1 || !conf.http_keepalive) {
75                 uh_connection_close(cl);
76                 return;
77         }
78
79         cl->state = CLIENT_STATE_INIT;
80         uloop_timeout_set(&cl->timeout, conf.http_keepalive * 1000);
81 }
82
83 void __printf(4, 5)
84 uh_client_error(struct client *cl, int code, const char *summary, const char *fmt, ...)
85 {
86         va_list arg;
87
88         uh_http_header(cl, code, summary);
89         ustream_printf(cl->us, "Content-Type: text/html\r\n\r\n");
90
91         uh_chunk_printf(cl, "<h1>%s</h1>", summary);
92
93         if (fmt) {
94                 va_start(arg, fmt);
95                 uh_chunk_vprintf(cl, fmt, arg);
96                 va_end(arg);
97         }
98
99         uh_request_done(cl);
100 }
101
102 static void uh_header_error(struct client *cl, int code, const char *summary)
103 {
104         uh_client_error(cl, code, summary, NULL);
105         uh_connection_close(cl);
106 }
107
108 static void client_timeout(struct uloop_timeout *timeout)
109 {
110         struct client *cl = container_of(timeout, struct client, timeout);
111
112         cl->state = CLIENT_STATE_CLOSE;
113         uh_connection_close(cl);
114 }
115
116 static int client_parse_request(struct client *cl, char *data)
117 {
118         struct http_request *req = &cl->request;
119         char *type, *path, *version;
120         int i;
121
122         type = strtok(data, " ");
123         path = strtok(NULL, " ");
124         version = strtok(NULL, " ");
125         if (!type || !path || !version)
126                 return CLIENT_STATE_DONE;
127
128         req->url = path;
129         if (!strcmp(type, "GET"))
130                 req->method = UH_HTTP_MSG_GET;
131         else if (!strcmp(type, "POST"))
132                 req->method = UH_HTTP_MSG_POST;
133         else if (!strcmp(type, "HEAD"))
134                 req->method = UH_HTTP_MSG_HEAD;
135         else
136                 return CLIENT_STATE_DONE;
137
138         cl->request.version = -1;
139         i = array_size(http_versions);
140         while (i--) {
141                 if (!strcmp(version, http_versions[i])) {
142                         cl->request.version = i;
143                         break;
144                 }
145         }
146         if (cl->request.version < 0)
147                 return CLIENT_STATE_DONE;
148
149         return CLIENT_STATE_HEADER;
150 }
151
152 static bool client_init_cb(struct client *cl, char *buf, int len)
153 {
154         char *newline;
155
156         newline = strstr(buf, "\r\n");
157         if (!newline)
158                 return false;
159
160         *newline = 0;
161         blob_buf_init(&cl->hdr, 0);
162         blobmsg_add_string(&cl->hdr, "REQUEST", buf);
163         ustream_consume(cl->us, newline + 2 - buf);
164         cl->state = client_parse_request(cl, (char *) blobmsg_data(blob_data(cl->hdr.head)));
165         if (cl->state == CLIENT_STATE_DONE)
166                 uh_header_error(cl, 400, "Bad Request");
167
168         return true;
169 }
170
171 static void client_header_complete(struct client *cl)
172 {
173         uh_handle_file_request(cl);
174 }
175
176 static int client_parse_header(struct client *cl, char *data)
177 {
178         char *name;
179         char *val;
180
181         if (!*data) {
182                 uloop_timeout_cancel(&cl->timeout);
183                 client_header_complete(cl);
184                 return CLIENT_STATE_DATA;
185         }
186
187         val = strchr(data, ':');
188         if (!val)
189                 return CLIENT_STATE_DONE;
190
191         *val = 0;
192         val++;
193
194         while (isspace(*val))
195                 val++;
196
197         for (name = data; *name; name++)
198                 if (isupper(*name))
199                         *name = tolower(*name);
200
201         blobmsg_add_string(&cl->hdr, data, val);
202
203         return CLIENT_STATE_HEADER;
204 }
205
206 static bool client_data_cb(struct client *cl, char *buf, int len)
207 {
208         return false;
209 }
210
211 static bool client_header_cb(struct client *cl, char *buf, int len)
212 {
213         char *newline;
214         int line_len;
215
216         newline = strstr(buf, "\r\n");
217         if (!newline)
218                 return false;
219
220         *newline = 0;
221         cl->state = client_parse_header(cl, buf);
222         line_len = newline + 2 - buf;
223         ustream_consume(cl->us, line_len);
224         if (cl->state == CLIENT_STATE_DATA)
225                 client_data_cb(cl, newline + 2, len - line_len);
226
227         return true;
228 }
229
230 typedef bool (*read_cb_t)(struct client *cl, char *buf, int len);
231 static read_cb_t read_cbs[] = {
232         [CLIENT_STATE_INIT] = client_init_cb,
233         [CLIENT_STATE_HEADER] = client_header_cb,
234         [CLIENT_STATE_DATA] = client_data_cb,
235 };
236
237 static void client_read_cb(struct client *cl)
238 {
239         struct ustream *us = cl->us;
240         char *str;
241         int len;
242
243         do {
244                 str = ustream_get_read_buf(us, &len);
245                 if (!str)
246                         break;
247
248                 if (cl->state >= array_size(read_cbs) || !read_cbs[cl->state])
249                         break;
250
251                 if (!read_cbs[cl->state](cl, str, len)) {
252                         if (len == us->r.buffer_len)
253                                 uh_header_error(cl, 413, "Request Entity Too Large");
254                         break;
255                 }
256         } while(1);
257 }
258
259 static void client_close(struct client *cl)
260 {
261         uh_dispatch_done(cl);
262         uloop_timeout_cancel(&cl->timeout);
263         ustream_free(&cl->sfd.stream);
264         close(cl->sfd.fd.fd);
265         list_del(&cl->list);
266         blob_buf_free(&cl->hdr);
267         free(cl);
268
269         uh_unblock_listeners();
270 }
271
272 static void client_ustream_read_cb(struct ustream *s, int bytes)
273 {
274         struct client *cl = container_of(s, struct client, sfd);
275
276         client_read_cb(cl);
277 }
278
279 static void client_ustream_write_cb(struct ustream *s, int bytes)
280 {
281         struct client *cl = container_of(s, struct client, sfd);
282
283         if (cl->dispatch.write_cb)
284                 cl->dispatch.write_cb(cl);
285 }
286
287 static void client_notify_state(struct ustream *s)
288 {
289         struct client *cl = container_of(s, struct client, sfd);
290
291         if (cl->state == CLIENT_STATE_CLOSE ||
292                 (s->eof && !s->w.data_bytes) || s->write_error)
293                 return client_close(cl);
294 }
295
296 void uh_accept_client(int fd)
297 {
298         static struct client *next_client;
299         struct client *cl;
300         unsigned int sl;
301         int sfd;
302         static int client_id = 0;
303
304         if (!next_client)
305                 next_client = calloc(1, sizeof(*next_client));
306
307         cl = next_client;
308
309         sl = sizeof(cl->peeraddr);
310         sfd = accept(fd, (struct sockaddr *) &cl->peeraddr, &sl);
311         if (sfd < 0)
312                 return;
313
314         sl = sizeof(cl->servaddr);
315         getsockname(fd, (struct sockaddr *) &cl->servaddr, &sl);
316         cl->us = &cl->sfd.stream;
317         cl->us->string_data = true;
318         cl->us->notify_read = client_ustream_read_cb;
319         cl->us->notify_write = client_ustream_write_cb;
320         cl->us->notify_state = client_notify_state;
321         ustream_fd_init(&cl->sfd, sfd);
322
323         cl->timeout.cb = client_timeout;
324         uloop_timeout_set(&cl->timeout, conf.network_timeout * 1000);
325
326         list_add_tail(&cl->list, &clients);
327
328         next_client = NULL;
329         n_clients++;
330         cl->id = client_id++;
331 }
332
333 void uh_close_fds(void)
334 {
335         struct client *cl;
336
337         uloop_done();
338         uh_close_listen_fds();
339         list_for_each_entry(cl, &clients, list) {
340                 close(cl->sfd.fd.fd);
341                 if (cl->dispatch.close_fds)
342                         cl->dispatch.close_fds(cl);
343         }
344 }