📄 request.c
字号:
connected = ne_sock_connect(sock, ia, 7777) == 0; ne_addr_destroy(addr); if (sock == NULL) return 0; else { ne_sock_close(sock); return 1; }}/* This is a regression test for neon 0.17.0 and earlier, which goes * into an infinite loop if a request with a body is sent to a server * which simply closes the connection. */static int closed_connection(void){ int ret; /* This spawns a server process which will run the 'serve_close' * response function 200 times, then die. This guarantees that the * request eventually fails... */ CALL(fail_request(1, serve_close, NULL, 1)); /* if server died -> infinite loop was detected. */ ret = !is_alive(7777); reap_server(); ONN("server aborted, infinite loop?", ret); return OK;}static int serve_close2(ne_socket *sock, void *userdata){ int *count = userdata; *count += 1; if (*count == 1) return 0; NE_DEBUG(NE_DBG_HTTP, "Re-entered! Buggy client.\n"); CALL(discard_request(sock)); CALL(SEND_STRING(sock, RESP200 "Content-Length: 0\r\n\r\n")); return 0;}/* As closed_connection(); but check that the client doesn't retry * after receiving the EOF on the first request down a new * connection. */static int close_not_retried(void){ int count = 0; ne_session *sess = ne_session_create("http", "localhost", 7777); CALL(spawn_server_repeat(7777, serve_close2, &count, 3)); ONN("request was retried after EOF", any_request(sess, "/foo") == NE_OK); reap_server(); ne_session_destroy(sess); return OK;}static enum { prog_error, /* error */ prog_transfer, /* doing a transfer */ prog_done /* finished. */} prog_state = prog_transfer;static off_t prog_last = -1, prog_total;/* callback for send_progress. */static void s_progress(void *userdata, off_t prog, off_t total){ NE_DEBUG(NE_DBG_HTTP, "progress callback: %" NE_FMT_OFF_T "/%" NE_FMT_OFF_T ".\n", prog, total); switch (prog_state) { case prog_error: case prog_done: return; case prog_transfer: if (total != prog_total) { t_context("total unexpected: %ld not %ld", total, prog_total); prog_state = prog_error; } else if (prog > total) { t_context("first progress was invalid (%ld/%ld)", prog, total); prog_state = prog_error; } else if (prog_last != -1 && prog_last > prog) { t_context("progess went backwards: %ld to %ld", prog_last, prog); prog_state = prog_error; } else if (prog_last == prog) { t_context("no progress made! %ld to %ld", prog_last, prog); prog_state = prog_error; } else if (prog == total) { prog_state = prog_done; } break; } prog_last = prog;}static ssize_t provide_progress(void *userdata, char *buf, size_t bufsiz){ int *count = userdata; if (*count >= 0 && buf != NULL) { buf[0] = 'a'; *count -= 1; return 1; } else { return 0; }}static int send_progress(void){ static int count = 200; ON(prepare_request(single_serve_string, RESP200 "Connection: close\r\n\r\n")); prog_total = 200; ne_set_progress(def_sess, s_progress, NULL); ne_set_request_body_provider(def_req, count, provide_progress, &count);#define sess def_sess ONREQ(ne_request_dispatch(def_req));#undef sess ON(finish_request()); CALL(prog_state == prog_error); return OK; }static int read_timeout(void){ ne_session *sess; ne_request *req; time_t start, finish; int ret; CALL(make_session(&sess, sleepy_server, NULL)); /* timeout after one second. */ ne_set_read_timeout(sess, 1); req = ne_request_create(sess, "GET", "/timeout"); time(&start); ret = ne_request_dispatch(req); time(&finish); reap_server(); ONN("request succeeded, should have timed out", ret == NE_OK); ONV(ret != NE_TIMEOUT, ("request failed non-timeout error: %s", ne_get_error(sess))); ONN("timeout ignored, or very slow machine", finish - start > 3); ne_request_destroy(req); ne_session_destroy(sess); return OK; }/* expect failure code 'code', for request to given hostname and port, * without running a server. */static int fail_noserver(const char *hostname, unsigned int port, int code){ ne_session *sess = ne_session_create("http", hostname, port); int ret = any_request(sess, "/foo"); ne_session_destroy(sess); ONV(ret == NE_OK, ("request to server at %s:%u succeded?!", hostname, port)); ONV(ret != code, ("request failed with %d not %d", ret, code)); return OK;}static int fail_lookup(void){ return fail_noserver("no.such.domain", 7777, NE_LOOKUP);}/* neon 0.23.0 to 0.23.3: if a nameserver lookup failed, subsequent * requests on the session would crash. */static int fail_double_lookup(void){ ne_session *sess = ne_session_create("http", "nohost.example.com", 80); ONN("request did not give lookup failure", any_request(sess, "/foo") != NE_LOOKUP); ONN("second request did not give lookup failure", any_request(sess, "/bar") != NE_LOOKUP); ne_session_destroy(sess); return OK;}static int fail_connect(void){ return fail_noserver("localhost", 7777, NE_CONNECT);}/* Test that the origin server hostname is NOT resolved for a proxied * request. */static int proxy_no_resolve(void){ ne_session *sess = ne_session_create("http", "no.such.domain", 80); int ret; ne_session_proxy(sess, "localhost", 7777); CALL(spawn_server(7777, single_serve_string, RESP200 "Content-Length: 0\r\n\r\n")); ret = any_request(sess, "/foo"); ne_session_destroy(sess); ONN("origin server name resolved when proxy used", ret == NE_LOOKUP); CALL(await_server()); return OK;}/* If the chunk size is entirely invalid, the request should be * aborted. Fails with neon <0.22; invalid chunk sizes would be * silently treated as 'zero'. */static int fail_chunksize(void){ return fail_request(0, single_serve_string, RESP200 TE_CHUNKED "\r\n" "ZZZZZ\r\n\r\n", 0);}/* in neon <0.22, if an error occcurred whilst reading the response * body, the connection would not be closed (though this test will * succeed in neon <0.22 since it the previous test fails). */static int abort_respbody(void){ ne_session *sess; CALL(make_session(&sess, single_serve_string, RESP200 TE_CHUNKED "\r\n" "zzz\r\n" RESP200 "Content-Length: 0\r\n\r\n")); /* connection must be aborted on the first request, since it * contains an invalid chunk size. */ ONN("invalid chunk size was accepted?", any_request(sess, "/foo") != NE_ERROR); CALL(await_server()); /* second request should fail since server has gone away. */ ONN("connection was not aborted", any_request(sess, "/foo") == NE_OK); ne_session_destroy(sess); return OK;}static int serve_abort(ne_socket *sock, void *ud){ exit(0);}/* Test that after an aborted request on a peristent connection, a * failure of the *subsequent* request is not treated as a persistent * connection timeout and retried. */static int retry_after_abort(void){ ne_session *sess; /* Serve two responses down a single persistent connection, the * second of which is invalid and will cause the request to be * aborted. */ CALL(make_session(&sess, single_serve_string, RESP200 "Content-Length: 0\r\n\r\n" RESP200 TE_CHUNKED "\r\n" "zzzzz\r\n")); CALL(any_request(sess, "/first")); ONN("second request should fail", any_request(sess, "/second") == NE_OK); CALL(await_server()); /* spawn a server, abort the server immediately. If the * connection reset is interpreted as a p.conn timeout, a new * connection will be attempted, which will fail with * NE_CONNECT. */ CALL(spawn_server(7777, serve_abort, NULL)); ONN("third request was retried", any_request(sess, "/third") == NE_CONNECT); reap_server(); ne_session_destroy(sess); return OK;}/* Fail to parse the response status line: check the error message is * sane. Failed during 0.23-dev briefly, and possibly with 0.22.0 * too. */static int fail_statusline(void){ ne_session *sess; int ret; CALL(make_session(&sess, single_serve_string, "Fish.\r\n")); ret = any_request(sess, "/fail"); ONV(ret != NE_ERROR, ("request failed with %d not NE_ERROR", ret)); /* FIXME: will break for i18n. */ ONV(strcmp(ne_get_error(sess), "Could not parse response status line."), ("session error was `%s'", ne_get_error(sess))); ne_session_destroy(sess); return OK; }#define LEN (9000)static int fail_long_header(void){ char resp[LEN + 500] = "HTTP/1.1 200 OK\r\n" "Server: fish\r\n"; size_t len = strlen(resp); /* add a long header */ memset(resp + len, 'a', LEN); resp[len + LEN] = '\0'; strcat(resp, "\r\n\r\n"); return invalid_response_gives_error(resp, "Line too long");}static int fail_corrupt_chunks(void){ static const struct { const char *resp, *error; } ts[] = { /* not CRLF */ { RESP200 TE_CHUNKED "\r\n" "5\r\n" "abcdeFISH", "delimiter was invalid" }, /* short CRLF */ { RESP200 TE_CHUNKED "\r\n" "5\r\n" "abcde\n", "not read chunk delimiter" }, /* CR-notLF */ { RESP200 TE_CHUNKED "\r\n" "5\r\n" "abcde\rZZZ", "delimiter was invalid" }, { NULL, NULL } }; int n; for (n = 0; ts[n].resp; n++) CALL(invalid_response_gives_error(ts[n].resp, ts[n].error)); return OK;}static int versions(void){ ne_session *sess; CALL(make_session(&sess, single_serve_string, "HTTP/1.1 200 OK\r\n" "Content-Length: 0\r\n\r\n" "HTTP/1.0 200 OK\r\n" "Content-Length: 0\r\n\r\n")); CALL(any_request(sess, "/http11")); ONN("did not detect HTTP/1.1 compliance", ne_version_pre_http11(sess) != 0); CALL(any_request(sess, "/http10")); ONN("did not detect lack of HTTP/1.1 compliance", ne_version_pre_http11(sess) == 0); ne_session_destroy(sess); return OK;}struct cr_args { const char *method, *uri; int result;};static void hk_createreq(ne_request *req, void *userdata, const char *method, const char *requri){ struct cr_args *args = userdata; args->result = 1; /* presume failure */ if (strcmp(args->method, method)) t_context("Hook got method %s not %s", method, args->method); else if (strcmp(args->uri, requri)) t_context("Hook got Req-URI %s not %s", requri, args->uri); else args->result = 0;}static int hook_create_req(void){ ne_session *sess; struct cr_args args; CALL(make_session(&sess, single_serve_string, EMPTY_RESP EMPTY_RESP)); ne_hook_create_request(sess, hk_createreq, &args); args.method = "GET"; args.uri = "/foo"; args.result = -1; CALL(any_request(sess, "/foo")); ONN("first hook never called", args.result == -1); if (args.result) return FAIL; args.uri = "http://localhost:7777/bar"; args.result = -1; /* force use of absoluteURI in request-uri */ ne_session_proxy(sess, "localhost", 7777); CALL(any_request(sess, "/bar")); ONN("second hook never called", args.result == -1); if (args.result) return FAIL; ne_session_destroy(sess); return OK; }static int serve_check_method(ne_socket *sock, void *ud){ char *method = ud; char buf[20]; size_t methlen = strlen(method); if (ne_sock_read(sock, buf, methlen) != (ssize_t)methlen) return -1; ONN("method corrupted", memcmp(buf, method, methlen)); return single_serve_string(sock, "HTTP/1.1 204 OK\r\n\r\n");} /* Test that the method string passed to ne_request_create is * strdup'ed. */static int dup_method(void){ char method[] = "FOO"; ne_session *sess; ne_request *req; CALL(make_session(&sess, serve_check_method, method)); req = ne_request_create(sess, method, "/bar"); strcpy(method, "ZZZ"); ONREQ(ne_request_dispatch(req)); ne_request_destroy(req); ne_session_destroy(sess); CALL(await_server()); return OK;}ne_test tests[] = { T(lookup_localhost), T(single_get_clength), T(single_get_eof), T(single_get_chunked), T(no_body_204), T(no_body_205), T(no_body_304), T(no_body_HEAD), T(no_body_empty_clength), T(no_body_bad_clength), T(no_headers), T(chunks), T(te_header), T(any_te_header), T(reason_phrase), T(chunk_numeric), T(chunk_extensions), T(chunk_trailers), T(chunk_oversize), T(te_over_clength), T(te_over_clength2), T(no_body_chunks), T(persist_http11), T(persist_chunked), T(persist_http10), T(persist_timeout), T(no_persist_http10), T(ptimeout_eof), T(ptimeout_eof2), T(closed_connection), T(close_not_retried), T(send_progress), T(ignore_bad_headers), T(fold_headers), T(fold_many_headers), T(multi_header), T(empty_header), T(trailing_header), T(ignore_header_case), T(ignore_header_ws), T(ignore_header_ws2), T(ignore_header_ws3), T(ignore_header_tabs), T(continued_header), T(skip_interim_1xx), T(skip_many_1xx), T(skip_1xx_hdrs), T(send_bodies), T(expect_100_once), T(unbounded_headers), T(unbounded_folding), T(blank_response), T(not_http), T(fail_eof_continued), T(fail_eof_headers), T(fail_eof_chunk), T(fail_eof_badclen), T(fail_long_header), T(fail_corrupt_chunks), T(read_timeout), T(fail_lookup), T(fail_double_lookup), T(fail_connect), T(proxy_no_resolve), T(fail_chunksize), T(abort_respbody), T(retry_after_abort), T(fail_statusline), T(dup_method), T(versions), T(hook_create_req), T(NULL)};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -