source: mainline/uspace/app/websrv/websrv.c@ 3e67ab1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3e67ab1 was 3e67ab1, checked in by Jiri Svoboda <jiri@…>, 13 years ago

Based on popular vote, move websrv back into /app.

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