Ignore:
File:
1 edited

Legend:

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

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