2 * uhttpd - Tiny single-threaded httpd
4 * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
5 * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include <libubox/blobmsg.h>
25 static LIST_HEAD(clients
);
28 struct config conf
= {};
30 const char * const 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",
36 const char * const http_methods
[] = {
37 [UH_HTTP_MSG_GET
] = "GET",
38 [UH_HTTP_MSG_POST
] = "POST",
39 [UH_HTTP_MSG_HEAD
] = "HEAD",
42 void uh_http_header(struct client
*cl
, int code
, const char *summary
)
44 const char *enc
= "Transfer-Encoding: chunked\r\n";
47 if (!uh_use_chunked(cl
))
50 if (cl
->request
.version
!= UH_HTTP_VER_1_1
)
51 conn
= "Connection: close";
53 conn
= "Connection: keep-alive";
55 ustream_printf(cl
->us
, "%s %03i %s\r\n%s\r\n%s",
56 http_versions
[cl
->request
.version
],
57 code
, summary
, conn
, enc
);
60 static void uh_connection_close(struct client
*cl
)
62 cl
->state
= CLIENT_STATE_CLOSE
;
64 ustream_state_change(cl
->us
);
67 static void uh_dispatch_done(struct client
*cl
)
69 if (cl
->dispatch
.free
)
70 cl
->dispatch
.free(cl
);
73 void uh_request_done(struct client
*cl
)
77 cl
->us
->notify_write
= NULL
;
78 memset(&cl
->dispatch
, 0, sizeof(cl
->dispatch
));
80 if (cl
->request
.version
!= UH_HTTP_VER_1_1
|| !conf
.http_keepalive
) {
81 uh_connection_close(cl
);
85 cl
->state
= CLIENT_STATE_INIT
;
86 uloop_timeout_set(&cl
->timeout
, conf
.http_keepalive
* 1000);
90 uh_client_error(struct client
*cl
, int code
, const char *summary
, const char *fmt
, ...)
94 uh_http_header(cl
, code
, summary
);
95 ustream_printf(cl
->us
, "Content-Type: text/html\r\n\r\n");
97 uh_chunk_printf(cl
, "<h1>%s</h1>", summary
);
101 uh_chunk_vprintf(cl
, fmt
, arg
);
108 static void uh_header_error(struct client
*cl
, int code
, const char *summary
)
110 uh_client_error(cl
, code
, summary
, NULL
);
111 uh_connection_close(cl
);
114 static void client_timeout(struct uloop_timeout
*timeout
)
116 struct client
*cl
= container_of(timeout
, struct client
, timeout
);
118 cl
->state
= CLIENT_STATE_CLOSE
;
119 uh_connection_close(cl
);
122 static int find_idx(const char * const *list
, int max
, const char *str
)
126 for (i
= 0; i
< max
; i
++)
127 if (!strcmp(list
[i
], str
))
133 static int client_parse_request(struct client
*cl
, char *data
)
135 struct http_request
*req
= &cl
->request
;
136 char *type
, *path
, *version
;
137 int h_method
, h_version
;
139 type
= strtok(data
, " ");
140 path
= strtok(NULL
, " ");
141 version
= strtok(NULL
, " ");
142 if (!type
|| !path
|| !version
)
143 return CLIENT_STATE_DONE
;
145 blobmsg_add_string(&cl
->hdr
, "URL", path
);
147 memset(&cl
->request
, 0, sizeof(cl
->request
));
148 h_method
= find_idx(http_methods
, ARRAY_SIZE(http_methods
), type
);
149 h_version
= find_idx(http_versions
, ARRAY_SIZE(http_versions
), version
);
150 if (h_method
< 0 || h_version
< 0) {
151 req
->version
= UH_HTTP_VER_1_0
;
152 return CLIENT_STATE_DONE
;
155 req
->method
= h_method
;
156 req
->version
= h_version
;
158 return CLIENT_STATE_HEADER
;
161 static bool client_init_cb(struct client
*cl
, char *buf
, int len
)
165 newline
= strstr(buf
, "\r\n");
170 blob_buf_init(&cl
->hdr
, 0);
171 cl
->state
= client_parse_request(cl
, buf
);
172 ustream_consume(cl
->us
, newline
+ 2 - buf
);
173 if (cl
->state
== CLIENT_STATE_DONE
)
174 uh_header_error(cl
, 400, "Bad Request");
179 static bool rfc1918_filter_check(struct client
*cl
)
181 if (!conf
.rfc1918_filter
)
184 if (!uh_addr_rfc1918(&cl
->peer_addr
) || uh_addr_rfc1918(&cl
->srv_addr
))
187 uh_client_error(cl
, 403, "Forbidden",
188 "Rejected request from RFC1918 IP "
189 "to public server address");
193 static void client_header_complete(struct client
*cl
)
195 if (!rfc1918_filter_check(cl
))
198 if (cl
->request
.expect_cont
)
199 ustream_printf(cl
->us
, "HTTP/1.1 100 Continue\r\n\r\n");
201 uh_handle_request(cl
);
204 static void client_parse_header(struct client
*cl
, char *data
)
206 struct http_request
*r
= &cl
->request
;
212 uloop_timeout_cancel(&cl
->timeout
);
213 cl
->state
= CLIENT_STATE_DATA
;
214 client_header_complete(cl
);
218 val
= uh_split_header(data
);
220 cl
->state
= CLIENT_STATE_DONE
;
224 for (name
= data
; *name
; name
++)
226 *name
= tolower(*name
);
228 if (!strcmp(data
, "expect")) {
229 if (!strcasecmp(val
, "100-continue"))
230 r
->expect_cont
= true;
232 uh_header_error(cl
, 412, "Precondition Failed");
235 } else if (!strcmp(data
, "content-length")) {
236 r
->content_length
= strtoul(val
, &err
, 0);
238 uh_header_error(cl
, 400, "Bad Request");
241 } else if (!strcmp(data
, "transfer-encoding")) {
242 if (!strcmp(val
, "chunked"))
243 r
->transfer_chunked
= true;
247 blobmsg_add_string(&cl
->hdr
, data
, val
);
249 cl
->state
= CLIENT_STATE_HEADER
;
252 static bool client_data_cb(struct client
*cl
, char *buf
, int len
)
254 struct dispatch
*d
= &cl
->dispatch
;
255 struct http_request
*r
= &cl
->request
;
269 cur_len
= min(r
->content_length
, len
);
272 r
->content_length
-= cur_len
;
274 d
->data_send(cl
, buf
, cur_len
);
278 if (!r
->transfer_chunked
)
281 if (r
->transfer_chunked
> 1)
284 sep
= strstr(buf
+ offset
, "\r\n");
289 cur_len
= sep
+ 2 - buf
;
291 r
->content_length
= strtoul(buf
+ offset
, &sep
, 16);
292 r
->transfer_chunked
++;
294 /* invalid chunk length */
298 /* empty chunk == eof */
299 if (!r
->content_length
)
300 r
->transfer_chunked
= false;
306 r
->content_length
= 0;
307 r
->transfer_chunked
= 0;
311 ustream_consume(cl
->us
, consumed
);
312 if (!r
->content_length
&& !r
->transfer_chunked
) {
313 if (cl
->dispatch
.data_done
)
314 cl
->dispatch
.data_done(cl
);
316 cl
->state
= CLIENT_STATE_DONE
;
321 static bool client_header_cb(struct client
*cl
, char *buf
, int len
)
326 newline
= strstr(buf
, "\r\n");
331 client_parse_header(cl
, buf
);
332 line_len
= newline
+ 2 - buf
;
333 ustream_consume(cl
->us
, line_len
);
334 if (cl
->state
== CLIENT_STATE_DATA
)
335 return client_data_cb(cl
, newline
+ 2, len
- line_len
);
340 typedef bool (*read_cb_t
)(struct client
*cl
, char *buf
, int len
);
341 static read_cb_t read_cbs
[] = {
342 [CLIENT_STATE_INIT
] = client_init_cb
,
343 [CLIENT_STATE_HEADER
] = client_header_cb
,
344 [CLIENT_STATE_DATA
] = client_data_cb
,
347 static void client_read_cb(struct client
*cl
)
349 struct ustream
*us
= cl
->us
;
354 str
= ustream_get_read_buf(us
, &len
);
358 if (cl
->state
>= array_size(read_cbs
) || !read_cbs
[cl
->state
])
361 if (!read_cbs
[cl
->state
](cl
, str
, len
)) {
362 if (len
== us
->r
.buffer_len
&&
363 cl
->state
!= CLIENT_STATE_DATA
)
364 uh_header_error(cl
, 413, "Request Entity Too Large");
370 static void client_close(struct client
*cl
)
373 uh_dispatch_done(cl
);
374 uloop_timeout_cancel(&cl
->timeout
);
375 ustream_free(&cl
->sfd
.stream
);
376 close(cl
->sfd
.fd
.fd
);
378 blob_buf_free(&cl
->hdr
);
381 uh_unblock_listeners();
384 static void client_ustream_read_cb(struct ustream
*s
, int bytes
)
386 struct client
*cl
= container_of(s
, struct client
, sfd
);
391 static void client_ustream_write_cb(struct ustream
*s
, int bytes
)
393 struct client
*cl
= container_of(s
, struct client
, sfd
);
395 if (cl
->dispatch
.write_cb
)
396 cl
->dispatch
.write_cb(cl
);
399 static void client_notify_state(struct ustream
*s
)
401 struct client
*cl
= container_of(s
, struct client
, sfd
);
403 if (!s
->write_error
) {
404 if (cl
->state
== CLIENT_STATE_DATA
)
407 if (!s
->eof
|| s
->w
.data_bytes
)
411 return client_close(cl
);
414 static void set_addr(struct uh_addr
*addr
, void *src
)
416 struct sockaddr_in
*sin
= src
;
417 struct sockaddr_in6
*sin6
= src
;
419 addr
->family
= sin
->sin_family
;
420 if (addr
->family
== AF_INET
) {
421 addr
->port
= ntohs(sin
->sin_port
);
422 memcpy(&addr
->in
, &sin
->sin_addr
, sizeof(addr
->in
));
424 addr
->port
= ntohs(sin6
->sin6_port
);
425 memcpy(&addr
->in6
, &sin6
->sin6_addr
, sizeof(addr
->in6
));
429 void uh_accept_client(int fd
)
431 static struct client
*next_client
;
435 static int client_id
= 0;
436 struct sockaddr_in6 addr
;
439 next_client
= calloc(1, sizeof(*next_client
));
444 sfd
= accept(fd
, (struct sockaddr
*) &addr
, &sl
);
448 set_addr(&cl
->peer_addr
, &addr
);
450 getsockname(fd
, (struct sockaddr
*) &addr
, &sl
);
451 set_addr(&cl
->srv_addr
, &addr
);
452 cl
->us
= &cl
->sfd
.stream
;
453 cl
->us
->string_data
= true;
454 cl
->us
->notify_read
= client_ustream_read_cb
;
455 cl
->us
->notify_write
= client_ustream_write_cb
;
456 cl
->us
->notify_state
= client_notify_state
;
457 ustream_fd_init(&cl
->sfd
, sfd
);
459 cl
->timeout
.cb
= client_timeout
;
460 uloop_timeout_set(&cl
->timeout
, conf
.network_timeout
* 1000);
462 list_add_tail(&cl
->list
, &clients
);
466 cl
->id
= client_id
++;
469 void uh_close_fds(void)
474 uh_close_listen_fds();
475 list_for_each_entry(cl
, &clients
, list
) {
476 close(cl
->sfd
.fd
.fd
);
477 if (cl
->dispatch
.close_fds
)
478 cl
->dispatch
.close_fds(cl
);