UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2013 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "protocol.h" 30 31 #include "session.h" 32 #include "httplistener.h" 33 #include "request.h" 34 35 #include "../util/pblock.h" 36 #include "../util/pool.h" 37 #include "../util/io.h" 38 #include "../util/util.h" 39 #include "../util/strbuf.h" 40 41 #define HTTP_SCHEME "http" 42 #define HTTPS_SCHEME "https" 43 44 void protocol_status(Session *sn, Request *rq, int n, const char *m) { 45 rq->status_num = n; 46 47 const char *msg = m ? m : protocol_status_message(n); 48 49 pb_param *pp = pblock_removekey(pb_key_status, rq->srvhdrs); 50 if (pp != NULL) { 51 param_free(pp); 52 } 53 54 pp = pblock_key_param_create(rq->srvhdrs, pb_key_status, msg, strlen(msg)); 55 pblock_kpinsert(pb_key_status, pp, rq->srvhdrs); 56 } 57 58 59 /* 60 * http_status_message from Open Webserver (frame/http.cpp) 61 * TODO: replace, use sstr_t 62 */ 63 NSAPI_PUBLIC const char * protocol_status_message (int code) 64 { 65 WS_ASSERT(code > 0); 66 67 const char *r; 68 69 switch (code) 70 { 71 case PROTOCOL_CONTINUE : // 100 72 r = "Continue"; 73 break; 74 case PROTOCOL_SWITCHING: //101 75 r = "Switching Protocols"; 76 break; 77 case PROTOCOL_OK: // 200 78 r = "OK"; 79 break; 80 case PROTOCOL_CREATED: // 201 81 r = "Created"; 82 break; 83 case PROTOCOL_ACCEPTED: // 202 84 r = "Accepted"; 85 break; 86 case PROTOCOL_NONAUTHORITATIVE: // 203 87 r = "Non-Authoritative Information"; 88 break; 89 case PROTOCOL_NO_CONTENT: //204 90 /* There is another define to PROTOCOL_NO_RESPONSE for 204 in nsapi.h 91 The spec maps this to No Content. 92 Hence cahnging this to No Content 93 */ 94 r = "No Content"; 95 break; 96 case PROTOCOL_RESET_CONTENT: // 205 97 r = "Reset Content"; 98 break; 99 case PROTOCOL_PARTIAL_CONTENT: // 206 100 r = "Partial Content"; 101 break; 102 case PROTOCOL_MULTI_STATUS: // 207 103 r = "Multi Status"; 104 break; 105 case PROTOCOL_MULTIPLE_CHOICES: // 300 106 r = "Multiple Choices"; 107 break; 108 case PROTOCOL_MOVED_PERMANENTLY: // 301 109 r = "Moved Permanently"; 110 break; 111 case PROTOCOL_REDIRECT: // 302 112 r = "Moved Temporarily"; /* The spec actually says "Found" */ 113 break; 114 case PROTOCOL_SEE_OTHER: // 303 115 r = "See Other"; 116 break; 117 case PROTOCOL_NOT_MODIFIED: // 304 118 r = "Use local copy"; /* The spec actually says "Not Modified" */ 119 break; 120 case PROTOCOL_USE_PROXY: // 305 121 r = "Use Proxy"; 122 break; 123 case PROTOCOL_TEMPORARY_REDIRECT: // 307 124 r = "Temporary Redirect"; 125 break; 126 case PROTOCOL_BAD_REQUEST: // 400 127 r = "Bad request"; 128 break; 129 case PROTOCOL_UNAUTHORIZED: // 401 130 r = "Unauthorized"; 131 break; 132 case PROTOCOL_PAYMENT_REQUIRED: // 402 133 r = "Payment Required"; 134 break; 135 case PROTOCOL_FORBIDDEN: // 403 136 r = "Forbidden"; 137 break; 138 case PROTOCOL_NOT_FOUND: // 404 139 r = "Not found"; 140 break; 141 case PROTOCOL_METHOD_NOT_ALLOWED: // 405 /* HTTP/1.1 */ 142 r = "Method Not Allowed"; 143 break; 144 case PROTOCOL_NOT_ACCEPTABLE: // 406 /* HTTP/1.1 */ 145 r = "Not Acceptable"; 146 break; 147 case PROTOCOL_PROXY_UNAUTHORIZED: // 407 148 r = "Proxy Authentication Required"; 149 break; 150 case PROTOCOL_REQUEST_TIMEOUT: // 408 /* HTTP/1.1 */ 151 r = "Request Timeout"; 152 break; 153 case PROTOCOL_CONFLICT: // 409 154 r = "Conflict"; /* HTTP/1.1 */ 155 break; 156 case PROTOCOL_GONE: // 410 157 r = "Gone"; /* HTTP/1.1 */ 158 break; 159 case PROTOCOL_LENGTH_REQUIRED: // 411 /* HTTP/1.1 */ 160 r = "Length Required"; 161 break; 162 case PROTOCOL_PRECONDITION_FAIL: // 412 /* HTTP/1.1 */ 163 r = "Precondition Failed"; 164 break; 165 case PROTOCOL_ENTITY_TOO_LARGE: // 413 /* HTTP/1.1 */ 166 r = "Request Entity Too Large"; 167 break; 168 case PROTOCOL_URI_TOO_LARGE: // 414 /* HTTP/1.1 */ 169 r = "Request-URI Too Large"; 170 break; 171 case PROTOCOL_UNSUPPORTED_MEDIA_TYPE: // 415 172 r = "Unsupported Media Type"; 173 break; 174 case PROTOCOL_REQUESTED_RANGE_NOT_SATISFIABLE: // 416 175 r = "Requested range not satisfiable"; 176 break; 177 case PROTOCOL_EXPECTATION_FAILED: // 417 178 r = "Expectation Failed"; 179 break; 180 case PROTOCOL_LOCKED: // 423 181 r = "Locked"; 182 break; 183 case PROTOCOL_FAILED_DEPENDENCY: // 424 184 r = "Failed Dependency"; 185 break; 186 case PROTOCOL_SERVER_ERROR: // 500 187 r = "Server Error"; /* The spec actually says "Internal Server Error" */ 188 break; 189 case PROTOCOL_NOT_IMPLEMENTED: // 501 190 r = "Not Implemented"; 191 break; 192 case PROTOCOL_BAD_GATEWAY: // 502 193 r = "Bad Gateway"; 194 break; 195 case PROTOCOL_SERVICE_UNAVAILABLE: // 503 196 r = "Service Unavailable"; 197 break; 198 case PROTOCOL_GATEWAY_TIMEOUT: // 504 /* HTTP/1.1 */ 199 r = "Gateway Timeout"; 200 break; 201 case PROTOCOL_VERSION_NOT_SUPPORTED: // 505 /* HTTP/1.1 */ 202 r = "HTTP Version Not Supported"; 203 break; 204 case PROTOCOL_INSUFFICIENT_STORAGE: // 507 205 r = "Insufficient Storage"; 206 break; 207 default: 208 switch (code / 100) 209 { 210 case 1: 211 r = "Information"; 212 break; 213 case 2: 214 r = "Success"; 215 break; 216 case 3: 217 r = "Redirect"; 218 break; 219 case 4: 220 r = "Client error"; 221 break; 222 case 5: 223 r = "Server error"; 224 break; 225 default: 226 r = "Unknown reason"; 227 break; 228 } 229 break; 230 } 231 232 return r; 233 } 234 235 236 void add_http_status_line(sbuf_t *out, pool_handle_t *pool, Request *rq) { 237 sbuf_write(out, "HTTP/1.1 ", 9); 238 239 char *status_code_str = pool_malloc(pool, 8); 240 int sc_len = snprintf(status_code_str, 8, "%d ", rq->status_num); 241 sbuf_write(out, status_code_str, sc_len); 242 243 char *scmsg = pblock_findkeyval(pb_key_status, rq->srvhdrs); 244 if(scmsg == NULL) { 245 scmsg = "OK"; 246 } 247 sbuf_write(out, scmsg, strlen(scmsg)); 248 249 sbuf_write(out, "\r\n", 2); 250 } 251 252 void add_http_response_header(sbuf_t *out, Request *rq) { 253 pblock *h = rq->srvhdrs; 254 pb_entry *p; 255 256 for(int i=0;i<h->hsize;i++) { 257 p = h->ht[i]; 258 while(p != NULL) { 259 /* from http.cpp */ 260 const pb_key *key = PARAM_KEY(p->param); 261 if (key == pb_key_status || key == pb_key_server || key == pb_key_date) { 262 /* Skip internal Status:, Server:, and Date: information */ 263 p = p->next; 264 continue; 265 } 266 /* end http.cpp */ 267 268 char *name = p->param->name; 269 char *value = p->param->value; 270 271 // make first char of name uppercase 272 size_t name_len = strlen(name); 273 if(name[0] > 90) { 274 /* 275 * make first char uppercase and write the remaining chars 276 * unmodified to the buffer 277 */ 278 sbuf_put(out, name[0]-32); 279 if(name_len > 1) { 280 sbuf_write(out, name+1, name_len-1); 281 } 282 } else { 283 // first char is already uppercase so just write the name 284 sbuf_write(out, name, name_len); 285 } 286 287 sbuf_write(out, ": ", 2); 288 sbuf_write(out, value, strlen(value)); 289 sbuf_write(out, "\r\n", 2); 290 291 p = p->next; 292 } 293 } 294 } 295 296 int http_start_response(Session *sn, Request *rq) { 297 Connection *conn = ((NSAPISession*)sn)->connection; 298 299 if(rq->status_num == -1) { 300 protocol_status(sn, rq, 200, "OK"); 301 } 302 303 // set socket blocking 304 //int flags; 305 //flags = fcntl(fd, F_GETFL, 0); 306 //fcntl(fd, F_SETFL, flags ^ O_NONBLOCK); 307 308 // output buffer 309 sbuf_t *out = sbuf_new(512); 310 311 // add the http status line to the output buffer 312 add_http_status_line(out, sn->pool, rq); 313 314 // add date header 315 struct tm mtms; 316 struct tm *mtm = system_gmtime(&rq->req_start, &mtms); 317 char date[HTTP_DATE_LEN + 1]; 318 strftime(date, HTTP_DATE_LEN, HTTP_DATE_FMT, mtm); 319 sbuf_write(out, "Date: ", 6); 320 sbuf_write(out, date, strlen(date)); 321 sbuf_write(out, "\r\n", 2); 322 323 // add server header 324 sbuf_write(out, "Server: webserver\r\n", 19); 325 326 // check content length ans transfer encoding 327 char *ctlen = pblock_findkeyval(pb_key_content_length, rq->srvhdrs); 328 char *enc = pblock_findkeyval(pb_key_transfer_encoding, rq->srvhdrs); 329 if(ctlen && enc) { 330 pblock_removekey(pb_key_transfer_encoding, rq->srvhdrs); 331 } 332 if(!ctlen) { 333 // set transfer-encoding header 334 if(!enc) { 335 pblock_kvinsert( 336 pb_key_transfer_encoding, 337 "chunked", 338 7, 339 rq->srvhdrs); 340 } else if(strcmp(enc, "chunked")) { 341 pblock_removekey(pb_key_transfer_encoding, rq->srvhdrs); 342 pblock_kvinsert( 343 pb_key_transfer_encoding, 344 "chunked", 345 7, 346 rq->srvhdrs); 347 } 348 349 // set stream property 350 HttpStream *stream = (HttpStream*)sn->csd; 351 stream->chunked_enc = 1; 352 rq->rq_attr.chunked = 1; 353 } 354 355 // add header from rq->srvhdrs 356 add_http_response_header(out, rq); 357 358 // add connection header 359 if(rq->rq_attr.keep_alive) { 360 sbuf_write(out, "Connection: keep-alive\r\n", 24); 361 pblock_kvinsert(pb_key_connection, "keep-alive", 10, rq->srvhdrs); 362 } else { 363 sbuf_write(out, "Connection: close\r\n", 19); 364 pblock_kvinsert(pb_key_connection, "close", 5, rq->srvhdrs); 365 } 366 367 368 // response header end 369 sbuf_write(out, "\r\n", 2); 370 371 // flush buffer to the socket 372 conn->write(conn, out->ptr, out->length); 373 sbuf_free(out); 374 375 rq->senthdrs = 1; 376 377 return 0; 378 } 379 380 int request_header(char *name, char **value, Session *sn, Request *rq) { 381 const pb_key *key = pblock_key(name); 382 pb_param *pp = pblock_findkey(key, rq->headers); 383 if(pp != NULL) { 384 *value = pp->value; 385 return REQ_PROCEED; 386 } else { 387 //return REQ_ABORTED; 388 *value = NULL; 389 return REQ_NOACTION; 390 } 391 } 392 393 char *http_uri2url(const char *prefix, const char *suffix) { 394 // TODO: implement 395 return NULL; 396 } 397 398 char *http_uri2url_dynamic(const char *prefix, const char *suffix, 399 Session *sn, Request *rq) 400 { 401 // TODO: implement 402 return NULL; 403 } 404 405 void http_get_scheme_host_port( 406 Session *sn, 407 Request *rq, 408 char **scheme, 409 char **host, 410 uint16_t *port) 411 { 412 Connection *con = ((NSAPISession*)sn)->connection; 413 414 if(con->ssl) { 415 *scheme = HTTPS_SCHEME; 416 } else { 417 *scheme = HTTP_SCHEME; 418 } 419 420 NSAPIRequest *request = (NSAPIRequest*)rq; 421 422 if(request->host) { 423 *host = request->host; 424 } else { 425 *host = "localhost"; 426 } 427 428 *port = request->port; 429 430 } 431