source: mainline/uspace/app/websrv/websrv.c@ 75baf6e

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

Standards-compliant boolean type.

  • 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
[3e6a98c5]36#include <stdbool.h>
[4a4cc150]37#include <errno.h>
[d4d74dc]38#include <assert.h>
[f4a2d624]39#include <stdio.h>
[4a4cc150]40#include <sys/types.h>
41#include <sys/stat.h>
[9c3bba0]42#include <stdlib.h>
[4a4cc150]43#include <fcntl.h>
[26b6789]44#include <task.h>
[f4a2d624]45
46#include <net/in.h>
47#include <net/inet.h>
48#include <net/socket.h>
49
[0a549cc]50#include <arg_parse.h>
51#include <macros.h>
[f4a2d624]52#include <str.h>
[0a549cc]53#include <str_error.h>
[f4a2d624]54
[0a549cc]55#define NAME "websrv"
[f4a2d624]56
[0a549cc]57#define DEFAULT_PORT 8080
58#define BACKLOG_SIZE 3
59
60#define WEB_ROOT "/data/web"
[4a4cc150]61
[f4a2d624]62/** Buffer for receiving the request. */
[0a549cc]63#define BUFFER_SIZE 1024
64
65static uint16_t port = DEFAULT_PORT;
66
[4a4cc150]67static char rbuf[BUFFER_SIZE];
[0a549cc]68static size_t rbuf_out;
69static size_t rbuf_in;
[4a4cc150]70
71static char lbuf[BUFFER_SIZE + 1];
72static size_t lbuf_used;
73
74static char fbuf[BUFFER_SIZE];
[f4a2d624]75
[26b6789]76static bool verbose = false;
77
[0a549cc]78/** Responses to send to client. */
79
80static const char *msg_ok =
[f4a2d624]81 "HTTP/1.0 200 OK\r\n"
[4a4cc150]82 "\r\n";
83
[0a549cc]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
[4a4cc150]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;
[0a549cc]129
130 ssize_t rc = recv(fd, rbuf, BUFFER_SIZE, 0);
[4a4cc150]131 if (rc <= 0) {
[0a549cc]132 fprintf(stderr, "recv() failed (%zd)\n", rc);
[4a4cc150]133 return rc;
134 }
[0a549cc]135
[4a4cc150]136 rbuf_in = rc;
137 }
[0a549cc]138
[4a4cc150]139 *c = rbuf[rbuf_out++];
140 return EOK;
141}
142
[0a549cc]143/** Receive one line with length limit */
[4a4cc150]144static int recv_line(int fd)
145{
[0a549cc]146 char *bp = lbuf;
147 char c = '\0';
148
[4a4cc150]149 while (bp < lbuf + BUFFER_SIZE) {
[0a549cc]150 char prev = c;
151 int rc = recv_char(fd, &c);
152
[4a4cc150]153 if (rc != EOK)
154 return rc;
[0a549cc]155
[4a4cc150]156 *bp++ = c;
[0a549cc]157 if ((prev == '\r') && (c == '\n'))
[4a4cc150]158 break;
159 }
[0a549cc]160
[4a4cc150]161 lbuf_used = bp - lbuf;
162 *bp = '\0';
[0a549cc]163
[4a4cc150]164 if (bp == lbuf + BUFFER_SIZE)
165 return ELIMIT;
[0a549cc]166
[4a4cc150]167 return EOK;
168}
169
170static bool uri_is_valid(char *uri)
171{
172 if (uri[0] != '/')
173 return false;
[0a549cc]174
[4a4cc150]175 if (uri[1] == '.')
176 return false;
[0a549cc]177
178 char *cp = uri + 1;
179
[4a4cc150]180 while (*cp != '\0') {
[0a549cc]181 char c = *cp++;
[4a4cc150]182 if (c == '/')
183 return false;
184 }
[0a549cc]185
[4a4cc150]186 return true;
187}
188
189static int send_response(int conn_sd, const char *msg)
190{
[0a549cc]191 size_t response_size = str_size(msg);
192
[26b6789]193 if (verbose)
194 fprintf(stderr, "Sending response\n");
195
[0a549cc]196 ssize_t rc = send(conn_sd, (void *) msg, response_size, 0);
[4a4cc150]197 if (rc < 0) {
[0a549cc]198 fprintf(stderr, "send() failed\n");
[4a4cc150]199 return rc;
200 }
[0a549cc]201
[4a4cc150]202 return EOK;
203}
204
205static int uri_get(const char *uri, int conn_sd)
206{
207 if (str_cmp(uri, "/") == 0)
[6bb169b5]208 uri = "/index.html";
[0a549cc]209
210 char *fname;
211 int rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
[4a4cc150]212 if (rc < 0)
213 return ENOMEM;
[0a549cc]214
215 int fd = open(fname, O_RDONLY);
[4a4cc150]216 if (fd < 0) {
[0a549cc]217 rc = send_response(conn_sd, msg_not_found);
[9c3bba0]218 free(fname);
[0a549cc]219 return rc;
[4a4cc150]220 }
[0a549cc]221
[9c3bba0]222 free(fname);
[0a549cc]223
224 rc = send_response(conn_sd, msg_ok);
[4a4cc150]225 if (rc != EOK)
226 return rc;
[0a549cc]227
[4a4cc150]228 while (true) {
[0a549cc]229 ssize_t nr = read(fd, fbuf, BUFFER_SIZE);
[4a4cc150]230 if (nr == 0)
231 break;
[0a549cc]232
[4a4cc150]233 if (nr < 0) {
234 close(fd);
235 return EIO;
236 }
[0a549cc]237
[4a4cc150]238 rc = send(conn_sd, fbuf, nr, 0);
239 if (rc < 0) {
[0a549cc]240 fprintf(stderr, "send() failed\n");
[4a4cc150]241 close(fd);
242 return rc;
243 }
244 }
[0a549cc]245
[4a4cc150]246 close(fd);
[0a549cc]247
[4a4cc150]248 return EOK;
249}
250
251static int req_process(int conn_sd)
252{
[0a549cc]253 int rc = recv_line(conn_sd);
[4a4cc150]254 if (rc != EOK) {
[0a549cc]255 fprintf(stderr, "recv_line() failed\n");
[4a4cc150]256 return rc;
257 }
[0a549cc]258
[26b6789]259 if (verbose)
260 fprintf(stderr, "Request: %s", lbuf);
[0a549cc]261
[4a4cc150]262 if (str_lcmp(lbuf, "GET ", 4) != 0) {
[0a549cc]263 rc = send_response(conn_sd, msg_not_implemented);
264 return rc;
[4a4cc150]265 }
[0a549cc]266
267 char *uri = lbuf + 4;
268 char *end_uri = str_chr(uri, ' ');
[4a4cc150]269 if (end_uri == NULL) {
270 end_uri = lbuf + lbuf_used - 2;
271 assert(*end_uri == '\r');
272 }
[0a549cc]273
[4a4cc150]274 *end_uri = '\0';
[26b6789]275 if (verbose)
276 fprintf(stderr, "Requested URI: %s\n", uri);
[0a549cc]277
[4a4cc150]278 if (!uri_is_valid(uri)) {
[0a549cc]279 rc = send_response(conn_sd, msg_bad_request);
280 return rc;
[4a4cc150]281 }
[0a549cc]282
[4a4cc150]283 return uri_get(uri, conn_sd);
284}
[f4a2d624]285
[0a549cc]286static void usage(void)
[f4a2d624]287{
[0a549cc]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"
[26b6789]297 "\tShow this application help.\n"
298 "-v | --verbose\n"
299 "\tVerbose mode\n");
[0a549cc]300}
[f4a2d624]301
[0a549cc]302static int parse_option(int argc, char *argv[], int *index)
303{
304 int value;
[f4a2d624]305 int rc;
[0a549cc]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;
[26b6789]319 case 'v':
320 verbose = true;
321 break;
[0a549cc]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;
[26b6789]333 } else if (str_cmp(argv[*index] +2, "verbose") == 0) {
334 verbose = true;
[0a549cc]335 } else {
336 usage();
337 return EINVAL;
338 }
339 break;
340 default:
341 usage();
342 return EINVAL;
343 }
344
345 return EOK;
346}
[f4a2d624]347
[0a549cc]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
[f4a2d624]364 addr.sin_family = AF_INET;
[0a549cc]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);
[f4a2d624]369 if (rc != EOK) {
[0a549cc]370 fprintf(stderr, "Error parsing network address (%s)\n",
371 str_error(rc));
[f4a2d624]372 return 1;
373 }
[0a549cc]374
[26b6789]375 printf("%s: HelenOS web server\n", NAME);
376
377 if (verbose)
378 fprintf(stderr, "Creating socket\n");
[0a549cc]379
380 int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
[f4a2d624]381 if (listen_sd < 0) {
[0a549cc]382 fprintf(stderr, "Error creating listening socket (%s)\n",
383 str_error(listen_sd));
384 return 2;
[f4a2d624]385 }
[0a549cc]386
[f4a2d624]387 rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
388 if (rc != EOK) {
[0a549cc]389 fprintf(stderr, "Error binding socket (%s)\n",
390 str_error(rc));
391 return 3;
[f4a2d624]392 }
[0a549cc]393
[ae481e0]394 rc = listen(listen_sd, BACKLOG_SIZE);
[f4a2d624]395 if (rc != EOK) {
[0a549cc]396 fprintf(stderr, "listen() failed (%s)\n", str_error(rc));
397 return 4;
[f4a2d624]398 }
[0a549cc]399
[26b6789]400 fprintf(stderr, "%s: Listening for connections at port %" PRIu16 "\n",
401 NAME, port);
402
403 task_retval(0);
404
[f4a2d624]405 while (true) {
[0a549cc]406 struct sockaddr_in raddr;
407 socklen_t raddr_len = sizeof(raddr);
408 int conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
[f4a2d624]409 &raddr_len);
[0a549cc]410
[f4a2d624]411 if (conn_sd < 0) {
[0a549cc]412 fprintf(stderr, "accept() failed (%s)\n", str_error(rc));
[4a4cc150]413 continue;
[f4a2d624]414 }
[0a549cc]415
[26b6789]416 if (verbose) {
417 fprintf(stderr, "Connection accepted (sd=%d), "
418 "waiting for request\n", conn_sd);
419 }
[0a549cc]420
421 rbuf_out = 0;
422 rbuf_in = 0;
423
[4a4cc150]424 rc = req_process(conn_sd);
[0a549cc]425 if (rc != EOK)
426 fprintf(stderr, "Error processing request (%s)\n",
427 str_error(rc));
428
[f4a2d624]429 rc = closesocket(conn_sd);
430 if (rc != EOK) {
[0a549cc]431 fprintf(stderr, "Error closing connection socket (%s)\n",
432 str_error(rc));
[415578ef]433 closesocket(listen_sd);
[0a549cc]434 return 5;
[f4a2d624]435 }
[0a549cc]436
[26b6789]437 if (verbose)
438 fprintf(stderr, "Connection closed\n");
[f4a2d624]439 }
[0a549cc]440
441 /* Not reached */
[f4a2d624]442 return 0;
443}
444
445/** @}
446 */
Note: See TracBrowser for help on using the repository browser.