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 cxmutstr 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(CxBuffer *out, pool_handle_t *pool, Request *rq) { 237 cxBufferWrite("HTTP/1.1 ", 1, 9, out); 238 239 char status_code_str[8]; 240 int sc_len = snprintf(status_code_str, 8, "%d ", rq->status_num); 241 cxBufferWrite(status_code_str, 1, sc_len, out); 242 243 char *scmsg = pblock_findkeyval(pb_key_status, rq->srvhdrs); 244 if(scmsg == NULL) { 245 scmsg = "OK"; 246 } 247 cxBufferWrite(scmsg, 1, strlen(scmsg), out); 248 249 cxBufferWrite("\r\n", 1, 2, out); 250 } 251 252 void add_http_response_header(CxBuffer *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 cxBufferPut(out, name[0]-32); 279 if(name_len > 1) { 280 cxBufferWrite(name+1, 1, name_len-1, out); 281 } 282 } else { 283 // first char is already uppercase so just write the name 284 cxBufferWrite(name, 1, name_len, out); 285 } 286 287 cxBufferWrite(": ", 1, 2, out); 288 cxBufferWrite(value, 1, strlen(value), out); 289 cxBufferWrite("\r\n", 1, 2, out); 290 291 p = p->next; 292 } 293 } 294 } 295 296 struct HttpResponseWriter { 297 Session *sn; 298 Request *rq; 299 CxBuffer buf; 300 }; 301 302 HttpResponseWriter *http_create_response(Session *sn, Request *rq) { 303 HttpResponseWriter *writer = pool_malloc(sn->pool, sizeof(HttpResponseWriter)); 304 if(!writer) { 305 return NULL; 306 } 307 if(cxBufferInit(&writer->buf, NULL, 512, pool_allocator(sn->pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) { 308 pool_free(sn->pool, writer); 309 return NULL; 310 } 311 writer->sn = sn; 312 writer->rq = rq; 313 314 if(rq->status_num == -1) { 315 protocol_status(sn, rq, 200, "OK"); 316 } 317 318 // add the http status line to the output buffer 319 add_http_status_line(&writer->buf, sn->pool, rq); 320 321 // add date header 322 struct tm mtms; 323 struct tm *mtm = system_gmtime(&rq->req_start, &mtms); 324 char date[HTTP_DATE_LEN + 1]; 325 strftime(date, HTTP_DATE_LEN, HTTP_DATE_FMT, mtm); 326 cxBufferWrite("Date: ", 1, 6, &writer->buf); 327 cxBufferWrite(date, 1, strlen(date), &writer->buf); 328 cxBufferWrite("\r\n", 1, 2, &writer->buf); 329 330 // add server header 331 cxBufferWrite("Server: webserver\r\n", 1, 19, &writer->buf); 332 333 // check content length ans transfer encoding 334 char *ctlen = pblock_findkeyval(pb_key_content_length, rq->srvhdrs); 335 char *enc = pblock_findkeyval(pb_key_transfer_encoding, rq->srvhdrs); 336 if(ctlen && enc) { 337 pblock_removekey(pb_key_transfer_encoding, rq->srvhdrs); 338 } 339 if(!ctlen) { 340 // set transfer-encoding header 341 if(!enc) { 342 pblock_kvinsert( 343 pb_key_transfer_encoding, 344 "chunked", 345 7, 346 rq->srvhdrs); 347 } else if(strcmp(enc, "chunked")) { 348 pblock_removekey(pb_key_transfer_encoding, rq->srvhdrs); 349 pblock_kvinsert( 350 pb_key_transfer_encoding, 351 "chunked", 352 7, 353 rq->srvhdrs); 354 } 355 356 // set stream property 357 httpstream_enable_chunked_write(sn->csd); 358 rq->rq_attr.chunked = 1; 359 } 360 361 // add header from rq->srvhdrs 362 add_http_response_header(&writer->buf, rq); 363 364 // add connection header 365 if(rq->rq_attr.keep_alive) { 366 cxBufferWrite("Connection: keep-alive\r\n", 1, 24, &writer->buf); 367 pblock_kvinsert(pb_key_connection, "keep-alive", 10, rq->srvhdrs); 368 } else { 369 cxBufferWrite("Connection: close\r\n", 1, 19, &writer->buf); 370 pblock_kvinsert(pb_key_connection, "close", 5, rq->srvhdrs); 371 } 372 373 // response header end 374 cxBufferWrite("\r\n", 1, 2,& writer->buf); 375 376 // reset pos (required for http_start_response_async) 377 writer->buf.pos = 0; 378 379 return writer; 380 } 381 382 int http_send_response(HttpResponseWriter *writer) { 383 Connection *conn = ((NSAPISession*)writer->sn)->connection; 384 CxBuffer *buf = &writer->buf; 385 386 // flush buffer to the socket 387 int ret = 0; 388 while(buf->pos < buf->size) { 389 int w = conn->write(conn, buf->space + buf->pos, buf->size - buf->pos); 390 if(w <= 0) { 391 if(conn->ssl) { 392 if(conn->ssl_error == SSL_ERROR_WANT_WRITE) { 393 return 1; 394 } 395 } else { 396 if(errno == EWOULDBLOCK) { 397 return 1; 398 } 399 } 400 ret = -1; 401 break; 402 } 403 buf->pos += w; 404 } 405 406 if(ret == 0) { 407 writer->rq->senthdrs = 1; 408 } 409 410 cxBufferDestroy(buf); 411 pool_free(writer->sn->pool, writer); 412 413 return ret; 414 } 415 416 417 int http_start_response(Session *sn, Request *rq) { 418 HttpResponseWriter *writer = http_create_response(sn, rq); 419 if(!writer) { 420 return 1; 421 } 422 423 return http_send_response(writer); 424 } 425 426 int http_send_continue(Session *sn) { 427 NSAPISession *s = (NSAPISession*)sn; 428 cxstring msg = CX_STR("HTTP/1.1 100 Continue\r\n\r\n"); 429 int w = s->connection->write(s->connection, msg.ptr, msg.length); 430 if(w != msg.length) { 431 return 1; 432 } 433 return 0; 434 } 435 436 int request_header(char *name, char **value, Session *sn, Request *rq) { 437 const pb_key *key = pblock_key(name); 438 pb_param *pp = pblock_findkey(key, rq->headers); 439 if(pp != NULL) { 440 *value = pp->value; 441 return REQ_PROCEED; 442 } else { 443 //return REQ_ABORTED; 444 *value = NULL; 445 return REQ_NOACTION; 446 } 447 } 448 449 char *http_uri2url(const char *prefix, const char *suffix) { 450 // TODO: implement 451 return NULL; 452 } 453 454 char *http_uri2url_dynamic(const char *prefix, const char *suffix, 455 Session *sn, Request *rq) 456 { 457 // TODO: implement 458 return NULL; 459 } 460 461 void http_get_scheme_host_port( 462 Session *sn, 463 Request *rq, 464 char **scheme, 465 char **host, 466 uint16_t *port) 467 { 468 Connection *con = ((NSAPISession*)sn)->connection; 469 470 if(con->ssl) { 471 *scheme = HTTPS_SCHEME; 472 } else { 473 *scheme = HTTP_SCHEME; 474 } 475 476 NSAPIRequest *request = (NSAPIRequest*)rq; 477 478 if(request->host) { 479 *host = request->host; 480 } else { 481 *host = "localhost"; 482 } 483 484 *port = request->port; 485 486 } 487