src/server/daemon/protocol.c

Sat, 11 May 2013 13:28:26 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 11 May 2013 13:28:26 +0200
changeset 63
66442f81f823
parent 48
37a512d7b8f6
child 65
14722c5f8856
permissions
-rw-r--r--

supports file system ACLs on Solaris

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2013 Olaf Wintermann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "protocol.h"

#include "../util/pblock.h"
#include "../util/pool.h"
#include "session.h"
#include "../util/io.h"

#include "../util/strbuf.h"


void protocol_status(Session *sn, Request *rq, int n, const char *m) {
    rq->status_num = n;

    const char *msg = m ? m : protocol_status_message(n);

    pb_param *pp = pblock_removekey(pb_key_status, rq->srvhdrs);
    if (pp != NULL) {
        param_free(pp);
    }

    pp = pblock_key_param_create(rq->srvhdrs, pb_key_status, msg, strlen(msg));
    pblock_kpinsert(pb_key_status, pp, rq->srvhdrs);
}


/*
 * http_status_message from Open Webserver (frame/http.cpp)
 * TODO: replace, use sstr_t
 */
NSAPI_PUBLIC const char * protocol_status_message (int code)
{
    const char *r;

    switch (code)
    {
        case PROTOCOL_CONTINUE : // 100
            r = "Continue";
            break;
        case PROTOCOL_SWITCHING: //101
            r = "Switching Protocols";
            break;
        case PROTOCOL_OK: // 200
            r = "OK";
            break;
        case PROTOCOL_CREATED: // 201
            r = "Created";
            break;
        case PROTOCOL_ACCEPTED: // 202
            r = "Accepted";
            break;
        case PROTOCOL_NONAUTHORITATIVE: // 203
            r = "Non-Authoritative Information";
            break;
        case PROTOCOL_NO_CONTENT: //204
        /* There is another define to PROTOCOL_NO_RESPONSE for 204 in nsapi.h
           The spec maps this to No Content.
           Hence cahnging this to No Content
        */
            r = "No Content";
            break;
        case PROTOCOL_RESET_CONTENT: // 205
            r = "Reset Content";
            break;
        case PROTOCOL_PARTIAL_CONTENT: // 206
            r = "Partial Content";
            break;
        case PROTOCOL_MULTI_STATUS: // 207
            r = "Multi Status";
            break;
        case PROTOCOL_MULTIPLE_CHOICES: // 300
            r = "Multiple Choices";
            break;
        case PROTOCOL_MOVED_PERMANENTLY: // 301
            r = "Moved Permanently";
            break;
        case PROTOCOL_REDIRECT:          // 302
            r = "Moved Temporarily"; /* The spec actually says "Found" */
            break;
        case PROTOCOL_SEE_OTHER:         // 303
            r = "See Other";
            break;
        case PROTOCOL_NOT_MODIFIED:      // 304
            r = "Use local copy";    /* The spec actually says "Not Modified" */
            break;
        case PROTOCOL_USE_PROXY:         // 305
            r = "Use Proxy";
            break;
        case PROTOCOL_TEMPORARY_REDIRECT: // 307
            r = "Temporary Redirect";
            break;
        case PROTOCOL_BAD_REQUEST:        // 400
            r = "Bad request";
            break;
        case PROTOCOL_UNAUTHORIZED:       // 401
            r = "Unauthorized";
            break;
        case PROTOCOL_PAYMENT_REQUIRED:   // 402
            r = "Payment Required";
            break;
        case PROTOCOL_FORBIDDEN:          // 403
            r = "Forbidden";
            break;
        case PROTOCOL_NOT_FOUND:         // 404
            r = "Not found";
            break;
        case PROTOCOL_METHOD_NOT_ALLOWED: // 405                /* HTTP/1.1 */
            r = "Method Not Allowed";
            break;
        case PROTOCOL_NOT_ACCEPTABLE: // 406                /* HTTP/1.1 */
            r = "Not Acceptable";
            break;
        case PROTOCOL_PROXY_UNAUTHORIZED: // 407
            r = "Proxy Authentication Required";
            break;
        case PROTOCOL_REQUEST_TIMEOUT:    // 408            /* HTTP/1.1 */
            r = "Request Timeout";
            break;
        case PROTOCOL_CONFLICT:           // 409
            r = "Conflict";                         /* HTTP/1.1 */
            break;
        case PROTOCOL_GONE:           // 410
            r = "Gone";                         /* HTTP/1.1 */
            break;
        case PROTOCOL_LENGTH_REQUIRED:    // 411                /* HTTP/1.1 */
            r = "Length Required";
            break;
        case PROTOCOL_PRECONDITION_FAIL:  // 412                /* HTTP/1.1 */
            r = "Precondition Failed";
            break;
        case PROTOCOL_ENTITY_TOO_LARGE:   // 413                /* HTTP/1.1 */
            r = "Request Entity Too Large";
            break;
        case PROTOCOL_URI_TOO_LARGE:      // 414                /* HTTP/1.1 */
            r = "Request-URI Too Large";
            break;
        case PROTOCOL_UNSUPPORTED_MEDIA_TYPE: // 415
            r = "Unsupported Media Type";
            break;
        case PROTOCOL_REQUESTED_RANGE_NOT_SATISFIABLE: // 416
            r = "Requested range not satisfiable";
            break;
        case PROTOCOL_EXPECTATION_FAILED:     // 417
            r = "Expectation Failed";
            break;
        case PROTOCOL_LOCKED:     // 423
            r = "Locked";
            break;
        case PROTOCOL_FAILED_DEPENDENCY:     // 424
            r = "Failed Dependency";
            break;
        case PROTOCOL_SERVER_ERROR:           // 500
            r = "Server Error";           /* The spec actually says "Internal Server Error" */
            break;
        case PROTOCOL_NOT_IMPLEMENTED:        // 501
            r = "Not Implemented";
            break;
        case PROTOCOL_BAD_GATEWAY:            // 502
            r = "Bad Gateway";
            break;
        case PROTOCOL_SERVICE_UNAVAILABLE:    // 503
            r = "Service Unavailable";
            break;
        case PROTOCOL_GATEWAY_TIMEOUT:        // 504            /* HTTP/1.1 */
            r = "Gateway Timeout";
            break;
        case PROTOCOL_VERSION_NOT_SUPPORTED:  // 505            /* HTTP/1.1 */
            r = "HTTP Version Not Supported";
            break;
        case PROTOCOL_INSUFFICIENT_STORAGE:  // 507
            r = "Insufficient Storage";
            break;
        default:
            switch (code / 100)
            {
                case 1:
                    r = "Information";
                    break;
                case 2:
                    r = "Success";
                    break;
                case 3:
                    r = "Redirect";
                    break;
                case 4:
                    r = "Client error";
                    break;
                case 5:
                    r = "Server error";
                    break;
                default:
                    r = "Unknown reason";
                    break;
            }
            break;
    }

    return r;
}


void add_http_status_line(sbuf_t *out, pool_handle_t *pool, Request *rq) {
    sbuf_write(out, "HTTP/1.1 ", 9);

    char *status_code_str = pool_malloc(pool, 8);
    int sc_len = snprintf(status_code_str, 8, "%d ", rq->status_num);
    sbuf_write(out, status_code_str, sc_len);
    
    char *scmsg = pblock_findkeyval(pb_key_status, rq->srvhdrs);
    if(scmsg == NULL) {
        scmsg = "OK";
    }
    sbuf_write(out, scmsg, strlen(scmsg));

    sbuf_write(out, "\r\n", 2);
}

void add_http_response_header(sbuf_t *out, Request *rq) {
    pblock   *h = rq->srvhdrs;
    pb_entry *p;

    for(int i=0;i<h->hsize;i++) {
        p = h->ht[i];
        while(p != NULL) {           
            /* from http.cpp */
            const pb_key *key = PARAM_KEY(p->param);
            if (key == pb_key_status || key == pb_key_server || key == pb_key_date) {
                /* Skip internal Status:, Server:, and Date: information */
                p = p->next;
                continue;
            }
            /* end http.cpp */

            char *name  = p->param->name;
            char *value = p->param->value;

            /* make first char of name uppercase */
            // TODO: don't modify the headers
            if(name[0] > 90) {
                name[0] -= 32;
            }

            sbuf_write(out, name, strlen(name));
            sbuf_write(out, ": ", 2);
            sbuf_write(out, value, strlen(value));
            sbuf_write(out, "\r\n", 2);

            p = p->next;
        }
    }
}

int http_start_response(Session *sn, Request *rq) {
    int fd = ((SystemIOStream*)sn->csd)->fd;

    if(rq->status_num == -1) {
        protocol_status(sn, rq, 200, "OK");
    }

    /* set socket blocking */
    int flags;
    flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags ^ O_NONBLOCK);

    /* output buffer */
    sbuf_t *out = sbuf_new(512);

    /* add the http status line to the output buffer */
    add_http_status_line(out, sn->pool, rq);

    /* add server header */
    sbuf_write(out, "Server: webserver\r\n", 19);

    /* add header from rq->srvhdrs */
    add_http_response_header(out, rq);
    
    /* add connection header */
    sbuf_write(out, "Connection: close\r\n", 19);

    /* response header end */
    sbuf_write(out, "\r\n", 2);

    /* flush buffer to the socket */
    write(fd, out->ptr, out->length);
    sbuf_free(out);
    
    rq->senthdrs = 1;
    
    return 0;
}

int request_header(char *name, char **value, Session *sn, Request *rq) {
    const pb_key *key = pblock_key(name);
    pb_param *pp = pblock_findkey(key, rq->headers);
    if(pp != NULL) {
        *value = pp->value;
        return REQ_PROCEED;
    } else {
        //return REQ_ABORTED;
        *value = NULL;
        return REQ_NOACTION;
    }
}

mercurial