int http_receive_headers(int sockfd, struct dict *headers) { int http_error = HTTP_SUCCESS; char *line = NULL; while (TRUE) { char *field_name; char *field_value; if ((http_error = http_read_line(sockfd, &line)) != HTTP_SUCCESS) { break; } if (strcmp(line, "") == 0) { debug("End of Http header"); break; } if ((http_error = http_parse_header_line(line, &field_name, &field_value)) != HTTP_SUCCESS) { break; } free(line); dict_set(headers, field_name, field_value); } free(line); return http_error; }
/*(*** 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; }