📄 request.c
字号:
/* HTTP request handling tests Copyright (C) 2001-2003, Joe Orton <joe@manyfish.co.uk> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include "config.h"#include <sys/types.h>#include <time.h> /* for time() */#ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include "ne_request.h"#include "ne_socket.h"#include "tests.h"#include "child.h"#include "utils.h"static char buffer[BUFSIZ];static ne_session *def_sess;static ne_request *def_req;static int prepare_request(server_fn fn, void *ud){ static char uri[100]; def_sess = ne_session_create("http", "localhost", 7777); sprintf(uri, "/test%d", test_num); def_req = ne_request_create(def_sess, "GET", uri); CALL(spawn_server(7777, fn, ud)); return OK;}static int finish_request(void){ ne_request_destroy(def_req); ne_session_destroy(def_sess); return await_server();}#define RESP200 "HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"#define TE_CHUNKED "Transfer-Encoding: chunked\r\n"/* takes response body chunks and appends them to a buffer. */static void collector(void *ud, const char *data, size_t len){ ne_buffer *buf = ud; ne_buffer_append(buf, data, len);}typedef ne_request *(*construct_request)(ne_session *sess, void *userdata);/* construct a get request, callback for run_request. */static ne_request *construct_get(ne_session *sess, void *userdata){ ne_request *r = ne_request_create(sess, "GET", "/"); ne_buffer *buf = userdata; ne_add_response_body_reader(r, ne_accept_2xx, collector, buf); return r;}/* run a request created by callback 'cb' in session 'sess'. */static int run_request(ne_session *sess, int status, construct_request cb, void *userdata){ ne_request *req = cb(sess, userdata); ON(req == NULL); ONREQ(ne_request_dispatch(req)); ONV(ne_get_status(req)->code != status, ("response status-code was %d not %d", ne_get_status(req)->code, status)); ne_request_destroy(req); return OK;}/* Runs a server function 'fn', expecting to get a header 'name' with value * 'value' in the response. */static int expect_header_value(const char *name, const char *value, server_fn fn, void *userdata){ ne_session *sess; ne_request *req; char *gotval = NULL; CALL(make_session(&sess, fn, userdata)); req = ne_request_create(sess, "FOO", "/bar"); ne_add_response_header_handler(req, name, ne_duplicate_header, &gotval); ONREQ(ne_request_dispatch(req)); CALL(await_server()); ONN("no header value set", gotval == NULL); ONV(strcmp(gotval, value), ("header value mis-match: got [%s] not [%s]", gotval, value)); ne_request_destroy(req); ne_session_destroy(sess); ne_free(gotval); return OK;}/* runs a server function 'fn', expecting response body to be equal to * 'expect' */static int expect_response(const char *expect, server_fn fn, void *userdata){ ne_session *sess = ne_session_create("http", "localhost", 7777); ne_buffer *buf = ne_buffer_create(); ON(sess == NULL || buf == NULL); ON(spawn_server(7777, fn, userdata)); CALL(run_request(sess, 200, construct_get, buf)); ON(await_server()); ONN("response body match", strcmp(buf->data, expect)); ne_session_destroy(sess); ne_buffer_destroy(buf); return OK;}#define EMPTY_RESP RESP200 "Content-Length: 0\r\n\r\n"/* Process a request with given method and response, expecting to get * a zero-length response body. A second request is sent down the * connection (to ensure that the response isn't silently eaten), so * 'resp' must be an HTTP/1.1 response with no 'Connection: close' * header. */static int expect_no_body(const char *method, const char *resp){ ne_session *sess = ne_session_create("http", "localhost", 7777); ne_request *req = ne_request_create(sess, method, "/first"); ssize_t ret; char *r = ne_malloc(strlen(resp) + sizeof(EMPTY_RESP)); strcpy(r, resp); strcat(r, EMPTY_RESP); ON(spawn_server(7777, single_serve_string, r)); ne_free(r); ONN("failed to begin request", ne_begin_request(req)); ret = ne_read_response_block(req, buffer, BUFSIZ); ONV(ret != 0, ("got response block of size %" NE_FMT_SSIZE_T, ret)); ONN("failed to end request", ne_end_request(req)); /* process following request; makes sure that nothing extra has * been eaten by the first request. */ ONV(any_request(sess, "/second"), ("second request on connection failed: %s",ne_get_error(sess))); ON(await_server()); ne_request_destroy(req); ne_session_destroy(sess); return OK;}static int reason_phrase(void){ ne_session *sess; CALL(make_session(&sess, single_serve_string, RESP200 "Connection: close\r\n\r\n")); CALL(any_request(sess, "/foo")); CALL(await_server()); ONV(strcmp(ne_get_error(sess), "200 OK"), ("reason phrase mismatch: got `%s' not `200 OK'", ne_get_error(sess))); ne_session_destroy(sess); return OK; }static int single_get_eof(void){ return expect_response("a", single_serve_string, RESP200 "Connection: close\r\n" "\r\n" "a");}static int single_get_clength(void){ return expect_response("a", single_serve_string, RESP200 "Content-Length: 1\r\n" "\r\n" "a" "bbbbbbbbasdasd");}static int single_get_chunked(void) { return expect_response("a", single_serve_string, RESP200 TE_CHUNKED "\r\n" "1\r\n" "a\r\n" "0\r\n" "\r\n" "g;lkjalskdjalksjd");}static int no_body_304(void){ return expect_no_body("GET", "HTTP/1.1 304 Not Mfodified\r\n" "Content-Length: 5\r\n\r\n");}static int no_body_204(void){ return expect_no_body("GET", "HTTP/1.1 204 Not Modified\r\n" "Content-Length: 5\r\n\r\n");}static int no_body_205(void){ return expect_no_body("GET", "HTTP/1.1 205 Reset Content\r\n" "Content-Length: 5\r\n\r\n");}static int no_body_HEAD(void){ return expect_no_body("HEAD", "HTTP/1.1 200 OK\r\n" "Content-Length: 5\r\n\r\n");}static int no_body_empty_clength(void){ return expect_no_body("GET", "HTTP/1.1 200 OK\r\n" "Content-Length:\r\n\r\n");}static int no_body_bad_clength(void){ return expect_no_body("GET", "HTTP/1.1 200 OK\r\n" "Content-Length: foobar\r\n\r\n");}static int no_headers(void){ return expect_response("abcde", single_serve_string, "HTTP/1.1 200 OK\r\n\r\n" "abcde");}#define CHUNK(len, data) #len "\r\n" data "\r\n"#define ABCDE_CHUNKS CHUNK(1, "a") CHUNK(1, "b") \ CHUNK(1, "c") CHUNK(1, "d") \ CHUNK(1, "e") CHUNK(0, "")static int chunks(void){ /* lots of little chunks. */ return expect_response("abcde", single_serve_string, RESP200 TE_CHUNKED "\r\n" ABCDE_CHUNKS);}static int te_header(void){ return expect_response("abcde", single_serve_string, RESP200 "Transfer-Encoding: CHUNKED\r\n" "\r\n" ABCDE_CHUNKS);}/* test that the presence of *any* t-e header implies a chunked * response. */static int any_te_header(void){ return expect_response("abcde", single_serve_string, RESP200 "Transfer-Encoding: punked\r\n" "\r\n" ABCDE_CHUNKS);}static int chunk_numeric(void){ /* leading zero's */ return expect_response("0123456789abcdef", single_serve_string, RESP200 TE_CHUNKED "\r\n" "000000010\r\n" "0123456789abcdef\r\n" "000000000\r\n" "\r\n");}static int chunk_extensions(void){ /* chunk-extensions. */ return expect_response("0123456789abcdef", single_serve_string, RESP200 TE_CHUNKED "\r\n" "000000010; foo=bar; norm=fish\r\n" "0123456789abcdef\r\n" "000000000\r\n" "\r\n");}static int chunk_trailers(void){ /* trailers. */ return expect_response("abcde", single_serve_string, RESP200 TE_CHUNKED "\r\n" "00000005; foo=bar; norm=fish\r\n" "abcde\r\n" "000000000\r\n" "X-Hello: world\r\n" "X-Another: header\r\n" "\r\n");}static int chunk_oversize(void){#define BIG (20000) char *body = ne_malloc(BIG + 1); static const char rnd[] = "abcdefghijklm"; int n; ne_buffer *buf = ne_buffer_create(); for (n = 0; n < BIG; n++) { body[n] = rnd[n % (sizeof(rnd) - 1)]; } body[n] = '\0';#undef BIG ne_buffer_concat(buf, RESP200 TE_CHUNKED "\r\n" "4E20\r\n", body, "\r\n", "0\r\n\r\n", NULL); CALL(expect_response(body, single_serve_string, buf->data)); ne_buffer_destroy(buf); ne_free(body); return OK;}static int te_over_clength(void){ /* T-E dominates over C-L. */ return expect_response("abcde", single_serve_string, RESP200 TE_CHUNKED "Content-Length: 300\r\n" "\r\n" ABCDE_CHUNKS);}/* te_over_clength with the headers the other way round; check for * ordering problems. */static int te_over_clength2(void){ return expect_response("abcde", single_serve_string, RESP200 "Content-Length: 300\r\n" TE_CHUNKED "\r\n" ABCDE_CHUNKS);}/* obscure case which is possibly a valid request by 2616, but should * be handled correctly in any case. neon <0.22.0 tries to * eat the response body, which is probably incorrect. */static int no_body_chunks(void){ return expect_no_body("HEAD", "HTTP/1.1 204 Not Modified\r\n" TE_CHUNKED "\r\n");}static int serve_twice(ne_socket *sock, void *userdata){ const char *resp = userdata; CALL(discard_request(sock)); SEND_STRING(sock, resp); CALL(discard_request(sock)); SEND_STRING(sock, resp); return OK;}/* Test persistent connection handling: serve 'response' twice on a * single TCP connection, expecting to get a response body equal to * 'body' both times. */static int test_persist(const char *response, const char *body){ ne_session *sess = ne_session_create("http", "localhost", 7777); ne_buffer *buf = ne_buffer_create(); ON(sess == NULL || buf == NULL); ON(spawn_server(7777, serve_twice, (char *)response)); CALL(run_request(sess, 200, construct_get, buf)); ONV(strcmp(buf->data, body), ("response #1 mismatch: [%s] not [%s]", buf->data, body)); /* Run it again. */ ne_buffer_clear(buf); CALL(run_request(sess, 200, construct_get, buf)); ON(await_server()); ONV(strcmp(buf->data, body), ("response #2 mismatch: [%s] not [%s]", buf->data, body)); ne_session_destroy(sess); ne_buffer_destroy(buf); return OK;}static int persist_http11(void){ return test_persist(RESP200 "Content-Length: 5\r\n\r\n" "abcde", "abcde");}static int persist_chunked(void){ return test_persist(RESP200 TE_CHUNKED "\r\n" ABCDE_CHUNKS, "abcde");}static int persist_http10(void){ return test_persist("HTTP/1.0 200 OK\r\n" "Connection: keep-alive\r\n" "Content-Length: 5\r\n\r\n" "abcde", "abcde");}/* Server function for fail_early_eof */static int serve_eof(ne_socket *sock, void *ud){ const char *resp = ud; /* dummy request/response. */ CALL(discard_request(sock)); CALL(SEND_STRING(sock, RESP200 "Content-Length: 0\r\n\r\n")); /* real request/response. */ CALL(discard_request(sock)); CALL(SEND_STRING(sock, resp)); return OK;}/* Utility function: 'resp' is a truncated response; such that an EOF * arrives early during response processing; but NOT as a valid * premature EOF due to a persistent connection timeout. It is an * error if the request is then retried, and the test fails. */static int fail_early_eof(const char *resp){ ne_session *sess = ne_session_create("http", "localhost", 7777); CALL(spawn_server_repeat(7777, serve_eof, (char *)resp, 3)); ONREQ(any_request(sess, "/foo")); ONN("request retried after early EOF", any_request(sess, "/foobar") == NE_OK); CALL(reap_server()); ne_session_destroy(sess); return OK;}/* This failed with neon <0.22. */static int fail_eof_continued(void){ return fail_early_eof("HTTP/1.1 100 OK\r\n\r\n");}static int fail_eof_headers(void){ return fail_early_eof("HTTP/1.1 200 OK\r\nJimbob\r\n");}static int fail_eof_chunk(void){ return fail_early_eof(RESP200 TE_CHUNKED "\r\n" "1\r\n" "a");}static int fail_eof_badclen(void){ return fail_early_eof(RESP200 "Content-Length: 10\r\n\r\n" "abcde");}/* Persistent connection timeout where a FIN is sent to terminate the * connection, which is caught by a 0 return from the read() when the * second request reads the status-line. */static int ptimeout_eof(void){ ne_session *sess = ne_session_create("http", "localhost", 7777); CALL(spawn_server_repeat(7777, single_serve_string, RESP200 "Content-Length: 0\r\n" "\r\n", 4)); CALL(any_2xx_request(sess, "/first")); CALL(any_2xx_request(sess, "/second")); ONN("server died prematurely?", dead_server()); reap_server(); ne_session_destroy(sess); return OK;}/* Persistent connection timeout where a FIN is sent to terminate the * connection, but the request fails in the write() call which sends * the body. */static int ptimeout_eof2(void){ ne_session *sess = ne_session_create("http", "localhost", 7777); CALL(spawn_server_repeat(7777, single_serve_string,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -