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 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",
36 void uh_http_header(struct client
*cl
, int code
, const char *summary
)
38 const char *enc
= "Transfer-Encoding: chunked\r\n";
41 if (!uh_use_chunked(cl
))
44 if (cl
->request
.version
!= UH_HTTP_VER_1_1
)
45 conn
= "Connection: close";
47 conn
= "Connection: keep-alive";
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
);
54 static void uh_connection_close(struct client
*cl
)
56 cl
->state
= CLIENT_STATE_DONE
;
58 ustream_state_change(cl
->us
);
61 static void uh_dispatch_done(struct client
*cl
)
63 if (cl
->dispatch
.free
)
64 cl
->dispatch
.free(cl
);
67 void uh_request_done(struct client
*cl
)
71 cl
->us
->notify_write
= NULL
;
72 memset(&cl
->dispatch
, 0, sizeof(cl
->dispatch
));
74 if (cl
->request
.version
!= UH_HTTP_VER_1_1
|| !conf
.http_keepalive
) {
75 uh_connection_close(cl
);
79 cl
->state
= CLIENT_STATE_INIT
;
80 uloop_timeout_set(&cl
->timeout
, conf
.http_keepalive
* 1000);
84 uh_client_error(struct client
*cl
, int code
, const char *summary
, const char *fmt
, ...)
88 uh_http_header(cl
, code
, summary
);
89 ustream_printf(cl
->us
, "Content-Type: text/html\r\n\r\n");
91 uh_chunk_printf(cl
, "<h1>%s</h1>", summary
);
95 uh_chunk_vprintf(cl
, fmt
, arg
);
102 static void uh_header_error(struct client
*cl
, int code
, const char *summary
)
104 uh_client_error(cl
, code
, summary
, NULL
);
105 uh_connection_close(cl
);
108 static void client_timeout(struct uloop_timeout
*timeout
)
110 struct client
*cl
= container_of(timeout
, struct client
, timeout
);
112 cl
->state
= CLIENT_STATE_CLOSE
;
113 uh_connection_close(cl
);
116 static int client_parse_request(struct client
*cl
, char *data
)
118 struct http_request
*req
= &cl
->request
;
119 char *type
, *path
, *version
;
122 type
= strtok(data
, " ");
123 path
= strtok(NULL
, " ");
124 version
= strtok(NULL
, " ");
125 if (!type
|| !path
|| !version
)
126 return CLIENT_STATE_DONE
;
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
;
136 return CLIENT_STATE_DONE
;
138 cl
->request
.version
= -1;
139 i
= array_size(http_versions
);
141 if (!strcmp(version
, http_versions
[i
])) {
142 cl
->request
.version
= i
;
146 if (cl
->request
.version
< 0)
147 return CLIENT_STATE_DONE
;
149 return CLIENT_STATE_HEADER
;
152 static bool client_init_cb(struct client
*cl
, char *buf
, int len
)
156 newline
= strstr(buf
, "\r\n");
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");
171 static void client_header_complete(struct client
*cl
)
173 uh_handle_file_request(cl
);
176 static int client_parse_header(struct client
*cl
, char *data
)
182 uloop_timeout_cancel(&cl
->timeout
);
183 client_header_complete(cl
);
184 return CLIENT_STATE_DATA
;
187 val
= strchr(data
, ':');
189 return CLIENT_STATE_DONE
;
194 while (isspace(*val
))
197 for (name
= data
; *name
; name
++)
199 *name
= tolower(*name
);
201 blobmsg_add_string(&cl
->hdr
, data
, val
);
203 return CLIENT_STATE_HEADER
;
206 static bool client_data_cb(struct client
*cl
, char *buf
, int len
)
211 static bool client_header_cb(struct client
*cl
, char *buf
, int len
)
216 newline
= strstr(buf
, "\r\n");
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
);
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
,
237 static void client_read_cb(struct client
*cl
)
239 struct ustream
*us
= cl
->us
;
244 str
= ustream_get_read_buf(us
, &len
);
248 if (cl
->state
>= array_size(read_cbs
) || !read_cbs
[cl
->state
])
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");
259 static void client_close(struct client
*cl
)
261 uh_dispatch_done(cl
);
262 uloop_timeout_cancel(&cl
->timeout
);
263 ustream_free(&cl
->sfd
.stream
);
264 close(cl
->sfd
.fd
.fd
);
266 blob_buf_free(&cl
->hdr
);
269 uh_unblock_listeners();
272 static void client_ustream_read_cb(struct ustream
*s
, int bytes
)
274 struct client
*cl
= container_of(s
, struct client
, sfd
);
279 static void client_ustream_write_cb(struct ustream
*s
, int bytes
)
281 struct client
*cl
= container_of(s
, struct client
, sfd
);
283 if (cl
->dispatch
.write_cb
)
284 cl
->dispatch
.write_cb(cl
);
287 static void client_notify_state(struct ustream
*s
)
289 struct client
*cl
= container_of(s
, struct client
, sfd
);
291 if (cl
->state
== CLIENT_STATE_CLOSE
||
292 (s
->eof
&& !s
->w
.data_bytes
) || s
->write_error
)
293 return client_close(cl
);
296 void uh_accept_client(int fd
)
298 static struct client
*next_client
;
302 static int client_id
= 0;
305 next_client
= calloc(1, sizeof(*next_client
));
309 sl
= sizeof(cl
->peeraddr
);
310 sfd
= accept(fd
, (struct sockaddr
*) &cl
->peeraddr
, &sl
);
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
);
323 cl
->timeout
.cb
= client_timeout
;
324 uloop_timeout_set(&cl
->timeout
, conf
.network_timeout
* 1000);
326 list_add_tail(&cl
->list
, &clients
);
330 cl
->id
= client_id
++;
333 void uh_close_fds(void)
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
);