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

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

Free allocated file name buffer.

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