} END_TEST /* TODO: find two headers with coliding hash and test it */ START_TEST(test_http_headers) { http_hdr_table *t; t = http_headers_init(); fail_if(http_header_parse(t, "") == 0); fail_if(http_header_parse(t, "field") == 0); fail_if(http_header_parse(t, "field\r\n") == 0); fail_if(http_header_parse(t, "field:") == 0); fail_if(http_header_parse(t, "field:\r\n") == 0); fail_if(http_header_parse(t, "field:value") == 0); fail_if(http_header_parse(t, ":value") == 0); fail_if(http_header_parse(t, ":value\r\n") == 0); fail_unless(http_header_parse(t, "Host: dsl.sk\r\n") == 0); assert_str(http_header_get(t, "Host"), "dsl.sk"); assert_str(http_header_get(t, "host"), "dsl.sk"); assert_str(http_header_get(t, "HOST"), "dsl.sk"); http_headers_destroy(t); } END_TEST
int handle_request(struct client *cli) { int ret; int hlen; char *idxptr = NULL; // RequestS req; // memset(&req, 0, sizeof (RequestS)); Logger(LOG_INFO, 0, "%s...\n", __FUNCTION__); ret = httpd_tcp_recv(cli, cli->req.buffer, sizeof (cli->req.buffer)); if ( ret <= 0 ) { Logger(LOG_ERR, 0, "http recv failed!\n"); return -1; } idxptr = strStr(cli->req.buffer, ret, "\r\n\r\n", 4); if (idxptr == NULL) { Logger(LOG_ERR, 0, "Get request header failed!\n"); return -1; } hlen = (int)(idxptr-cli->req.buffer); cli->req.entity = idxptr + 4; http_header_parse(cli, hlen); return 0; }
int http_header_parse_next_field(struct http_header_parser *parser, const char **name_r, const unsigned char **data_r, size_t *size_r, const char **error_r) { const uoff_t max_size = parser->limits.max_size; const uoff_t max_field_size = parser->limits.max_field_size; const unsigned char *data; size_t size; int ret; *error_r = NULL; while ((ret=i_stream_read_more(parser->input, &parser->begin, &size)) > 0) { /* check header size limits */ if (parser->size >= max_size) { *error_r = "Excessive header size"; return -1; } if (parser->field_size > max_field_size) { *error_r = "Excessive header field size"; return -1; } /* don't parse beyond header size limits */ if (size > (max_size - parser->size)) size = max_size - parser->size; if (size > (max_field_size - parser->field_size)) { size = max_field_size - parser->field_size; size = (size == 0 ? 1 : size); /* need to parse one more byte */ } parser->cur = parser->begin; parser->end = parser->cur + size; if ((ret=http_header_parse(parser)) < 0) { *error_r = parser->error; return -1; } i_stream_skip(parser->input, parser->cur - parser->begin); parser->size += parser->cur - parser->begin; parser->field_size += parser->cur - parser->begin; if (ret == 1) { parser->field_size = 0; if (parser->state != HTTP_HEADER_PARSE_STATE_EOH) { data = buffer_get_data(parser->value_buf, &size); /* trim trailing OWS */ while (size > 0 && (data[size-1] == ' ' || data[size-1] == '\t')) size--; *name_r = str_c(parser->name); *data_r = data; *size_r = size; parser->state = HTTP_HEADER_PARSE_STATE_INIT; } else { *name_r = NULL; *data_r = NULL; } return 1; } } i_assert(ret != -2); if (ret < 0) { if (i_stream_is_eof(parser->input)) *error_r = "Premature end of input"; else *error_r = "Stream error"; } return ret; }
int http_get(char *urlstr, http_req_func_t func, void *farg) { url_t url; struct sockaddr_in iaddr; sockaddrlen_t addrlen; int i, err=-1; sock_t sock; int url_parsed=0; char buf[16384], buf1[1024]; size_t len=0; http_header_t http; http_req_t req; int got_header = 0; http_req_error_t req_err = 0; do { memset(&req, 0, sizeof(req)); req.req_url = &url; req.reply_hdr = &http; req_err = HTTP_REQ_ERROR_BAD_URL; i = url_parse(&url, urlstr); assertb(i==0); url_parsed = 1; if( strcasecmp(url.proto, "file") == 0 ) { struct stat st; FILE *f=0; req_err = HTTP_REQ_ERROR_FILE_NOT_FOUND; do { i = stat(url.path, &st); assertb(i==0); http.content_len = st.st_size; req.req_state = HTTP_REQ_BODY; f = fopen(url.path, "r"); assertb(f); while(1) { len = fread(buf, 1, sizeof(buf), f); if( len < 0 ) { req_err = HTTP_REQ_ERROR_INCOMPLETE; break; } if( len <= 0 ) break; err = func(&req, buf, len, farg); if( err ) break; } req_err = 0; err = 0; } while(0); if( f ) { fclose(f); } break; } req_err = HTTP_REQ_ERROR_BAD_URL; assertb( strcasecmp(url.proto, "http") == 0 ); req_err = HTTP_REQ_ERROR_CONNECT; sock = socket(AF_INET, SOCK_STREAM, 0); assertb_sockerr(sock>=0); req.req_state = HTTP_REQ_RESOLVE; i = snprintf(buf, sizeof(buf), "resolving host %s\n", url.host); err = func(&req, buf, i, farg); if( err != 0 ) break; addrlen = iaddr_pack(&iaddr, inet_resolve(url.host), url.port); req.req_state = HTTP_REQ_CONNECT; i = snprintf(buf, sizeof(buf), "connecting to host %s at %s\n" ,url.host ,iaddr_fmt(&iaddr, buf1, sizeof(buf1)) ); err = func(&req, buf, i, farg); if( err != 0 ) break; i = connect(sock, (struct sockaddr*)&iaddr, addrlen); assertb_sockerr(i==0); i = snprintf(buf, sizeof(buf), "GET %s HTTP/1.0\r\n" "Host: %s\r\n" "\r\n" ,url.path_args ,url.host ); req.req_state = HTTP_REQ_SEND; err = func(&req, buf, i, farg); if( err != 0 ) break; i = sock_send_timeout(sock, buf, i, 5000); assertb(i>=0); len = 0; got_header = 0; while(1) { assertb( len < sizeof(buf) ); i = recv(sock, buf+len, sizeof(buf)-len, 0); if( i < 0 ) { warn_sockerr(sock); req_err = HTTP_REQ_ERROR_INCOMPLETE; break; } if( i == 0 ) { req.req_state = HTTP_REQ_EOF; err = func(&req, 0, 0, farg); break; } len += i; if( !got_header ) { http_header_init(&http, 0); i = http_header_parse(&http, buf, len); if( i < 0 ) { req_err = HTTP_REQ_ERROR_BAD_RESPONSE; break; } if( i == 0 ) { continue; } got_header = 1; req.reply_max = http.content_len; req.req_state = HTTP_REQ_HEAD; err = func(&req, buf, http.header_len, farg); if( err != 0 ) { break; } len -= http.header_len; if( len > 0 ) { memmove(buf, buf+http.header_len, len); } } if( got_header ) { req.reply_len += len; req.req_state = HTTP_REQ_BODY; err = func(&req, buf, len, farg); len = 0; if( err ) { break; } } } req_err = 0; } while(0); if( got_header && http.response_code != 200 ) { req_err = HTTP_REQ_ERROR_FILE_NOT_FOUND; } if( req_err ) { req.req_state = HTTP_REQ_ERROR; req.req_error = req_err; err = func(&req, buf, len, farg); } if( url_parsed ) { url_free(&url); } if( got_header ) { http_header_free(&http); } return err; }
void http_redirect_client_select_read(fdselect_t *sel, fd_t fd, int state, void *arg) { http_redirect_client_t *client = (http_redirect_client_t *)arg; http_redirect_t *server = client->server; struct stat st; int i, n, err=-1; char buf[4096]; char buf1[1024]; do { i = client->recv_max - client->recv_len; if( i <= 0 ) { err = 405; /* request too big */ break; } i = recv(client->sock, client->recv_buf+client->recv_len, i, 0); if( i <= 0 ) { if( sock_wouldblock(client->sock, i) ) { /* ignore spurious blocking reads */ err = 0; break; } assertb_sockerr(i); } client->recv_len += i; client->recv_buf[client->recv_len] = 0; debug(DEBUG_INFO, ("http_redirect_client_select_read: addr=%s recv=%d\n" "recv_buf=\n%s\n" ,iaddr_fmt(&client->addr, buf1, sizeof(buf1)) ,i ,client->recv_buf )); i = http_header_parse(&client->http_req, client->recv_buf, client->recv_len); if( i == 0 ) { /* header not complete yet, continue */ err = 0; break; } else if( i < 0 ) { err = -i; break; } /* find a remote server to redirect to */ if( strcmp(req_url->path, "/p2p_router", strlen("/p2p_router"))==0 ) { addrlen = iaddr_pack(&addr, INADDR_ANY, 8969); } else { addrlen = iaddr_pack(&addr, INADDR_ANY, 80); } /* start connecting to remote server */ client->redirect_sock = socket(AF_INET, SOCK_STREAM, 0); assertb_sockerr(client->redirect_sock>=0); i = sock_nonblock(client->redirect_sock, 1); assertb_sockerr(i==0); i = connect(client->redirect_sock, (struct sockaddr*)&addr, addrlen); assertb_sockerr(i==0 || sock_wouldblock(client->redirect_sock, i)); /* stop reading from client, and wait for connection to complete */ fdselect_unset(server->fdselect, client->sock, FDSELECT_READ); fdselect_set(server->fdselect, client->redirect_sock, FDSELECT_READ, http_redirect_client_select_redirect_connect, client); i = snprintf(buf, sizeof(buf), "%s%s", server->root_dir, client->http_req.uri); err = 404; /* file not found */ i = stat(buf, &st); assertb(i==0); client->send_file = fopen(buf, "r"); assertb(client->send_file); debug(DEBUG_INFO, ("http_redirect_client_select_read: sending file=%s\n", buf)); i = snprintf(buf, sizeof(buf), "HTTP/1.0 200 Ok\r\n" "Pragma: no-cache\r\n" "Cache-Control: no-cache\r\n" "Connection: keep-alive\r\n" "Content-Length: %lu\r\n" "Content-Type: text/plain\r\n" "\r\n" ,st.st_size); n = i; i = send(client->sock, buf, n, 0); assertb(i==n); /* stop reading and start writing */ fdselect_unset(server->fdselect, client->sock, FDSELECT_READ); fdselect_set(server->fdselect, client->sock, FDSELECT_WRITE, http_redirect_client_select_write, client); err = 0; } while(0); if( err > 0 ) { i = snprintf(buf, sizeof(buf), "HTTP/1.0 %d\r\n" "Content-Type: text/html; charset=iso-8859-1\r\n" "\r\n" "<HTML><HEAD>\r\n" "<TITLE>HTTP Error %d</TITLE>\r\n" "</HEAD><BODY>\r\n" "<H1>HTTP Error %d</H1>\r\n" "%s<br>\r\n" "<HR>\r\n" "</BODY></HTML>\r\n" , err, err, err , client->http_req.uri); send(client->sock, buf, i, 0); } if( err ) { http_redirect_client_free(client); } }
void http_response_parse/*_and_read*/(struct http_ctx *c, http_response_t *r, buffer_t *d) { char buf[LIBNET_HTTP_SIZE_CHUNK] = {0}, *tok; uint32_t len, i = 0, chunk_size = 0; bool chunked = false; http_header_field_t *hf_e; if(c == NULL || r == NULL || d == NULL) { libnet_error_set(LIBNET_E_INV_ARG); return; } htbl_create(&r->header, 0); buffer_create(&r->body, LIBNET_BM_FILE); len = buffer_size(d); buffer_seek(d, 0); /* read status line */ while(len > 0) { buffer_read(d, buf + i, 1); if(strstr(buf, LIBNET_HTTP_DEL)) { break; } ++i; --len; } //printf("status line: %s", buf); /* do something with the http version */ /* get status code */ tok = strstr(buf, " ") + 1; sprintf(buf, "%s", tok); sscanf(buf, "%3d", &r->code); /* get reason */ i = strcspn(buf+4, LIBNET_HTTP_DEL) + 1; if(i > LIBNET_HTTP_SIZE_REASON) { i -= LIBNET_HTTP_SIZE_REASON; // LIBNET_HTTP_DEL; } snprintf(r->reason, i, "%s", buf+4); /* now parse header */ http_header_parse(&r->header, d); /* chunked? */ hf_e = (http_header_field_t *)htbl_get(&r->header, "transfer-encoding"); chunked = (!strcmp(hf_e->value, "chunked") ? true : false); uint32_t get_chunk_size(socket_t *s) { uint32_t i = 0, size; char buf[LIBNET_HTTP_SIZE_CHUNK] = {0}; while(!strstr(buf, LIBNET_HTTP_DEL)) { if(0 == socket_read(s, buf+i++, 1)) { return 0; } } if(i >= 3) { buf[i-2] = 0; } else { return 0; } sscanf(buf, "%x", &size); return size; }