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