Ignore:
File:
1 edited

Legend:

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

    r6bb169b5 rf4a2d624  
    11/*
    2  * Copyright (c) 2011 Jiri Svoboda
     2 * Copyright (c) 2010 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3131 */
    3232/**
    33  * @file Skeletal web server.
     33 * @file (Less-than-skeleton) web server.
    3434 */
    3535
    36 #include <bool.h>
    37 #include <errno.h>
    3836#include <stdio.h>
    39 #include <sys/types.h>
    40 #include <sys/stat.h>
    41 #include <stdlib.h>
    42 #include <fcntl.h>
    4337
    4438#include <net/in.h>
     
    4640#include <net/socket.h>
    4741
    48 #include <arg_parse.h>
    49 #include <macros.h>
    5042#include <str.h>
    51 #include <str_error.h>
    5243
    53 #define NAME  "websrv"
    54 
    55 #define DEFAULT_PORT  8080
    56 #define BACKLOG_SIZE  3
    57 
    58 #define WEB_ROOT  "/data/web"
     44#define PORT_NUMBER 8080
    5945
    6046/** Buffer for receiving the request. */
    61 #define BUFFER_SIZE  1024
     47#define BUFFER_SIZE 1024
     48static char buf[BUFFER_SIZE];
    6249
    63 static uint16_t port = DEFAULT_PORT;
    64 
    65 static char rbuf[BUFFER_SIZE];
    66 static size_t rbuf_out;
    67 static size_t rbuf_in;
    68 
    69 static char lbuf[BUFFER_SIZE + 1];
    70 static size_t lbuf_used;
    71 
    72 static char fbuf[BUFFER_SIZE];
    73 
    74 /** Responses to send to client. */
    75 
    76 static const char *msg_ok =
     50/** Response to send to client. */
     51static const char *response_msg =
    7752    "HTTP/1.0 200 OK\r\n"
    78     "\r\n";
    79 
    80 static const char *msg_bad_request =
    81     "HTTP/1.0 400 Bad Request\r\n"
    8253    "\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 
    93 static 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 
    106 static 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) */
    120 static 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 */
    140 static 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 
    166 static 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 
    185 static 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 
    199 static 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 
    245 static 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 
    278 static 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 
    292 static 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 }
     54    "<h1>Hello from HelenOS!</h1>\r\n";
    33255
    33356int main(int argc, char *argv[])
    33457{
    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        
    34758        struct sockaddr_in addr;
    348        
     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
    34968        addr.sin_family = AF_INET;
    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);
     69        addr.sin_port = htons(PORT_NUMBER);
     70
     71        rc = inet_pton(AF_INET, "127.0.0.1", (void *) &addr.sin_addr.s_addr);
    35472        if (rc != EOK) {
    355                 fprintf(stderr, "Error parsing network address (%s)\n",
    356                     str_error(rc));
     73                printf("Error parsing network address.\n");
    35774                return 1;
    35875        }
    359        
    360         fprintf(stderr, "Creating socket\n");
    361        
    362         int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
     76
     77        printf("Creating socket.\n");
     78
     79        listen_sd = socket(PF_INET, SOCK_STREAM, 0);
    36380        if (listen_sd < 0) {
    364                 fprintf(stderr, "Error creating listening socket (%s)\n",
    365                     str_error(listen_sd));
    366                 return 2;
     81                printf("Error creating listening socket.\n");
     82                return 1;
    36783        }
    368        
     84
    36985        rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
    37086        if (rc != EOK) {
    371                 fprintf(stderr, "Error binding socket (%s)\n",
    372                     str_error(rc));
    373                 return 3;
     87                printf("Error binding socket.\n");
     88                return 1;
    37489        }
    375        
    376         rc = listen(listen_sd, BACKLOG_SIZE);
     90
     91        rc = listen(listen_sd, 1);
    37792        if (rc != EOK) {
    378                 fprintf(stderr, "listen() failed (%s)\n", str_error(rc));
    379                 return 4;
     93                printf("Error calling listen() (%d).\n", rc);
     94                return 1;
    38095        }
    381        
    382         fprintf(stderr, "Listening for connections at port %" PRIu16 "\n",
    383             port);
     96
     97        response_size = str_size(response_msg);
     98
     99        printf("Listening for connections at port number %u.\n", PORT_NUMBER);
    384100        while (true) {
    385                 struct sockaddr_in raddr;
    386                 socklen_t raddr_len = sizeof(raddr);
    387                 int conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
     101                raddr_len = sizeof(raddr);
     102                conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
    388103                    &raddr_len);
    389                
     104
    390105                if (conn_sd < 0) {
    391                         fprintf(stderr, "accept() failed (%s)\n", str_error(rc));
    392                         continue;
     106                        printf("accept() failed.\n");
     107                        return 1;
    393108                }
    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                
     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
    406129                rc = closesocket(conn_sd);
    407130                if (rc != EOK) {
    408                         fprintf(stderr, "Error closing connection socket (%s)\n",
    409                             str_error(rc));
    410                         closesocket(listen_sd);
    411                         return 5;
     131                        printf("Error closing connection socket: %d\n", rc);
     132                        return 1;
    412133                }
    413                
    414                 fprintf(stderr, "Connection closed\n");
     134
     135                printf("Closed connection.\n");
    415136        }
    416        
    417         /* Not reached */
     137
     138        /* Not reached. */
    418139        return 0;
    419140}
Note: See TracChangeset for help on using the changeset viewer.