diff -r 1fdbf4170ef4 -r b8bf95b39952 src/server/daemon/protocol.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/daemon/protocol.c Sat Jan 14 13:53:44 2012 +0100 @@ -0,0 +1,315 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2011 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); + 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;ihsize;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 */ + 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; + + /* set socket blocking */ + int flags; + flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags ^ O_NONBLOCK); + + /* iovec 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: WS uap-dev\r\n", 20); + + /* add header from rq->srvhdrs */ + add_http_response_header(out, rq); + + /* 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; +} + +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; + } +}