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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b4ca0a9c was c196671, checked in by Vojtech Horky <vojtechhorky@…>, 13 years ago

Merge MSIM simulator port (#401)

Merged from lp:~vojtech-horky/helenos/msim.

Not all features work, but it is possible to boot HelenOS running in
MSIM that runs in HelenOS in Qemu
(i.e. host → Qemu → HelenOS → MSIM → HelenOS).

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