source: mainline/uspace/app/websrv/websrv.c@ 68f57e1

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

Merge mainline changes

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