Follow us on Google+ Follow us on Facebook Follow us on Twitter

Changeset mainline,1356


Ignore:
Timestamp:
2012-01-06T14:05:58Z (6 years ago)
Author:
Martin Decky <martin@…>
branch-nick:
mainline
revision id:
martin@decky.cz-20120106140558-jnob55qg3v81nrv4
Message:

improve web server

  • add proper responses for HTTP error codes 400, 404, 501
  • send debugging output to stderr
  • more compact debugging output
  • use str_error() in error messages
  • respond properly on files not found (404) and unsupported methods (400)
  • add -p (—port) command-line option to override the default listening port
  • cstyle
File:
1 edited

Legend:

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

    r1349 r1356  
    4646#include <net/socket.h>
    4747
     48#include <arg_parse.h>
     49#include <macros.h>
    4850#include <str.h>
    49 
    50 #define PORT_NUMBER 8080
    51 #define BACKLOG_SIZE 3
    52 
    53 #define WEB_ROOT "/data/web"
     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"
    5459
    5560/** Buffer for receiving the request. */
    56 #define BUFFER_SIZE 1024
     61#define BUFFER_SIZE  1024
     62
     63static uint16_t port = DEFAULT_PORT;
     64
    5765static char rbuf[BUFFER_SIZE];
    58 static size_t rbuf_out, rbuf_in;
     66static size_t rbuf_out;
     67static size_t rbuf_in;
    5968
    6069static char lbuf[BUFFER_SIZE + 1];
     
    6372static char fbuf[BUFFER_SIZE];
    6473
    65 /** Response to send to client. */
    66 static const char *ok_msg =
     74/** Responses to send to client. */
     75
     76static const char *msg_ok =
    6777    "HTTP/1.0 200 OK\r\n"
    6878    "\r\n";
    6979
     80static const char *msg_bad_request =
     81    "HTTP/1.0 400 Bad Request\r\n"
     82    "\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
    70119/** Receive one character (with buffering) */
    71120static int recv_char(int fd, char *c)
    72121{
    73         ssize_t rc;
    74 
    75122        if (rbuf_out == rbuf_in) {
    76123                rbuf_out = 0;
    77124                rbuf_in = 0;
    78 
    79                 rc = recv(fd, rbuf, BUFFER_SIZE, 0);
     125               
     126                ssize_t rc = recv(fd, rbuf, BUFFER_SIZE, 0);
    80127                if (rc <= 0) {
    81                         printf("recv() failed (%zd)\n", rc);
     128                        fprintf(stderr, "recv() failed (%zd)\n", rc);
    82129                        return rc;
    83130                }
    84 
     131               
    85132                rbuf_in = rc;
    86133        }
    87 
     134       
    88135        *c = rbuf[rbuf_out++];
    89136        return EOK;
    90137}
    91138
    92 /** Receive one line with length limit. */
     139/** Receive one line with length limit */
    93140static int recv_line(int fd)
    94141{
    95         char c, prev;
    96         int rc;
    97         char *bp;
    98 
    99         bp = lbuf; c = '\0';
     142        char *bp = lbuf;
     143        char c = '\0';
     144       
    100145        while (bp < lbuf + BUFFER_SIZE) {
    101                 prev = c;
    102                 rc = recv_char(fd, &c);
     146                char prev = c;
     147                int rc = recv_char(fd, &c);
     148               
    103149                if (rc != EOK)
    104150                        return rc;
    105 
     151               
    106152                *bp++ = c;
    107                 if (prev == '\r' && c == '\n')
     153                if ((prev == '\r') && (c == '\n'))
    108154                        break;
    109155        }
    110 
     156       
    111157        lbuf_used = bp - lbuf;
    112158        *bp = '\0';
    113 
     159       
    114160        if (bp == lbuf + BUFFER_SIZE)
    115161                return ELIMIT;
    116 
     162       
    117163        return EOK;
    118164}
     
    120166static bool uri_is_valid(char *uri)
    121167{
    122         char *cp;
    123         char c;
    124 
    125168        if (uri[0] != '/')
    126169                return false;
     170       
    127171        if (uri[1] == '.')
    128172                return false;
    129 
    130         cp = uri + 1;
     173       
     174        char *cp = uri + 1;
     175       
    131176        while (*cp != '\0') {
    132                 c = *cp++;
     177                char c = *cp++;
    133178                if (c == '/')
    134179                        return false;
    135180        }
    136 
     181       
    137182        return true;
    138183}
     
    140185static int send_response(int conn_sd, const char *msg)
    141186{
    142         size_t response_size;
    143         ssize_t rc;
    144 
    145         response_size = str_size(msg);
    146 
    147         /* Send a canned response. */
    148         printf("Send response...\n");
    149         rc = send(conn_sd, (void *) msg, response_size, 0);
     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);
    150191        if (rc < 0) {
    151                 printf("send() failed.\n");
    152                 return rc;
    153         }
    154 
     192                fprintf(stderr, "send() failed\n");
     193                return rc;
     194        }
     195       
    155196        return EOK;
    156197}
     
    158199static int uri_get(const char *uri, int conn_sd)
    159200{
    160         int rc;
    161         char *fname;
    162         int fd;
    163         ssize_t nr;
    164 
    165201        if (str_cmp(uri, "/") == 0)
    166202                uri = "/index.htm";
    167 
    168         rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
     203       
     204        char *fname;
     205        int rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
    169206        if (rc < 0)
    170207                return ENOMEM;
    171 
    172         fd = open(fname, O_RDONLY);
     208       
     209        int fd = open(fname, O_RDONLY);
    173210        if (fd < 0) {
    174                 printf("File '%s' not found.\n", fname);
     211                rc = send_response(conn_sd, msg_not_found);
    175212                free(fname);
    176                 return ENOENT;
    177         }
    178 
     213                return rc;
     214        }
     215       
    179216        free(fname);
    180 
    181         rc = send_response(conn_sd, ok_msg);
     217       
     218        rc = send_response(conn_sd, msg_ok);
    182219        if (rc != EOK)
    183220                return rc;
    184 
     221       
    185222        while (true) {
    186                 nr = read(fd, fbuf, BUFFER_SIZE);
     223                ssize_t nr = read(fd, fbuf, BUFFER_SIZE);
    187224                if (nr == 0)
    188225                        break;
    189 
     226               
    190227                if (nr < 0) {
    191228                        close(fd);
    192229                        return EIO;
    193230                }
    194 
     231               
    195232                rc = send(conn_sd, fbuf, nr, 0);
    196233                if (rc < 0) {
    197                         printf("send() failed\n");
     234                        fprintf(stderr, "send() failed\n");
    198235                        close(fd);
    199236                        return rc;
    200237                }
    201238        }
    202 
     239       
    203240        close(fd);
    204 
     241       
    205242        return EOK;
    206243}
     
    208245static int req_process(int conn_sd)
    209246{
    210         int rc;
    211         char *uri, *end_uri;
    212 
    213         rc = recv_line(conn_sd);
     247        int rc = recv_line(conn_sd);
    214248        if (rc != EOK) {
    215                 printf("recv_line() failed\n");
    216                 return rc;
    217         }
    218 
    219         printf("%s", lbuf);
    220 
     249                fprintf(stderr, "recv_line() failed\n");
     250                return rc;
     251        }
     252       
     253        fprintf(stderr, "Request: %s", lbuf);
     254       
    221255        if (str_lcmp(lbuf, "GET ", 4) != 0) {
    222                 printf("Invalid HTTP method.\n");
    223                 return EINVAL;
    224         }
    225 
    226         uri = lbuf + 4;
    227         end_uri = str_chr(uri, ' ');
     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, ' ');
    228262        if (end_uri == NULL) {
    229263                end_uri = lbuf + lbuf_used - 2;
    230264                assert(*end_uri == '\r');
    231265        }
    232 
     266       
    233267        *end_uri = '\0';
    234         printf("Requested URI '%s'.\n", uri);
    235 
     268        fprintf(stderr, "Requested URI: %s\n", uri);
     269       
    236270        if (!uri_is_valid(uri)) {
    237                 printf("Invalid request URI.\n");
     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();
    238327                return EINVAL;
    239328        }
    240 
    241         return uri_get(uri, conn_sd);
     329       
     330        return EOK;
    242331}
    243332
    244333int main(int argc, char *argv[])
    245334{
     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       
    246347        struct sockaddr_in addr;
    247         struct sockaddr_in raddr;
    248 
    249         socklen_t raddr_len;
    250 
    251         int listen_sd, conn_sd;
    252         int rc;
    253 
    254 
     348       
    255349        addr.sin_family = AF_INET;
    256         addr.sin_port = htons(PORT_NUMBER);
    257 
    258         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);
    259354        if (rc != EOK) {
    260                 printf("Error parsing network address.\n");
     355                fprintf(stderr, "Error parsing network address (%s)\n",
     356                    str_error(rc));
    261357                return 1;
    262358        }
    263 
    264         printf("Creating socket.\n");
    265 
    266         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);
    267363        if (listen_sd < 0) {
    268                 printf("Error creating listening socket.\n");
    269                 return 1;
    270         }
    271 
     364                fprintf(stderr, "Error creating listening socket (%s)\n",
     365                    str_error(listen_sd));
     366                return 2;
     367        }
     368       
    272369        rc = bind(listen_sd, (struct sockaddr *) &addr, sizeof(addr));
    273370        if (rc != EOK) {
    274                 printf("Error binding socket.\n");
    275                 return 1;
    276         }
    277 
     371                fprintf(stderr, "Error binding socket (%s)\n",
     372                    str_error(rc));
     373                return 3;
     374        }
     375       
    278376        rc = listen(listen_sd, BACKLOG_SIZE);
    279377        if (rc != EOK) {
    280                 printf("Error calling listen() (%d).\n", rc);
    281                 return 1;
    282         }
    283 
    284         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);
    285384        while (true) {
    286                 raddr_len = sizeof(raddr);
    287                 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,
    288388                    &raddr_len);
    289 
     389               
    290390                if (conn_sd < 0) {
    291                         printf("accept() failed.\n");
     391                        fprintf(stderr, "accept() failed (%s)\n", str_error(rc));
    292392                        continue;
    293393                }
    294 
    295                 printf("Accepted connection, sd=%d.\n", conn_sd);
    296 
    297                 printf("Wait for client request\n");
    298                 rbuf_out = rbuf_in = 0;
    299 
     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               
    300401                rc = req_process(conn_sd);
    301                 if (rc != EOK)
    302                         printf("Error processing request.\n");
    303 
     402                if (rc != EOK)
     403                        fprintf(stderr, "Error processing request (%s)\n",
     404                            str_error(rc));
     405               
    304406                rc = closesocket(conn_sd);
    305407                if (rc != EOK) {
    306                         printf("Error closing connection socket: %d\n", rc);
     408                        fprintf(stderr, "Error closing connection socket (%s)\n",
     409                            str_error(rc));
    307410                        closesocket(listen_sd);
    308                         return 1;
    309                 }
    310 
    311                 printf("Closed connection.\n");
    312         }
    313 
    314         /* Not reached. */
     411                        return 5;
     412                }
     413               
     414                fprintf(stderr, "Connection closed\n");
     415        }
     416       
     417        /* Not reached */
    315418        return 0;
    316419}
Note: See TracChangeset for help on using the changeset viewer.