Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • TabularUnified uspace/app/websrv/websrv.c

    rf4a2d624 r3e67ab1  
    11/*
    2  * Copyright (c) 2010 Jiri Svoboda
     2 * Copyright (c) 2012 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3131 */
    3232/**
    33  * @file (Less-than-skeleton) web server.
     33 * @file Skeletal web server.
    3434 */
    3535
     36#include <bool.h>
     37#include <errno.h>
    3638#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>
    3744
    3845#include <net/in.h>
     
    4047#include <net/socket.h>
    4148
     49#include <arg_parse.h>
     50#include <macros.h>
    4251#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"
    4560
    4661/** 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
     64static uint16_t port = DEFAULT_PORT;
     65
     66static char rbuf[BUFFER_SIZE];
     67static size_t rbuf_out;
     68static size_t rbuf_in;
     69
     70static char lbuf[BUFFER_SIZE + 1];
     71static size_t lbuf_used;
     72
     73static char fbuf[BUFFER_SIZE];
     74
     75static bool verbose = false;
     76
     77/** Responses to send to client. */
     78
     79static const char *msg_ok =
    5280    "HTTP/1.0 200 OK\r\n"
     81    "\r\n";
     82
     83static const char *msg_bad_request =
     84    "HTTP/1.0 400 Bad Request\r\n"
    5385    "\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
     96static 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
     109static 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) */
     123static 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 */
     143static 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
     169static 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
     188static 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
     204static 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
     250static 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
     285static 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
     301static 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}
    55346
    56347int main(int argc, char *argv[])
    57348{
     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       
    58361        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       
    68363        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);
    72368        if (rc != EOK) {
    73                 printf("Error parsing network address.\n");
     369                fprintf(stderr, "Error parsing network address (%s)\n",
     370                    str_error(rc));
    74371                return 1;
    75372        }
    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);
    80380        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       
    85386        rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
    86387        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);
    92394        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
    100404        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,
    103408                    &raddr_len);
    104 
     409               
    105410                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               
    129428                rc = closesocket(conn_sd);
    130429                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 */
    139441        return 0;
    140442}
Note: See TracChangeset for help on using the changeset viewer.