Ignore:
File:
1 edited

Legend:

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

    r3e67ab1 rf4a2d624  
    11/*
    2  * Copyright (c) 2012 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>
    43 #include <task.h>
    4437
    4538#include <net/in.h>
     
    4740#include <net/socket.h>
    4841
    49 #include <arg_parse.h>
    50 #include <macros.h>
    5142#include <str.h>
    52 #include <str_error.h>
    5343
    54 #define NAME  "websrv"
    55 
    56 #define DEFAULT_PORT  8080
    57 #define BACKLOG_SIZE  3
    58 
    59 #define WEB_ROOT  "/data/web"
     44#define PORT_NUMBER 8080
    6045
    6146/** Buffer for receiving the request. */
    62 #define BUFFER_SIZE  1024
     47#define BUFFER_SIZE 1024
     48static char buf[BUFFER_SIZE];
    6349
    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 =
     50/** Response to send to client. */
     51static const char *response_msg =
    8052    "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"
    8553    "\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 }
     54    "<h1>Hello from HelenOS!</h1>\r\n";
    34655
    34756int main(int argc, char *argv[])
    34857{
    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        
    36158        struct sockaddr_in addr;
    362        
     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
    36368        addr.sin_family = AF_INET;
    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);
     69        addr.sin_port = htons(PORT_NUMBER);
     70
     71        rc = inet_pton(AF_INET, "127.0.0.1", (void *) &addr.sin_addr.s_addr);
    36872        if (rc != EOK) {
    369                 fprintf(stderr, "Error parsing network address (%s)\n",
    370                     str_error(rc));
     73                printf("Error parsing network address.\n");
    37174                return 1;
    37275        }
    373        
    374         printf("%s: HelenOS web server\n", NAME);
    37576
    376         if (verbose)
    377                 fprintf(stderr, "Creating socket\n");
    378        
    379         int listen_sd = socket(PF_INET, SOCK_STREAM, 0);
     77        printf("Creating socket.\n");
     78
     79        listen_sd = socket(PF_INET, SOCK_STREAM, 0);
    38080        if (listen_sd < 0) {
    381                 fprintf(stderr, "Error creating listening socket (%s)\n",
    382                     str_error(listen_sd));
    383                 return 2;
     81                printf("Error creating listening socket.\n");
     82                return 1;
    38483        }
    385        
     84
    38685        rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
    38786        if (rc != EOK) {
    388                 fprintf(stderr, "Error binding socket (%s)\n",
    389                     str_error(rc));
    390                 return 3;
     87                printf("Error binding socket.\n");
     88                return 1;
    39189        }
    392        
    393         rc = listen(listen_sd, BACKLOG_SIZE);
     90
     91        rc = listen(listen_sd, 1);
    39492        if (rc != EOK) {
    395                 fprintf(stderr, "listen() failed (%s)\n", str_error(rc));
    396                 return 4;
     93                printf("Error calling listen() (%d).\n", rc);
     94                return 1;
    39795        }
    398        
    399         fprintf(stderr, "%s: Listening for connections at port %" PRIu16 "\n",
    400             NAME, port);
    40196
    402         task_retval(0);
     97        response_size = str_size(response_msg);
    40398
     99        printf("Listening for connections at port number %u.\n", PORT_NUMBER);
    404100        while (true) {
    405                 struct sockaddr_in raddr;
    406                 socklen_t raddr_len = sizeof(raddr);
    407                 int conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
     101                raddr_len = sizeof(raddr);
     102                conn_sd = accept(listen_sd, (struct sockaddr *) &raddr,
    408103                    &raddr_len);
    409                
     104
    410105                if (conn_sd < 0) {
    411                         fprintf(stderr, "accept() failed (%s)\n", str_error(rc));
    412                         continue;
     106                        printf("accept() failed.\n");
     107                        return 1;
    413108                }
    414                
    415                 if (verbose) {
    416                         fprintf(stderr, "Connection accepted (sd=%d), "
    417                             "waiting for request\n", conn_sd);
     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;
    418119                }
    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                
     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
    428129                rc = closesocket(conn_sd);
    429130                if (rc != EOK) {
    430                         fprintf(stderr, "Error closing connection socket (%s)\n",
    431                             str_error(rc));
    432                         closesocket(listen_sd);
    433                         return 5;
     131                        printf("Error closing connection socket: %d\n", rc);
     132                        return 1;
    434133                }
    435                
    436                 if (verbose)
    437                         fprintf(stderr, "Connection closed\n");
     134
     135                printf("Closed connection.\n");
    438136        }
    439        
    440         /* Not reached */
     137
     138        /* Not reached. */
    441139        return 0;
    442140}
Note: See TracChangeset for help on using the changeset viewer.