source: mainline/uspace/app/websrv/websrv.c@ ce04ea44

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

Rename read() to vfs_read() and write() to vfs_write()

  • 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 <stdbool.h>
37#include <errno.h>
38#include <assert.h>
39#include <stdio.h>
40#include <sys/types.h>
41#include <stdlib.h>
42#include <task.h>
43
44#include <vfs/vfs.h>
45
46#include <inet/addr.h>
47#include <inet/endpoint.h>
48#include <inet/tcp.h>
49
50#include <arg_parse.h>
51#include <macros.h>
52#include <str.h>
53#include <str_error.h>
54
55#define NAME "websrv"
56
57#define DEFAULT_PORT 8080
58
59#define WEB_ROOT "/data/web"
60
61/** Buffer for receiving the request. */
62#define BUFFER_SIZE 1024
63
64static void websrv_new_conn(tcp_listener_t *, tcp_conn_t *);
65
66static tcp_listen_cb_t listen_cb = {
67 .new_conn = websrv_new_conn
68};
69
70static tcp_cb_t conn_cb = {
71 .connected = NULL
72};
73
74static uint16_t port = DEFAULT_PORT;
75
76static char rbuf[BUFFER_SIZE];
77static size_t rbuf_out;
78static size_t rbuf_in;
79
80static char lbuf[BUFFER_SIZE + 1];
81static size_t lbuf_used;
82
83static char fbuf[BUFFER_SIZE];
84
85static bool verbose = false;
86
87/** Responses to send to client. */
88
89static const char *msg_ok =
90 "HTTP/1.0 200 OK\r\n"
91 "\r\n";
92
93static const char *msg_bad_request =
94 "HTTP/1.0 400 Bad Request\r\n"
95 "\r\n"
96 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
97 "<html><head>\r\n"
98 "<title>400 Bad Request</title>\r\n"
99 "</head>\r\n"
100 "<body>\r\n"
101 "<h1>Bad Request</h1>\r\n"
102 "<p>The requested URL has bad syntax.</p>\r\n"
103 "</body>\r\n"
104 "</html>\r\n";
105
106static const char *msg_not_found =
107 "HTTP/1.0 404 Not Found\r\n"
108 "\r\n"
109 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
110 "<html><head>\r\n"
111 "<title>404 Not Found</title>\r\n"
112 "</head>\r\n"
113 "<body>\r\n"
114 "<h1>Not Found</h1>\r\n"
115 "<p>The requested URL was not found on this server.</p>\r\n"
116 "</body>\r\n"
117 "</html>\r\n";
118
119static const char *msg_not_implemented =
120 "HTTP/1.0 501 Not Implemented\r\n"
121 "\r\n"
122 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
123 "<html><head>\r\n"
124 "<title>501 Not Implemented</title>\r\n"
125 "</head>\r\n"
126 "<body>\r\n"
127 "<h1>Not Implemented</h1>\r\n"
128 "<p>The requested method is not implemented on this server.</p>\r\n"
129 "</body>\r\n"
130 "</html>\r\n";
131
132/** Receive one character (with buffering) */
133static int recv_char(tcp_conn_t *conn, char *c)
134{
135 size_t nrecv;
136 int rc;
137
138 if (rbuf_out == rbuf_in) {
139 rbuf_out = 0;
140 rbuf_in = 0;
141
142 rc = tcp_conn_recv_wait(conn, rbuf, BUFFER_SIZE, &nrecv);
143 if (rc != EOK) {
144 fprintf(stderr, "tcp_conn_recv() failed (%d)\n", rc);
145 return rc;
146 }
147
148 rbuf_in = nrecv;
149 }
150
151 *c = rbuf[rbuf_out++];
152 return EOK;
153}
154
155/** Receive one line with length limit */
156static int recv_line(tcp_conn_t *conn)
157{
158 char *bp = lbuf;
159 char c = '\0';
160
161 while (bp < lbuf + BUFFER_SIZE) {
162 char prev = c;
163 int rc = recv_char(conn, &c);
164
165 if (rc != EOK)
166 return rc;
167
168 *bp++ = c;
169 if ((prev == '\r') && (c == '\n'))
170 break;
171 }
172
173 lbuf_used = bp - lbuf;
174 *bp = '\0';
175
176 if (bp == lbuf + BUFFER_SIZE)
177 return ELIMIT;
178
179 return EOK;
180}
181
182static bool uri_is_valid(char *uri)
183{
184 if (uri[0] != '/')
185 return false;
186
187 if (uri[1] == '.')
188 return false;
189
190 char *cp = uri + 1;
191
192 while (*cp != '\0') {
193 char c = *cp++;
194 if (c == '/')
195 return false;
196 }
197
198 return true;
199}
200
201static int send_response(tcp_conn_t *conn, const char *msg)
202{
203 size_t response_size = str_size(msg);
204
205 if (verbose)
206 fprintf(stderr, "Sending response\n");
207
208 int rc = tcp_conn_send(conn, (void *) msg, response_size);
209 if (rc != EOK) {
210 fprintf(stderr, "tcp_conn_send() failed\n");
211 return rc;
212 }
213
214 return EOK;
215}
216
217static int uri_get(const char *uri, tcp_conn_t *conn)
218{
219 if (str_cmp(uri, "/") == 0)
220 uri = "/index.html";
221
222 char *fname;
223 int rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
224 if (rc < 0)
225 return ENOMEM;
226
227 int fd = vfs_lookup_open(fname, WALK_REGULAR, MODE_READ);
228 if (fd < 0) {
229 rc = send_response(conn, msg_not_found);
230 free(fname);
231 return rc;
232 }
233
234 free(fname);
235
236 rc = send_response(conn, msg_ok);
237 if (rc != EOK)
238 return rc;
239
240 aoff64_t pos = 0;
241 while (true) {
242 ssize_t nr = vfs_read(fd, &pos, fbuf, BUFFER_SIZE);
243 if (nr == 0)
244 break;
245
246 if (nr < 0) {
247 vfs_put(fd);
248 return EIO;
249 }
250
251 rc = tcp_conn_send(conn, fbuf, nr);
252 if (rc != EOK) {
253 fprintf(stderr, "tcp_conn_send() failed\n");
254 vfs_put(fd);
255 return rc;
256 }
257 }
258
259 vfs_put(fd);
260
261 return EOK;
262}
263
264static int req_process(tcp_conn_t *conn)
265{
266 int rc = recv_line(conn);
267 if (rc != EOK) {
268 fprintf(stderr, "recv_line() failed\n");
269 return rc;
270 }
271
272 if (verbose)
273 fprintf(stderr, "Request: %s", lbuf);
274
275 if (str_lcmp(lbuf, "GET ", 4) != 0) {
276 rc = send_response(conn, msg_not_implemented);
277 return rc;
278 }
279
280 char *uri = lbuf + 4;
281 char *end_uri = str_chr(uri, ' ');
282 if (end_uri == NULL) {
283 end_uri = lbuf + lbuf_used - 2;
284 assert(*end_uri == '\r');
285 }
286
287 *end_uri = '\0';
288 if (verbose)
289 fprintf(stderr, "Requested URI: %s\n", uri);
290
291 if (!uri_is_valid(uri)) {
292 rc = send_response(conn, msg_bad_request);
293 return rc;
294 }
295
296 return uri_get(uri, conn);
297}
298
299static void usage(void)
300{
301 printf("Skeletal server\n"
302 "\n"
303 "Usage: " NAME " [options]\n"
304 "\n"
305 "Where options are:\n"
306 "-p port_number | --port=port_number\n"
307 "\tListening port (default " STRING(DEFAULT_PORT) ").\n"
308 "\n"
309 "-h | --help\n"
310 "\tShow this application help.\n"
311 "-v | --verbose\n"
312 "\tVerbose mode\n");
313}
314
315static int parse_option(int argc, char *argv[], int *index)
316{
317 int value;
318 int rc;
319
320 switch (argv[*index][1]) {
321 case 'h':
322 usage();
323 exit(0);
324 break;
325 case 'p':
326 rc = arg_parse_int(argc, argv, index, &value, 0);
327 if (rc != EOK)
328 return rc;
329
330 port = (uint16_t) value;
331 break;
332 case 'v':
333 verbose = true;
334 break;
335 /* Long options with double dash */
336 case '-':
337 if (str_lcmp(argv[*index] + 2, "help", 5) == 0) {
338 usage();
339 exit(0);
340 } else if (str_lcmp(argv[*index] + 2, "port=", 5) == 0) {
341 rc = arg_parse_int(argc, argv, index, &value, 7);
342 if (rc != EOK)
343 return rc;
344
345 port = (uint16_t) value;
346 } else if (str_cmp(argv[*index] +2, "verbose") == 0) {
347 verbose = true;
348 } else {
349 usage();
350 return EINVAL;
351 }
352 break;
353 default:
354 usage();
355 return EINVAL;
356 }
357
358 return EOK;
359}
360
361static void websrv_new_conn(tcp_listener_t *lst, tcp_conn_t *conn)
362{
363 int rc;
364
365 if (verbose)
366 fprintf(stderr, "New connection, waiting for request\n");
367
368 rbuf_out = 0;
369 rbuf_in = 0;
370
371 rc = req_process(conn);
372 if (rc != EOK) {
373 fprintf(stderr, "Error processing request (%s)\n",
374 str_error(rc));
375 return;
376 }
377
378 rc = tcp_conn_send_fin(conn);
379 if (rc != EOK) {
380 fprintf(stderr, "Error sending FIN.\n");
381 return;
382 }
383}
384
385int main(int argc, char *argv[])
386{
387 inet_ep_t ep;
388 tcp_listener_t *lst;
389 tcp_t *tcp;
390 int rc;
391
392 /* Parse command line arguments */
393 for (int i = 1; i < argc; i++) {
394 if (argv[i][0] == '-') {
395 rc = parse_option(argc, argv, &i);
396 if (rc != EOK)
397 return rc;
398 } else {
399 usage();
400 return EINVAL;
401 }
402 }
403
404 printf("%s: HelenOS web server\n", NAME);
405
406 if (verbose)
407 fprintf(stderr, "Creating listener\n");
408
409 inet_ep_init(&ep);
410 ep.port = port;
411
412 rc = tcp_create(&tcp);
413 if (rc != EOK) {
414 fprintf(stderr, "Error initializing TCP.\n");
415 return 1;
416 }
417
418 rc = tcp_listener_create(tcp, &ep, &listen_cb, NULL, &conn_cb, NULL,
419 &lst);
420 if (rc != EOK) {
421 fprintf(stderr, "Error creating listener.\n");
422 return 2;
423 }
424
425 fprintf(stderr, "%s: Listening for connections at port %" PRIu16 "\n",
426 NAME, port);
427
428 task_retval(0);
429 async_manager();
430
431 /* Not reached */
432 return 0;
433}
434
435/** @}
436 */
Note: See TracBrowser for help on using the repository browser.