static int http_parse_first_line(struct http_connection *hc, struct evbuffer *evb) { int ret = 0; char *line = NULL; size_t line_length = 0; line = evbuffer_readln(evb, &line_length, EVBUFFER_EOL_CRLF); if (line == NULL) { dbprintf("Could not read http first line\n"); return -1; } if (line_length > 2048) { dbprintf("Http firstline too long(len > 2048)\n"); goto failed; } ret = http_parse_request_line(hc, line); if (ret) { dbprintf("Could not parse http request line\n"); goto failed; } free(line); return 0; failed: if (line) { free(line); } return -1; }
int http_receive_request(int sockfd, struct HttpRequest **received_request) { debug("http_receive_request"); *received_request = NULL; char *method = NULL; char *resource = NULL; struct dict *headers = dict_create(); int content_length = -1; char *payload = NULL; int http_error = HTTP_SUCCESS; char *line = NULL; if ((http_error = http_read_line(sockfd, &line)) != HTTP_SUCCESS) { goto error; } if ((http_error = http_parse_request_line(line, &method, &resource)) != HTTP_SUCCESS) { goto error; } free(line); if ((http_error = http_receive_headers(sockfd, headers)) != HTTP_SUCCESS) { goto error; } const char *l = dict_get_case(headers, "Content-Length"); if (l != 0) { content_length = atoi(l); } if (content_length == -1 || content_length == 0) { debug("No or 0 content-length."); content_length = 0; } else { if ((http_error = http_receive_payload(sockfd, &payload, content_length)) != HTTP_SUCCESS) { goto error; } } // create and fill return object struct HttpRequest *request = (struct HttpRequest *)malloc(sizeof(struct HttpRequest)); check_mem(request); request->method = method; request->resource = resource; request->headers = headers; request->content_length = content_length; request->payload = payload; *received_request = request; return HTTP_SUCCESS; error: dict_free(headers); //TODO free entries free(method); free(resource); free(payload); free(line); assert(http_error != HTTP_SUCCESS); return http_error; }
static void http_server_handler(int c) { int code; struct socket_buffer sock; struct http_request request; char *buf; socket_buffer_init(&sock, c); #if HAVE_OPENSSL if (o.ssl) { sock.fdn.ssl = new_ssl(sock.fdn.fd); if (SSL_accept(sock.fdn.ssl) != 1) { loguser("Failed SSL connection: %s\n", ERR_error_string(ERR_get_error(), NULL)); fdinfo_close(&sock.fdn); return; } } #endif code = http_read_request_line(&sock, &buf); if (code != 0) { if (o.verbose) logdebug("Error reading Request-Line.\n"); send_string(&sock.fdn, http_code2str(code)); fdinfo_close(&sock.fdn); return; } if (o.debug > 1) logdebug("Request-Line: %s", buf); code = http_parse_request_line(buf, &request); free(buf); if (code != 0) { if (o.verbose) logdebug("Error parsing Request-Line.\n"); send_string(&sock.fdn, http_code2str(code)); fdinfo_close(&sock.fdn); return; } if (!method_is_known(request.method)) { if (o.debug > 1) logdebug("Bad method: %s.\n", request.method); http_request_free(&request); send_string(&sock.fdn, http_code2str(405)); fdinfo_close(&sock.fdn); return; } code = http_read_header(&sock, &buf); if (code != 0) { if (o.verbose) logdebug("Error reading header.\n"); http_request_free(&request); send_string(&sock.fdn, http_code2str(code)); fdinfo_close(&sock.fdn); return; } if (o.debug > 1) logdebug("Header:\n%s", buf); code = http_request_parse_header(&request, buf); free(buf); if (code != 0) { if (o.verbose) logdebug("Error parsing header.\n"); http_request_free(&request); send_string(&sock.fdn, http_code2str(code)); fdinfo_close(&sock.fdn); return; } /* Check authentication. */ if (o.proxy_auth) { struct http_credentials credentials; int ret, stale; if (http_header_get_proxy_credentials(request.header, &credentials) == NULL) { /* No credentials or a parsing error. */ send_proxy_authenticate(&sock.fdn, 0); http_request_free(&request); fdinfo_close(&sock.fdn); return; } ret = check_auth(&request, &credentials, &stale); http_credentials_free(&credentials); if (!ret) { /* Password doesn't match. */ /* RFC 2617, section 1.2: "If a proxy does not accept the credentials sent with a request, it SHOULD return a 407 (Proxy Authentication Required). */ send_proxy_authenticate(&sock.fdn, stale); http_request_free(&request); fdinfo_close(&sock.fdn); return; } } if (strcmp(request.method, "CONNECT") == 0) { code = handle_connect(&sock, &request); } else if (strcmp(request.method, "GET") == 0 || strcmp(request.method, "HEAD") == 0 || strcmp(request.method, "POST") == 0) { code = handle_method(&sock, &request); } else { code = 500; } http_request_free(&request); if (code != 0) { send_string(&sock.fdn, http_code2str(code)); fdinfo_close(&sock.fdn); return; } fdinfo_close(&sock.fdn); }
//************************************************* void *handle_http(void *arg) { http_request_t *request = (http_request_t *)arg; int fd = request->fd; int rcode = 0; int is_static; struct stat sbuf; char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[SHORTLINE], header_key[MAXLINE], header_value[MAXLINE]; char filename[MAXLINE], cgiargs[MAXLINE]; int n; /* Read request line and headers */ while (1) { printf("[INFO] read from fd %d\n", fd); n = read(fd, request->last, (uint64_t)request->buf + MAX_BUF - (uint64_t)request->last); printf("[INFO] n = %d\n", n); if (n == 0) { goto err; } if (n < 0) { if (errno != EAGAIN) { log_err("read error, errno = %d", errno); goto err; } break; } request->last += n; rcode = http_parse_request_line(request); if (rcode == SERVER_AGAIN) { continue; } else if (rcode != SERVER_OK) { log_err("parse request line error, rcode = %d", rcode); goto err; } strncpy(method, request->request_start, request->method_end - request->request_start); strncpy(uri, request->uri_start, request->uri_end - request->uri_start); sprintf(version, "%d.%d", request->http_major, request->http_minor); if (request->method != HTTP_GET) { debug("error method: %s\n", method); debug("error uri: %s\n", uri); debug("error version: %s\n", version); clienterror(fd, method, "501", "Not Implemented", "Server does not implement this method"); return NULL; } debug("http request line"); debug("method = %s", method); debug("uri = %s", uri); debug("version = %s", version); rcode = http_parse_request_body(request); if (rcode == SERVER_AGAIN) { continue; } else if (rcode != SERVER_OK) { log_err("parse request body error, rcode = %d", rcode); goto err; } debug("number of headers = %d", request->num_headers); if (request->num_headers > 0) { list_node_t *head = request->head->next; int i; for (i = 0; i < request->num_headers; i++) { http_header_t *header = (http_header_t *)head->ptr; memset(header_key, 0, sizeof(header_key)); memset(header_value, 0, sizeof(header_value)); memcpy(header_key, header->key_start, header->key_end - header->key_start); memcpy(header_value, header->value_start, header->value_end - header->value_start); debug("%s: %s", header_key, header_value); head = head->next; } } is_static = parse_uri(uri, filename, cgiargs); if (stat(filename, &sbuf) < 0) { clienterror(fd, filename, "404", "Not found", "Server couldn't find this file"); continue; } if (is_static) { if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { clienterror(fd, filename, "403", "Forbidden", "Server couldn't read the file"); continue; } serve_static(fd, filename, sbuf.st_size); } goto close; } return NULL; err: close: debug("closing fd %d", fd); close(fd); }
/*(*** http_eater */ void http_eater(void *htd, char *p, int m) { http *ht; server *sv; ht = htd; sv = ht->ht_server; #if DEBUG { char buf[75]; int i; for(i = 0; i < m && i < sizeof(buf) - 1; i ++) { buf[i] = isprint(p[i]) ? p[i] : '?'; } buf[i] = 0; log_msg(sv->sv_log, LOG_DEBUG, "Got line [%s]", buf); } #endif reswitch: switch(ht->ht_state) { case HTTP_REQUEST: /* XXX: parse request */ { if(!http_parse_request_line(&ht->ht_hrl, p, m)) goto violation; /* MEMORY LEAK */ log_msg(sv->sv_log, LOG_DEBUG, "Method is [%s] URI is [%s] major is [%s] minor is [%s]", ht->ht_hrl.hrl_method, ht->ht_hrl.hrl_uri, ht->ht_hrl.hrl_major, ht->ht_hrl.hrl_minor); ht->ht_state = HTTP_HEADER; } break; case HTTP_HEADER: if(m) { /* Non-empty header lines */ http_release_header_line(&ht->ht_hhl); if(http_parse_header_line(&ht->ht_hhl, p, m)) { /* This is a nice header */ ht->ht_state = HTTP_MORE_HEADERS; } else goto violation; } else { /* Empty line : headers finished */ if(!http_process_header(ht)) goto violation; ht->ht_state = HTTP_BUILD_RESPONSE; goto reswitch; } break; case HTTP_MORE_HEADERS: if(m) { if(http_parse_header_continuation_line(&ht->ht_hhl, p, m)) break; /* Stay in this state. */ else { /* Not a continuation header. */ /* Post current header. */ if(!http_process_header(ht)) goto violation; ht->ht_state = HTTP_HEADER; goto reswitch; } } else { /* Empty line : headers finished */ if(!http_process_header(ht)) goto violation; ht->ht_state = HTTP_BUILD_RESPONSE; goto reswitch; } break; case HTTP_BUILD_RESPONSE: /* Build response */ { char *response; int response_length; char *content_type; size_t content_length; resource *rs; char *fn; if(!strcmp(ht->ht_hrl.hrl_method, "GET")) { fn = http_uri_to_filename(ht, ht->ht_hrl.hrl_uri); rs = resource_obtain(fn); if(!rs) { log_msg(sv->sv_log, LOG_ERROR, "Can't obtain %s (%s)", fn, strerror(errno)); response_length = xasprintf( &response, "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/html\r\n" "Connection: close\r\n" "\r\n" "<html>" "<head><title>404 Not Found</title></head><body>" "Unable to find resource %s (%s)." "</body></html>", fn, strerror(errno)); ht->ht_state = HTTP_RESPONSE_ONLY; } else { content_type = rs->rs_mime_type; content_length = rs->rs_length; if(ht->ht_writer) { writer_delete(ht->ht_writer); ht->ht_writer = 0; } ht->ht_resource = rs; response_length = xasprintf( &response, "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\n" "Connection: close\r\n" "Content-Length: %d\r\n" "\r\n", content_type, content_length); ht->ht_state = HTTP_RESPONDING; } xfree(fn); } else { response_length = xasprintf( &response, "HTTP/1.1 501 Not implemented\r\n" "Content-Type: text/html\r\n" "Connection: close\r\n" "\r\n" "<html>" "<head><title>501 Not implemented</title></head><body>" "Method %s is not supported by this server." "</body></html>", ht->ht_hrl.hrl_method); ht->ht_state = HTTP_RESPONSE_ONLY; } ht->ht_response = response; ht->ht_writer = writer_create(response, response_length, response); demux_set_event_mask(sv->sv_demux, ht->ht_connection, POLLOUT); /* Stop reading */ } break; case HTTP_RESPONSE_ONLY: case HTTP_RESPONDING: break; case HTTP_SERVING: break; } return; violation: log_msg(sv->sv_log, LOG_ERROR, "HTTP protocol violation"); demux_delete_connection(sv->sv_demux, ht->ht_connection); return; }
int http_parser_input(void* parser, const void* data, size_t *bytes) { enum { INPUT_NEEDMORE = 1, INPUT_DONE = 0, }; int r; struct http_context *ctx; ctx = (struct http_context*)parser; // save raw data r = http_rawdata(ctx, data, *bytes); if(0 != r) { assert(r < 0); return r; } if(SM_FIRSTLINE <= ctx->stateM && ctx->stateM < SM_HEADER) { r = is_server_mode(ctx) ? http_parse_request_line(ctx) : http_parse_status_line(ctx); } if(SM_HEADER <= ctx->stateM && ctx->stateM < SM_BODY) { r = http_parse_header_line(ctx); } assert(r <= 0); if(SM_BODY <= ctx->stateM && ctx->stateM < SM_DONE) { if(is_transfer_encoding_chunked(ctx)) { r = http_parse_chunked(ctx); } else { if(-1 == ctx->content_length) { if(is_server_mode(ctx)) { ctx->content_length = 0; ctx->stateM = SM_DONE; } else { // H4.4 Message Length, section 5, server closing the connection // receive all until socket closed assert(!is_server_mode(ctx)); if(0 == *bytes /*|| ctx->raw_size == ctx->offset*/) { ctx->content_length = ctx->raw_size - ctx->offset; ctx->stateM = SM_DONE; } } } else { assert(ctx->raw_size <= ctx->offset + ctx->content_length); if(ctx->raw_size >= ctx->offset + ctx->content_length) ctx->stateM = SM_DONE; } } } if(r < 0) return r; *bytes = 0; return ctx->stateM == SM_DONE ? INPUT_DONE : INPUT_NEEDMORE; }