source: mainline/uspace/app/websrv/websrv.c@ 4d6629f

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4d6629f was 8d2dd7f2, checked in by Jakub Jermar <jakub@…>, 8 years ago

Reduce the number of files that include <sys/types.h>

  • Property mode set to 100644
File size: 8.8 KB
Line 
1/*
2 * Copyright (c) 2012 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup websrv
30 * @{
31 */
32/**
33 * @file Skeletal web server.
34 */
35
36#include <errno.h>
37#include <assert.h>
38#include <stdbool.h>
39#include <stdio.h>
40#include <stdint.h>
41#include <stddef.h>
42#include <stdlib.h>
43#include <task.h>
44
45#include <vfs/vfs.h>
46
47#include <inet/addr.h>
48#include <inet/endpoint.h>
49#include <inet/tcp.h>
50
51#include <arg_parse.h>
52#include <macros.h>
53#include <str.h>
54#include <str_error.h>
55
56#define NAME "websrv"
57
58#define DEFAULT_PORT 8080
59
60#define WEB_ROOT "/data/web"
61
62/** Buffer for receiving the request. */
63#define BUFFER_SIZE 1024
64
65static void websrv_new_conn(tcp_listener_t *, tcp_conn_t *);
66
67static tcp_listen_cb_t listen_cb = {
68 .new_conn = websrv_new_conn
69};
70
71static tcp_cb_t conn_cb = {
72 .connected = NULL
73};
74
75static uint16_t port = DEFAULT_PORT;
76
77static char rbuf[BUFFER_SIZE];
78static size_t rbuf_out;
79static size_t rbuf_in;
80
81static char lbuf[BUFFER_SIZE + 1];
82static size_t lbuf_used;
83
84static char fbuf[BUFFER_SIZE];
85
86static bool verbose = false;
87
88/** Responses to send to client. */
89
90static const char *msg_ok =
91 "HTTP/1.0 200 OK\r\n"
92 "\r\n";
93
94static const char *msg_bad_request =
95 "HTTP/1.0 400 Bad Request\r\n"
96 "\r\n"
97 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
98 "<html><head>\r\n"
99 "<title>400 Bad Request</title>\r\n"
100 "</head>\r\n"
101 "<body>\r\n"
102 "<h1>Bad Request</h1>\r\n"
103 "<p>The requested URL has bad syntax.</p>\r\n"
104 "</body>\r\n"
105 "</html>\r\n";
106
107static const char *msg_not_found =
108 "HTTP/1.0 404 Not Found\r\n"
109 "\r\n"
110 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
111 "<html><head>\r\n"
112 "<title>404 Not Found</title>\r\n"
113 "</head>\r\n"
114 "<body>\r\n"
115 "<h1>Not Found</h1>\r\n"
116 "<p>The requested URL was not found on this server.</p>\r\n"
117 "</body>\r\n"
118 "</html>\r\n";
119
120static const char *msg_not_implemented =
121 "HTTP/1.0 501 Not Implemented\r\n"
122 "\r\n"
123 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
124 "<html><head>\r\n"
125 "<title>501 Not Implemented</title>\r\n"
126 "</head>\r\n"
127 "<body>\r\n"
128 "<h1>Not Implemented</h1>\r\n"
129 "<p>The requested method is not implemented on this server.</p>\r\n"
130 "</body>\r\n"
131 "</html>\r\n";
132
133/** Receive one character (with buffering) */
134static int recv_char(tcp_conn_t *conn, char *c)
135{
136 size_t nrecv;
137 int rc;
138
139 if (rbuf_out == rbuf_in) {
140 rbuf_out = 0;
141 rbuf_in = 0;
142
143 rc = tcp_conn_recv_wait(conn, rbuf, BUFFER_SIZE, &nrecv);
144 if (rc != EOK) {
145 fprintf(stderr, "tcp_conn_recv() failed (%d)\n", rc);
146 return rc;
147 }
148
149 rbuf_in = nrecv;
150 }
151
152 *c = rbuf[rbuf_out++];
153 return EOK;
154}
155
156/** Receive one line with length limit */
157static int recv_line(tcp_conn_t *conn)
158{
159 char *bp = lbuf;
160 char c = '\0';
161
162 while (bp < lbuf + BUFFER_SIZE) {
163 char prev = c;
164 int rc = recv_char(conn, &c);
165
166 if (rc != EOK)
167 return rc;
168
169 *bp++ = c;
170 if ((prev == '\r') && (c == '\n'))
171 break;
172 }
173
174 lbuf_used = bp - lbuf;
175 *bp = '\0';
176
177 if (bp == lbuf + BUFFER_SIZE)
178 return ELIMIT;
179
180 return EOK;
181}
182
183static bool uri_is_valid(char *uri)
184{
185 if (uri[0] != '/')
186 return false;
187
188 if (uri[1] == '.')
189 return false;
190
191 char *cp = uri + 1;
192
193 while (*cp != '\0') {
194 char c = *cp++;
195 if (c == '/')
196 return false;
197 }
198
199 return true;
200}
201
202static int send_response(tcp_conn_t *conn, const char *msg)
203{
204 size_t response_size = str_size(msg);
205
206 if (verbose)
207 fprintf(stderr, "Sending response\n");
208
209 int rc = tcp_conn_send(conn, (void *) msg, response_size);
210 if (rc != EOK) {
211 fprintf(stderr, "tcp_conn_send() failed\n");
212 return rc;
213 }
214
215 return EOK;
216}
217
218static int uri_get(const char *uri, tcp_conn_t *conn)
219{
220 if (str_cmp(uri, "/") == 0)
221 uri = "/index.html";
222
223 char *fname;
224 int rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
225 if (rc < 0)
226 return ENOMEM;
227
228 int fd = vfs_lookup_open(fname, WALK_REGULAR, MODE_READ);
229 if (fd < 0) {
230 rc = send_response(conn, msg_not_found);
231 free(fname);
232 return rc;
233 }
234
235 free(fname);
236
237 rc = send_response(conn, msg_ok);
238 if (rc != EOK)
239 return rc;
240
241 aoff64_t pos = 0;
242 while (true) {
243 ssize_t nr = vfs_read(fd, &pos, fbuf, BUFFER_SIZE);
244 if (nr == 0)
245 break;
246
247 if (nr < 0) {
248 vfs_put(fd);
249 return EIO;
250 }
251
252 rc = tcp_conn_send(conn, fbuf, nr);
253 if (rc != EOK) {
254 fprintf(stderr, "tcp_conn_send() failed\n");
255 vfs_put(fd);
256 return rc;
257 }
258 }
259
260 vfs_put(fd);
261
262 return EOK;
263}
264
265static int req_process(tcp_conn_t *conn)
266{
267 int rc = recv_line(conn);
268 if (rc != EOK) {
269 fprintf(stderr, "recv_line() failed\n");
270 return rc;
271 }
272
273 if (verbose)
274 fprintf(stderr, "Request: %s", lbuf);
275
276 if (str_lcmp(lbuf, "GET ", 4) != 0) {
277 rc = send_response(conn, msg_not_implemented);
278 return rc;
279 }
280
281 char *uri = lbuf + 4;
282 char *end_uri = str_chr(uri, ' ');
283 if (end_uri == NULL) {
284 end_uri = lbuf + lbuf_used - 2;
285 assert(*end_uri == '\r');
286 }
287
288 *end_uri = '\0';
289 if (verbose)
290 fprintf(stderr, "Requested URI: %s\n", uri);
291
292 if (!uri_is_valid(uri)) {
293 rc = send_response(conn, msg_bad_request);
294 return rc;
295 }
296
297 return uri_get(uri, conn);
298}
299
300static void usage(void)
301{
302 printf("Skeletal server\n"
303 "\n"
304 "Usage: " NAME " [options]\n"
305 "\n"
306 "Where options are:\n"
307 "-p port_number | --port=port_number\n"
308 "\tListening port (default " STRING(DEFAULT_PORT) ").\n"
309 "\n"
310 "-h | --help\n"
311 "\tShow this application help.\n"
312 "-v | --verbose\n"
313 "\tVerbose mode\n");
314}
315
316static int parse_option(int argc, char *argv[], int *index)
317{
318 int value;
319 int rc;
320
321 switch (argv[*index][1]) {
322 case 'h':
323 usage();
324 exit(0);
325 break;
326 case 'p':
327 rc = arg_parse_int(argc, argv, index, &value, 0);
328 if (rc != EOK)
329 return rc;
330
331 port = (uint16_t) value;
332 break;
333 case 'v':
334 verbose = true;
335 break;
336 /* Long options with double dash */
337 case '-':
338 if (str_lcmp(argv[*index] + 2, "help", 5) == 0) {
339 usage();
340 exit(0);
341 } else if (str_lcmp(argv[*index] + 2, "port=", 5) == 0) {
342 rc = arg_parse_int(argc, argv, index, &value, 7);
343 if (rc != EOK)
344 return rc;
345
346 port = (uint16_t) value;
347 } else if (str_cmp(argv[*index] +2, "verbose") == 0) {
348 verbose = true;
349 } else {
350 usage();
351 return EINVAL;
352 }
353 break;
354 default:
355 usage();
356 return EINVAL;
357 }
358
359 return EOK;
360}
361
362static void websrv_new_conn(tcp_listener_t *lst, tcp_conn_t *conn)
363{
364 int rc;
365
366 if (verbose)
367 fprintf(stderr, "New connection, waiting for request\n");
368
369 rbuf_out = 0;
370 rbuf_in = 0;
371
372 rc = req_process(conn);
373 if (rc != EOK) {
374 fprintf(stderr, "Error processing request (%s)\n",
375 str_error(rc));
376 return;
377 }
378
379 rc = tcp_conn_send_fin(conn);
380 if (rc != EOK) {
381 fprintf(stderr, "Error sending FIN.\n");
382 return;
383 }
384}
385
386int main(int argc, char *argv[])
387{
388 inet_ep_t ep;
389 tcp_listener_t *lst;
390 tcp_t *tcp;
391 int rc;
392
393 /* Parse command line arguments */
394 for (int i = 1; i < argc; i++) {
395 if (argv[i][0] == '-') {
396 rc = parse_option(argc, argv, &i);
397 if (rc != EOK)
398 return rc;
399 } else {
400 usage();
401 return EINVAL;
402 }
403 }
404
405 printf("%s: HelenOS web server\n", NAME);
406
407 if (verbose)
408 fprintf(stderr, "Creating listener\n");
409
410 inet_ep_init(&ep);
411 ep.port = port;
412
413 rc = tcp_create(&tcp);
414 if (rc != EOK) {
415 fprintf(stderr, "Error initializing TCP.\n");
416 return 1;
417 }
418
419 rc = tcp_listener_create(tcp, &ep, &listen_cb, NULL, &conn_cb, NULL,
420 &lst);
421 if (rc != EOK) {
422 fprintf(stderr, "Error creating listener.\n");
423 return 2;
424 }
425
426 fprintf(stderr, "%s: Listening for connections at port %" PRIu16 "\n",
427 NAME, port);
428
429 task_retval(0);
430 async_manager();
431
432 /* Not reached */
433 return 0;
434}
435
436/** @}
437 */
Note: See TracBrowser for help on using the repository browser.