nuclear@12: /* tinyweb - tiny web server library and daemon nuclear@12: * Author: John Tsiombikas nuclear@12: * nuclear@12: * This program is placed in the public domain. Feel free to use it any nuclear@12: * way you like. Mentions and retaining this attribution header will be nuclear@12: * appreciated, but not required. nuclear@12: */ nuclear@3: #include nuclear@3: #include nuclear@3: #include nuclear@5: #include nuclear@3: #include nuclear@3: #include nuclear@1: #include "http.h" nuclear@7: #include "logger.h" nuclear@1: nuclear@3: nuclear@7: static const char *http_method_str[] = { nuclear@3: "", nuclear@3: "OPTIONS", nuclear@3: "GET", nuclear@3: "HEAD", nuclear@3: "POST", nuclear@3: "PUT", nuclear@3: "DELETE", nuclear@3: "TRACE", nuclear@3: "CONNECT", nuclear@3: 0 nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: /* HTTP 1xx message strings */ nuclear@7: static const char *http_msg1xx[] = { nuclear@3: "Continue", /* 100 */ nuclear@3: "Switching Protocols" /* 101 */ nuclear@3: }; nuclear@3: nuclear@3: /* HTTP 2xx message strings */ nuclear@7: static const char *http_msg2xx[] = { nuclear@3: "OK", /* 200 */ nuclear@3: "Created", /* 201 */ nuclear@3: "Accepted", /* 202 */ nuclear@3: "Non-Authoritative Information", /* 203 */ nuclear@3: "No Content", /* 204 */ nuclear@3: "Reset Content", /* 205 */ nuclear@3: "Partial Content" /* 206 */ nuclear@3: }; nuclear@3: nuclear@3: /* HTTP 3xx message strings */ nuclear@7: static const char *http_msg3xx[] = { nuclear@3: "Multiple Choices", /* 300 */ nuclear@3: "Moved Permanently", /* 301 */ nuclear@3: "Found", /* 302 */ nuclear@3: "See Other", /* 303 */ nuclear@3: "Not Modified", /* 304 */ nuclear@3: "Use Proxy", /* 305 */ nuclear@3: "", /* 306 is undefined? */ nuclear@3: "Temporary Redirect" /* 307 */ nuclear@3: }; nuclear@3: nuclear@3: /* HTTP 4xx error strings */ nuclear@7: static const char *http_msg4xx[] = { nuclear@3: "Bad Request", /* 400 */ nuclear@3: "Unauthorized", /* 401 */ nuclear@3: "What the Fuck?", /* 402 */ nuclear@3: "Forbidden", /* 403 */ nuclear@3: "Not Found", /* 404 */ nuclear@3: "Method Not Allowed", /* 405 */ nuclear@3: "Not Acceptable", /* 406 */ nuclear@3: "Proxy Authentication Required", /* 407 */ nuclear@3: "Request Time-out", /* 408 */ nuclear@3: "Conflict", /* 409 */ nuclear@3: "Gone", /* 410 */ nuclear@3: "Length Required", /* 411 */ nuclear@3: "Precondition Failed", /* 412 */ nuclear@3: "Request Entity Too Large", /* 413 */ nuclear@3: "Request-URI Too Large", /* 414 */ nuclear@3: "Unsupported Media Type", /* 415 */ nuclear@3: "Request range not satisfiable", /* 416 */ nuclear@3: "Expectation Failed" /* 417 */ nuclear@3: }; nuclear@3: nuclear@3: /* HTTP 5xx error strings */ nuclear@7: static const char *http_msg5xx[] = { nuclear@3: "Internal Server Error", /* 500 */ nuclear@3: "Not Implemented", /* 501 */ nuclear@3: "Bad Gateway", /* 502 */ nuclear@3: "Service Unavailable", /* 503 */ nuclear@3: "Gateway Time-out", /* 504 */ nuclear@3: "HTTP Version not supported" /* 505 */ nuclear@3: }; nuclear@3: nuclear@3: nuclear@3: static enum http_method parse_method(const char *s); nuclear@3: nuclear@3: nuclear@7: int http_parse_request(struct http_req_header *hdr, const char *buf, int bufsz) nuclear@2: { nuclear@2: int i, nlines = 0; nuclear@2: char *rqline = 0; nuclear@3: char *method, *uri, *version, *ptr; nuclear@3: const char *startln, *endln; nuclear@3: nuclear@3: memset(hdr, 0, sizeof *hdr); nuclear@2: nuclear@2: for(i=1; i 4 && buf[i - 2] == '\n' && buf[i - 3] == '\r') { nuclear@3: hdr->body_offset = i + 1; nuclear@3: break; nuclear@3: } nuclear@2: } nuclear@2: } nuclear@2: nuclear@3: if(!rqline) { nuclear@3: return HTTP_HDR_PARTIAL; nuclear@3: } nuclear@2: nuclear@3: ptr = rqline; nuclear@3: while(*ptr && isspace(*ptr)) ++ptr; nuclear@3: method = ptr; nuclear@2: nuclear@3: /* parse the request line */ nuclear@3: while(*ptr && !isspace(*ptr)) ++ptr; nuclear@3: while(*ptr && isspace(*ptr)) *ptr++ = 0; nuclear@3: nuclear@3: uri = ptr; nuclear@3: while(*ptr && !isspace(*ptr)) ++ptr; nuclear@3: while(*ptr && isspace(*ptr)) *ptr++ = 0; nuclear@3: nuclear@3: version = ptr; nuclear@3: while(*ptr && !isspace(*ptr)) ++ptr; nuclear@3: while(*ptr && isspace(*ptr)) *ptr++ = 0; nuclear@3: nuclear@3: hdr->method = parse_method(method); nuclear@3: hdr->uri = strdup(uri); nuclear@3: if(sscanf(version, "HTTP/%d.%d", &hdr->ver_major, &hdr->ver_minor) != 2) { nuclear@3: fprintf(stderr, "warning: failed to parse HTTP version \"%s\"\n", version); nuclear@3: hdr->ver_major = 1; nuclear@3: hdr->ver_minor = 1; nuclear@3: } nuclear@3: nuclear@3: if(!(hdr->hdrfields = malloc(nlines * sizeof *hdr->hdrfields))) { nuclear@3: perror("failed to allocate memory for the header fields"); nuclear@3: return HTTP_HDR_NOMEM; nuclear@3: } nuclear@3: hdr->num_hdrfields = 0; nuclear@3: nuclear@3: startln = buf; nuclear@3: endln = buf; nuclear@4: for(i=1; ibody_offset - 2; i++) { nuclear@3: if(buf[i] == '\n' && buf[i - 1] == '\r') { nuclear@3: int linesz; nuclear@4: nuclear@3: endln = buf + i - 1; nuclear@4: linesz = endln - startln; nuclear@3: nuclear@3: if(startln > buf) { /* skip first line */ nuclear@3: int idx = hdr->num_hdrfields++; nuclear@3: hdr->hdrfields[idx] = malloc(linesz + 1); nuclear@3: memcpy(hdr->hdrfields[idx], startln, linesz); nuclear@3: hdr->hdrfields[idx][linesz] = 0; nuclear@3: } nuclear@3: startln = endln = buf + i + 1; nuclear@3: } nuclear@3: } nuclear@3: nuclear@3: return HTTP_HDR_OK; nuclear@3: } nuclear@3: nuclear@7: void http_log_request(struct http_req_header *hdr) nuclear@3: { nuclear@3: int i; nuclear@3: nuclear@7: logmsg("HTTP request header\n"); nuclear@7: logmsg(" method: %s\n", http_method_str[hdr->method]); nuclear@7: logmsg(" uri: %s\n", hdr->uri); nuclear@7: logmsg(" version: %d.%d\n", hdr->ver_major, hdr->ver_minor); nuclear@7: logmsg(" fields (%d):\n", hdr->num_hdrfields); nuclear@3: nuclear@3: for(i=0; inum_hdrfields; i++) { nuclear@7: logmsg(" %s\n", hdr->hdrfields[i]); nuclear@3: } nuclear@7: logmsg("\n"); nuclear@3: } nuclear@3: nuclear@7: void http_destroy_request(struct http_req_header *hdr) nuclear@3: { nuclear@3: int i; nuclear@3: nuclear@3: if(hdr->hdrfields) { nuclear@3: for(i=0; inum_hdrfields; i++) { nuclear@3: free(hdr->hdrfields[i]); nuclear@3: } nuclear@3: free(hdr->hdrfields); nuclear@3: } nuclear@3: free(hdr->uri); nuclear@2: } nuclear@2: nuclear@5: int http_init_resp(struct http_resp_header *resp) nuclear@5: { nuclear@5: memset(resp, 0, sizeof *resp); nuclear@5: resp->status = 200; nuclear@5: resp->ver_major = resp->ver_minor = 1; nuclear@5: resp->fields = 0; nuclear@5: resp->num_fields = 0; nuclear@5: nuclear@5: return 0; nuclear@5: } nuclear@5: nuclear@5: int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...) nuclear@5: { nuclear@5: int sz; nuclear@5: va_list ap; nuclear@7: char *field, **newarr, tmp; nuclear@5: nuclear@5: va_start(ap, fmt); nuclear@5: sz = vsnprintf(&tmp, 0, fmt, ap); nuclear@5: va_end(ap); nuclear@5: nuclear@5: if(sz <= 0) sz = 1023; nuclear@5: if(!(field = malloc(sz + 1))) { nuclear@5: return -1; nuclear@5: } nuclear@5: va_start(ap, fmt); nuclear@5: vsnprintf(field, sz + 1, fmt, ap); nuclear@5: va_end(ap); nuclear@5: nuclear@5: if(!(newarr = realloc(resp->fields, (resp->num_fields + 1) * sizeof *resp->fields))) { nuclear@5: free(field); nuclear@5: return -1; nuclear@5: } nuclear@7: resp->fields = newarr; nuclear@7: nuclear@7: resp->fields[resp->num_fields++] = field; nuclear@5: return 0; nuclear@5: } nuclear@5: nuclear@5: void http_destroy_resp(struct http_resp_header *resp) nuclear@5: { nuclear@5: int i; nuclear@5: if(resp->fields) { nuclear@5: for(i=0; inum_fields; i++) { nuclear@5: free(resp->fields[i]); nuclear@5: } nuclear@5: free(resp->fields); nuclear@5: resp->fields = 0; nuclear@5: } nuclear@5: resp->num_fields = 0; nuclear@5: } nuclear@5: nuclear@5: int http_serialize_resp(struct http_resp_header *resp, char *buf) nuclear@5: { nuclear@5: int i, stsize, size, *fsize; nuclear@5: char *ptr, tmp; nuclear@5: nuclear@5: stsize = snprintf(&tmp, 0, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor, nuclear@5: resp->status, http_strmsg(resp->status)); nuclear@5: nuclear@5: fsize = alloca(resp->num_fields * sizeof *fsize); nuclear@5: nuclear@5: size = stsize; nuclear@5: for(i=0; inum_fields; i++) { nuclear@5: int len = strlen(resp->fields[i]) + 2; nuclear@5: fsize[i] = len; nuclear@5: size += len; nuclear@5: } nuclear@5: size += 2; /* CRLF and null */ nuclear@5: nuclear@5: if(buf) { nuclear@5: sprintf(buf, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor, nuclear@5: resp->status, http_strmsg(resp->status)); nuclear@5: nuclear@5: ptr = buf + stsize; nuclear@5: for(i=0; inum_fields; i++) { nuclear@5: sprintf(ptr, "%s\r\n", resp->fields[i]); nuclear@5: ptr += fsize[i]; nuclear@5: } nuclear@5: *ptr++ = '\r'; nuclear@5: *ptr++ = '\n'; nuclear@5: *ptr++ = 0; nuclear@5: } nuclear@5: nuclear@5: return size; nuclear@5: } nuclear@5: nuclear@1: const char *http_strmsg(int code) nuclear@1: { nuclear@1: static const char **msgxxx[] = { nuclear@1: 0, http_msg1xx, http_msg2xx, http_msg3xx, http_msg4xx, http_msg5xx nuclear@1: }; nuclear@1: static int msgcount[] = { nuclear@1: 0, nuclear@1: sizeof http_msg1xx / sizeof *http_msg1xx, nuclear@1: sizeof http_msg2xx / sizeof *http_msg2xx, nuclear@1: sizeof http_msg3xx / sizeof *http_msg3xx, nuclear@1: sizeof http_msg4xx / sizeof *http_msg4xx, nuclear@1: sizeof http_msg5xx / sizeof *http_msg5xx nuclear@1: }; nuclear@1: nuclear@1: int type = code / 100; nuclear@1: int idx = code % 100; nuclear@1: nuclear@1: if(type < 1 || type >= sizeof msgxxx / sizeof *msgxxx) { nuclear@1: return "Invalid HTTP Status"; nuclear@1: } nuclear@1: nuclear@1: if(idx < 0 || idx >= msgcount[type]) { nuclear@1: return "Unknown HTTP Status"; nuclear@1: } nuclear@1: nuclear@1: return msgxxx[type][idx]; nuclear@1: } nuclear@3: nuclear@3: static enum http_method parse_method(const char *s) nuclear@3: { nuclear@3: int i; nuclear@3: for(i=0; http_method_str[i]; i++) { nuclear@3: if(strcmp(s, http_method_str[i]) == 0) { nuclear@3: return (enum http_method)i; nuclear@3: } nuclear@3: } nuclear@3: return HTTP_UNKNOWN; nuclear@3: }