source: mainline/uspace/app/websrv/websrv.c@ 4a4cc150

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

Improve web server to serve actual files from /data/web.

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