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

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

Implement listening backlog.

  • Property mode set to 100644
File size: 6.1 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 <str.h>
49
50#define PORT_NUMBER 8080
51#define BACKLOG_SIZE 3
52
53#define WEB_ROOT "/data/web"
54
55/** Buffer for receiving the request. */
56#define BUFFER_SIZE 1024
57static char rbuf[BUFFER_SIZE];
58static size_t rbuf_out, rbuf_in;
59
60static char lbuf[BUFFER_SIZE + 1];
61static size_t lbuf_used;
62
63static char fbuf[BUFFER_SIZE];
64
65/** Response to send to client. */
66static const char *ok_msg =
67 "HTTP/1.0 200 OK\r\n"
68 "\r\n";
69
70/** Receive one character (with buffering) */
71static int recv_char(int fd, char *c)
72{
73 ssize_t rc;
74
75 if (rbuf_out == rbuf_in) {
76 rbuf_out = 0;
77 rbuf_in = 0;
78
79 rc = recv(fd, rbuf, BUFFER_SIZE, 0);
80 if (rc <= 0) {
81 printf("recv() failed (%zd)\n", rc);
82 return rc;
83 }
84
85 rbuf_in = rc;
86 }
87
88 *c = rbuf[rbuf_out++];
89 return EOK;
90}
91
92/** Receive one line with length limit. */
93static int recv_line(int fd)
94{
95 char c, prev;
96 int rc;
97 char *bp;
98
99 bp = lbuf; c = '\0';
100 while (bp < lbuf + BUFFER_SIZE) {
101 prev = c;
102 rc = recv_char(fd, &c);
103 if (rc != EOK)
104 return rc;
105
106 *bp++ = c;
107 if (prev == '\r' && c == '\n')
108 break;
109 }
110
111 lbuf_used = bp - lbuf;
112 *bp = '\0';
113
114 if (bp == lbuf + BUFFER_SIZE)
115 return ELIMIT;
116
117 return EOK;
118}
119
120static bool uri_is_valid(char *uri)
121{
122 char *cp;
123 char c;
124
125 if (uri[0] != '/')
126 return false;
127 if (uri[1] == '.')
128 return false;
129
130 cp = uri + 1;
131 while (*cp != '\0') {
132 c = *cp++;
133 if (c == '/')
134 return false;
135 }
136
137 return true;
138}
139
140static int send_response(int conn_sd, const char *msg)
141{
142 size_t response_size;
143 ssize_t rc;
144
145 response_size = str_size(msg);
146
147 /* Send a canned response. */
148 printf("Send response...\n");
149 rc = send(conn_sd, (void *) msg, response_size, 0);
150 if (rc < 0) {
151 printf("send() failed.\n");
152 return rc;
153 }
154
155 return EOK;
156}
157
158static int uri_get(const char *uri, int conn_sd)
159{
160 int rc;
161 char *fname;
162 int fd;
163 ssize_t nr;
164
165 if (str_cmp(uri, "/") == 0)
166 uri = "/index.htm";
167
168 rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
169 if (rc < 0)
170 return ENOMEM;
171
172 fd = open(fname, O_RDONLY);
173 if (fd < 0) {
174 printf("File '%s' not found.\n", fname);
175 free(fname);
176 return ENOENT;
177 }
178
179 free(fname);
180
181 rc = send_response(conn_sd, ok_msg);
182 if (rc != EOK)
183 return rc;
184
185 while (true) {
186 nr = read(fd, fbuf, BUFFER_SIZE);
187 if (nr == 0)
188 break;
189
190 if (nr < 0) {
191 close(fd);
192 return EIO;
193 }
194
195 rc = send(conn_sd, fbuf, nr, 0);
196 if (rc < 0) {
197 printf("send() failed\n");
198 close(fd);
199 return rc;
200 }
201 }
202
203 close(fd);
204
205 return EOK;
206}
207
208static int req_process(int conn_sd)
209{
210 int rc;
211 char *uri, *end_uri;
212
213 rc = recv_line(conn_sd);
214 if (rc != EOK) {
215 printf("recv_line() failed\n");
216 return rc;
217 }
218
219 printf("%s", lbuf);
220
221 if (str_lcmp(lbuf, "GET ", 4) != 0) {
222 printf("Invalid HTTP method.\n");
223 return EINVAL;
224 }
225
226 uri = lbuf + 4;
227 end_uri = str_chr(uri, ' ');
228 if (end_uri == NULL) {
229 end_uri = lbuf + lbuf_used - 2;
230 assert(*end_uri == '\r');
231 }
232
233 *end_uri = '\0';
234 printf("Requested URI '%s'.\n", uri);
235
236 if (!uri_is_valid(uri)) {
237 printf("Invalid request URI.\n");
238 return EINVAL;
239 }
240
241 return uri_get(uri, conn_sd);
242}
243
244int main(int argc, char *argv[])
245{
246 struct sockaddr_in addr;
247 struct sockaddr_in raddr;
248
249 socklen_t raddr_len;
250
251 int listen_sd, conn_sd;
252 int rc;
253
254
255 addr.sin_family = AF_INET;
256 addr.sin_port = htons(PORT_NUMBER);
257
258 rc = inet_pton(AF_INET, "127.0.0.1", (void *) &addr.sin_addr.s_addr);
259 if (rc != EOK) {
260 printf("Error parsing network address.\n");
261 return 1;
262 }
263
264 printf("Creating socket.\n");
265
266 listen_sd = socket(PF_INET, SOCK_STREAM, 0);
267 if (listen_sd < 0) {
268 printf("Error creating listening socket.\n");
269 return 1;
270 }
271
272 rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
273 if (rc != EOK) {
274 printf("Error binding socket.\n");
275 return 1;
276 }
277
278 rc = listen(listen_sd, BACKLOG_SIZE);
279 if (rc != EOK) {
280 printf("Error calling listen() (%d).\n", rc);
281 return 1;
282 }
283
284 printf("Listening for connections at port number %u.\n", PORT_NUMBER);
285 while (true) {
286 raddr_len = sizeof(raddr);
287 conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
288 &raddr_len);
289
290 if (conn_sd < 0) {
291 printf("accept() failed.\n");
292 continue;
293 }
294
295 printf("Accepted connection, sd=%d.\n", conn_sd);
296
297 printf("Wait for client request\n");
298 rbuf_out = rbuf_in = 0;
299
300 rc = req_process(conn_sd);
301 if (rc != EOK)
302 printf("Error processing request.\n");
303
304 rc = closesocket(conn_sd);
305 if (rc != EOK) {
306 printf("Error closing connection socket: %d\n", rc);
307 closesocket(listen_sd);
308 return 1;
309 }
310
311 printf("Closed connection.\n");
312 }
313
314 /* Not reached. */
315 return 0;
316}
317
318/** @}
319 */
Note: See TracBrowser for help on using the repository browser.