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