source: mainline/uspace/app/websrv/websrv.c@ 94f6df7

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

We can now use .html extension.

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