Changeset 6843a9c in mainline for uspace/app/websrv/websrv.c


Ignore:
Timestamp:
2012-06-29T13:02:14Z (14 years ago)
Author:
Jan Vesely <jano.vesely@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/fix-logger-deadlock, topic/msim-upgrade, topic/simplify-dev-export
Children:
722912e
Parents:
ba72f2b (diff), 0bbd13e (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge mainline changes

Trivial conflicts.

File:
1 edited

Legend:

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

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