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

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

Less includes in library headers

There is no need for errno.h to include fibril.h.
Similarly, tinput.h does not need list.h or async.h.

Unfortunately, many programs depended on the fact that including
errno.h would (recursively) include unistd.h and NULL would be
defined. Most of the fixes remedy this problem.

  • 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.htm";
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.