| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567 |
- /*
- HTTP request handling tests
- Copyright (C) 2001-2024, Joe Orton <[email protected]>
- 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 <fcntl.h>
- #include <errno.h>
- #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;
- /* Last (real) port used by fail_request_with_error(). */
- static unsigned int fail_request_last_port;
- static int prepare_request(server_fn fn, void *ud)
- {
- static char uri[100];
- unsigned int port;
- CALL(new_spawn_server(1, fn, ud, &port));
- def_sess = ne_session_create("http", "localhost", port);
- sprintf(uri, "/test%d", test_num);
- def_req = ne_request_create(def_sess, "GET", uri);
- return OK;
- }
- static int finish_request(void)
- {
- ne_request_destroy(def_req);
- return destroy_and_wait(def_sess);
- }
- #define RESP200 "HTTP/1.1 200 OK\r\n" "Server: neon-test-server\r\n"
- #define TE_CHUNKED "Transfer-Encoding: chunked\r\n"
- #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, "")
- /* takes response body chunks and appends them to a buffer. */
- static int collector(void *ud, const char *data, size_t len)
- {
- ne_buffer *buf = ud;
- ne_buffer_append(buf, data, len);
- return 0;
- }
- 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;
- if (buf) 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. If 'value' is NULL, expects that
- * *no* header of that name is present. */
- static int expect_header_value(const char *name, const char *value,
- server_fn fn, void *userdata)
- {
- ne_session *sess;
- ne_request *req;
- const char *gotval;
- CALL(make_session(&sess, fn, userdata));
- NE_DEBUG(NE_DBG_HTTP, "[test] Testing for expected header [%s] / [%s]\n",
- name, value);
- req = ne_request_create(sess, "FOO", "/bar");
- ONREQ(ne_request_dispatch(req));
- ne_close_connection(sess);
- CALL(await_server());
-
- gotval = ne_get_response_header(req, name);
- ONV(value && !gotval, ("header '%s: %s' not sent", name, value));
- ONV(!value && gotval, ("header '%s: %s' not expected", name, gotval));
- ONV(value && gotval && strcmp(gotval, value),
- ("header '%s' mis-match: got '%s' not '%s'",
- name, gotval, value));
-
- ne_request_destroy(req);
- ne_session_destroy(sess);
- 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_buffer *buf = ne_buffer_create();
- CALL(session_server(&sess, fn, userdata));
- CALL(run_request(sess, 200, construct_get, buf));
- CALL(destroy_and_wait(sess));
- ONN("response body match", strcmp(buf->data, expect));
- 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_request *req;
- ssize_t ret;
- char *r = ne_malloc(strlen(resp) + sizeof(EMPTY_RESP));
-
- strcpy(r, resp);
- strcat(r, EMPTY_RESP);
- CALL(session_server(&sess, single_serve_string, r));
- ne_free(r);
- req = ne_request_create(sess, method, "/first");
- 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)));
- ne_request_destroy(req);
- return destroy_and_wait(sess);
- }
- static int reason_phrase(void)
- {
- ne_session *sess;
- CALL(make_session(&sess, single_serve_string, RESP200
- "Connection: close\r\n\r\n"));
- ONREQ(any_request(sess, "/foo"));
- ne_close_connection(sess);
- 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;
- }
- #if 0
- /* This feature was added then remoevd, it potentially wasn't safe
- * since the location string wasn't cleaned or checked to be a valid
- * URI. */
- static int redirect_error(void)
- {
- ne_session *sess;
- CALL(make_session(&sess, single_serve_string,
- "HTTP/1.1 301 Moved Permanently\r\n"
- "Location: http://example.com/redirected\r\n"
- "Connection: close\r\n\r\n"));
- ONREQ(any_request(sess, "/foo"));
- ne_close_connection(sess);
- CALL(await_server());
- ONV(strcmp(ne_get_error(sess), "Redirected to http://example.com/redirected"),
- ("error mismatch: got `%s' not redirect location",
- ne_get_error(sess)));
- ne_session_destroy(sess);
- return OK;
- }
- #endif
- 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_HEAD(void)
- {
- return expect_no_body("HEAD", "HTTP/1.1 200 OK\r\n"
- "Content-Length: 5\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;
- }
- /* 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_p(const char *response, const char *body, int proxy)
- {
- ne_session *sess;
- ne_buffer *buf = ne_buffer_create();
- if (proxy) {
- CALL(proxied_session_server(&sess, "http", "localhost", 1234,
- serve_twice, (void *)response));
- ne_set_session_flag(sess, NE_SESSFLAG_CONNAUTH, 1);
- }
- else {
- CALL(session_server(&sess, serve_twice, (void *)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));
- CALL(destroy_and_wait(sess));
- ONV(strcmp(buf->data, body),
- ("response #2 mismatch: [%s] not [%s]", buf->data, body));
- ne_buffer_destroy(buf);
- return OK;
- }
- static int test_persist(const char *response, const char *body)
- {
- return test_persist_p(response, body, 0);
- }
- 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");
- }
- static int persist_proxy_http10(void)
- {
- return test_persist_p("HTTP/1.0 200 OK\r\n"
- "Proxy-Connection: keep-alive\r\n"
- "Content-Length: 5\r\n\r\n" "abcde",
- "abcde", 1);
- }
- /* 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;
- unsigned int port;
- CALL(new_spawn_server(3, serve_eof, (char *)resp, &port));
-
- sess = ne_session_create("http", "localhost", port);
- 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;
- unsigned int port;
- CALL(new_spawn_server(4, single_serve_string,
- RESP200 "Content-Length: 0\r\n" "\r\n",
- &port));
- sess = ne_session_create("http", "localhost", port);
-
- 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;
- unsigned int port;
- CALL(new_spawn_server(4, single_serve_string,
- RESP200 "Content-Length: 0\r\n" "\r\n",
- &port));
-
- sess = ne_session_create("http", "localhost", port);
- CALL(any_2xx_request(sess, "/first"));
- minisleep();
- CALL(any_2xx_request_body(sess, "/second"));
-
- ONN("server died prematurely?", dead_server());
- reap_server();
- ne_session_destroy(sess);
- return OK;
- }
- /* TODO: add a ptimeout_reset too, if an RST can be reliably generated
- * mid-connection. */
- static int incr_server(ne_socket *sock, void *arg)
- {
- struct many_serve_args *msa = arg;
-
- CALL(many_serve_string(sock, msa));
-
- msa->count++;
-
- return OK;
- }
- /* Emulates a persistent connection timeout on the server. This tests
- * the timeout occurring after between 1 and 10 requests down the
- * connection. */
- static int persist_timeout(void)
- {
- ne_session *sess;
- ne_buffer *buf = ne_buffer_create();
- struct many_serve_args args;
- unsigned int port;
- int n;
- args.str = RESP200 "Content-Length: 5\r\n\r\n" "abcde";
- args.count = 1;
- CALL(new_spawn_server(9, incr_server, &args, &port));
-
- sess = ne_session_create("http", "localhost", port);
- for (args.count = 1; args.count < 10; args.count++) {
- for (n = 0; n < args.count; n++) {
- ONV(run_request(sess, 200, construct_get, buf),
- ("%d of %d, request failed: %s", n, args.count,
- ne_get_error(sess)));
-
- ONV(strcmp(buf->data, "abcde"),
- ("%d of %d, response body mismatch", n, args.count));
- /* Ready for next time. */
- ne_buffer_clear(buf);
- }
- }
- ne_buffer_destroy(buf);
- return destroy_and_wait(sess);
- }
- /* Test that an HTTP/1.0 server is not presumed to support persistent
- * connections by default. */
- static int no_persist_http10(void)
- {
- ne_session *sess;
- unsigned int port;
- CALL(new_spawn_server(4, single_serve_string,
- "HTTP/1.0 200 OK\r\n"
- "Content-Length: 5\r\n\r\n"
- "abcde"
- "Hello, world - what a nice day!\r\n",
- &port));
- sess = ne_session_create("http", "localhost", port);
- /* if the connection is treated as persistent, the status-line for
- * the second request will be "Hello, world...", which will
- * fail. */
- ONREQ(any_request(sess, "/foobar"));
- ONREQ(any_request(sess, "/foobar"));
- ONN("server died prematurely?", dead_server());
- CALL(reap_server());
- ne_session_destroy(sess);
- return OK;
- }
- #define NO_BODY "Content-Length: 0\r\n\r\n"
- static int response_bodies(void)
- {
- struct {
- const char *expect;
- server_fn fn;
- const char *response;
- } ts[] = {
- { "a", single_serve_string, RESP200 "Connection: close\r\n" "\r\n" "a" },
- { "a", single_serve_string, RESP200 "Content-Length: \t\t 1 \t\t\r\n"
- "\r\n" "a" "bbbbbbbbasdasd" },
- { "a", single_serve_string, RESP200 TE_CHUNKED "\r\n"
- "1\r\n" "a\r\n"
- "0\r\n" "\r\n"
- "g;lkjalskdjalksjd" },
- { "abcde", single_serve_string, "HTTP/1.1 200 OK\r\n\r\n" "abcde" }, /* no headers */
- { "abcde", single_serve_string, RESP200 "Stupid Header\r\n" "ReallyStupidHeader\r\n"
- "Content-Length: 5\r\n" "\r\n" "abcde" },
- { "abcde", single_serve_string, RESP200 "Content-Length: \r\n 5\r\n" "\r\n" "abcde" },
- { "abcde", single_serve_string, RESP200 "Content-Length: \r\n \r\n \r\n \r\n 5\r\n" "\r\n" "abcde" },
- /* chunk tests */
- { "abcde", single_serve_string, RESP200 TE_CHUNKED "\r\n" ABCDE_CHUNKS },
- { "abcde", single_serve_string, RESP200 "Transfer-Encoding: CHUNKED\r\n" "\r\n" ABCDE_CHUNKS },
- /* http://bugzilla.gnome.org/show_bug.cgi?id=310636 says privoxy
- * uses the "identity" transfer-coding. */
- { "abcde", single_serve_string, RESP200 "Transfer-Encoding: identity\r\n" "Content-Length: 5\r\n"
- "\r\n" "abcde" },
- /* leading zero's */
- { "0123456789abcdef", single_serve_string, RESP200 TE_CHUNKED "\r\n"
- "000000010\r\n" "0123456789abcdef\r\n"
- "000000000\r\n" "\r\n" },
- /* chunk-extensions. */
- { "0123456789abcdef", single_serve_string, RESP200 TE_CHUNKED "\r\n"
- "000000010; foo=bar; norm=fish\r\n"
- "0123456789abcdef\r\n"
- "000000000\r\n" "\r\n" },
- { "0123456789abcdef", single_serve_string, RESP200 TE_CHUNKED "\r\n"
- "10 \t ; foo=bar\r\n"
- "0123456789abcdef\r\n"
- "000000000\r\n" "\r\n" },
- /* trailers. */
- { "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" },
- /* T-E dominates over C-L. */
- { "abcde", single_serve_string, RESP200 TE_CHUNKED
- "Content-Length: 300\r\n"
- "\r\n" ABCDE_CHUNKS },
- { "abcde", single_serve_string, RESP200 "Content-Length: 300\r\n"
- TE_CHUNKED "\r\n" ABCDE_CHUNKS },
- { NULL, NULL, NULL }
- };
- unsigned n;
- for (n = 0; ts[n].expect != NULL; n++ ){
- CALL(expect_response(ts[n].expect, ts[n].fn, (void *)ts[n].response));
- }
- return OK;
- }
- static int response_headers(void)
- {
- struct {
- const char *name, *value;
- server_fn fn;
- const char *response;
- } ts[] = {
- { "ranDom-HEader", "", single_serve_string, RESP200 "RANDom-HeADEr:\r\n" NO_BODY },
- { "ranDom-HEader", "noddy", single_serve_string, RESP200 "RANDom-HeADEr: noddy\r\n" NO_BODY },
- { "ranDom-HEader", "fishy", single_serve_string, RESP200 "RANDom-HeADEr: fishy\r\n" NO_BODY },
- { "ranDom-HEader", "fishy", single_serve_string, RESP200 "RANDom-HeADEr: \t fishy\r\n" NO_BODY},
- { "ranDom-HEader", "fishy", single_serve_string, RESP200 "RANDom-HeADEr: fishy \r\n" NO_BODY },
- { "ranDom-HEader", "geezer", single_serve_string, RESP200 "RANDom-HeADEr: \t \tgeezer\r\n" NO_BODY },
- { "gONe", "fishing", single_serve_string,
- RESP200 TE_CHUNKED "\r\n0\r\n" "Hello: world\r\n" "GONE: fishing\r\n" "\r\n" },
- { "hello", "w o r l d", single_serve_string,
- RESP200 "Hello: \n\tw\r\n\to r l\r\n\td \r\n" NO_BODY },
- { "X-Header", "jim, jab, jar", single_serve_string,
- RESP200 "X-Header: jim\r\n" "x-header: jab\r\n" "x-Header: jar\r\n" NO_BODY },
- { "X-Header", "jim, jab, jar", single_serve_string,
- RESP200 "X-Header: jim \r\n" "x-header: jab \r\n" "x-Header: jar \r\n" NO_BODY },
- /* RFC 2616 14.10: headers listed in Connection must be stripped on
- * receiving an HTTP/1.0 message in case there was a pre-1.1 proxy
- * somewhere. */
- { "X-Widget", NULL, single_serve_string,
- "HTTP/1.0 200 OK\r\n" "Connection: x-widget\r\n" "x-widget: blah\r\n" NO_BODY },
- { "X-Widget", NULL, single_serve_string,
- "HTTP/1.0 200 OK\r\n" "Connection: connection, x-fish, x-widget\r\n" "x-widget: blah\r\n" NO_BODY },
- /* Test trailer header handling. */
- { "X-Trailer", "foo", single_serve_string,
- RESP200 TE_CHUNKED "X-Header: bar\r\n\r\n"
- CHUNK(6, "foobar") "0\r\nX-Trailer: foo\r\n\r\n" },
- { "X-Header", "foo", single_serve_string,
- RESP200 TE_CHUNKED "X-Header: foo\r\n\r\n"
- CHUNK(6, "foobar") "0\r\nX-Trailer: barish\r\n\r\n" },
- /* Test that merging between trailers and main response headers works. */
- { "X-Trailer", "fooish, barish", single_serve_string,
- RESP200 TE_CHUNKED "X-Trailer: fooish\r\n\r\n"
- CHUNK(6, "foobar") "0\r\nX-Trailer: barish\r\n\r\n" },
- /* Test that bare LFs are treated as a spaces. */
- { "X-Test", "just plain spaces", single_serve_string,
- RESP200 "X-Test: just\rplain\rspaces\r\n" NO_BODY },
- /* Invalid vs valid header names */
- { "Content\"Length", NULL, single_serve_string,
- RESP200 "Content\"Length: foobar\r\n" NO_BODY },
- { "Content!Length", "foobar", single_serve_string,
- RESP200 "Content!Length: foobar\r\n" NO_BODY },
- { NULL, NULL, NULL, NULL }
- };
- unsigned n;
- for (n = 0; ts[n].name != NULL; n++ ){
- CALL(expect_header_value(ts[n].name, ts[n].value, ts[n].fn, (void *)ts[n].response));
- }
- return OK;
- }
- static int post_send_retry(ne_request *req, void *userdata,
- const ne_status *status)
- {
- return status->code == 400 ? NE_RETRY : NE_OK;
- }
- /* Test that the stored response headers are forgotten if the request
- * is retried. */
- static int reset_headers(void)
- {
- ne_session *sess;
- ne_request *req;
- const char *value;
- CALL(make_session(&sess, single_serve_string,
- "HTTP/1.1 400 Hit me again\r\n"
- "Content-Length: 0\r\n"
- "X-Foo: bar\r\n" "\r\n"
- "HTTP/1.1 200 Thank you kindly\r\n"
- "Content-Length: 0\r\n"
- "X-Foo: hello fair world\r\n" "\r\n"));
- ne_hook_post_send(sess, post_send_retry, NULL);
-
- req = ne_request_create(sess, "GET", "/foo");
- ONREQ(ne_request_dispatch(req));
- value = ne_get_response_header(req, "X-Foo");
- ONCMP("hello fair world", value, "response header", "X-Foo");
- ne_request_destroy(req);
- return destroy_and_wait(sess);
- }
- static int iterate_none(void)
- {
- ne_session *sess;
- ne_request *req;
- CALL(make_session(&sess, single_serve_string,
- "HTTP/1.0 200 OK\r\n\r\n"));
- req = ne_request_create(sess, "GET", "/");
- ONREQ(ne_request_dispatch(req));
- ONN("iterator was not NULL for no headers",
- ne_response_header_iterate(req, NULL, NULL, NULL) != NULL);
- ne_request_destroy(req);
- return destroy_and_wait(sess);
- }
- #define MANY_HEADERS (90)
- static int iterate_many(void)
- {
- ne_request *req;
- ne_buffer *buf = ne_buffer_create();
- ne_session *sess;
- int n;
- struct header {
- char name[10], value[10];
- int seen;
- } hdrs[MANY_HEADERS];
- void *cursor = NULL;
- const char *name, *value;
-
- ne_buffer_czappend(buf, "HTTP/1.0 200 OK\r\n");
- for (n = 0; n < MANY_HEADERS; n++) {
- sprintf(hdrs[n].name, "x-%d", n);
- sprintf(hdrs[n].value, "Y-%d", n);
- hdrs[n].seen = 0;
-
- ne_buffer_concat(buf, hdrs[n].name, ": ", hdrs[n].value, "\r\n", NULL);
- }
-
- ne_buffer_czappend(buf, "\r\n");
- CALL(make_session(&sess, single_serve_string, buf->data));
- req = ne_request_create(sess, "GET", "/foo");
- ONREQ(ne_request_dispatch(req));
- while ((cursor = ne_response_header_iterate(req, cursor, &name, &value))) {
- ONV(strncmp(name, "x-", 2) || strncmp(value, "Y-", 2)
- || strcmp(name + 2, value + 2)
- || (n = atoi(name + 2)) >= MANY_HEADERS
- || n < 0,
- ("bad name/value pair: %s = %s", name, value));
- NE_DEBUG(NE_DBG_HTTP, "iterate: got pair (%d): %s = %s\n",
- n, name, value);
- ONV(hdrs[n].seen == 1, ("duplicate pair %d", n));
- hdrs[n].seen = 1;
- }
-
- for (n = 0; n < MANY_HEADERS; n++) {
- ONV(hdrs[n].seen == 0, ("unseen pair %d", n));
- }
-
- ne_buffer_destroy(buf);
- ne_request_destroy(req);
- return destroy_and_wait(sess);
- }
- struct s1xx_args {
- int count;
- int hdrs;
- };
- static int serve_1xx(ne_socket *sock, void *ud)
- {
- struct s1xx_args *args = ud;
- CALL(discard_request(sock));
-
- do {
- if (args->hdrs) {
- SEND_STRING(sock, "HTTP/1.1 100 Continue\r\n"
- "Random: header\r\n"
- "Another: header\r\n\r\n");
- } else {
- SEND_STRING(sock, "HTTP/1.1 100 Continue\r\n\r\n");
- }
- } while (--args->count > 0);
-
- SEND_STRING(sock, RESP200 "Content-Length: 0\r\n\r\n");
-
- return OK;
- }
- #define sess def_sess
- static int skip_interim_1xx(void)
- {
- struct s1xx_args args = {0, 0};
- ON(prepare_request(serve_1xx, &args));
- ONREQ(ne_request_dispatch(def_req));
- return finish_request();
- }
- static int skip_many_1xx(void)
- {
- struct s1xx_args args = {5, 0};
- ON(prepare_request(serve_1xx, &args));
- ONREQ(ne_request_dispatch(def_req));
- return finish_request();
- }
- static int skip_1xx_hdrs(void)
- {
- struct s1xx_args args = {5, 5};
- ON(prepare_request(serve_1xx, &args));
- ONREQ(ne_request_dispatch(def_req));
- return finish_request();
- }
- #undef sess
- /* server for expect_100_once: serves a 100-continue request, and
- * fails if the request body is sent twice. */
- static int serve_100_once(ne_socket *sock, void *ud)
- {
- struct s1xx_args args = {2, 0};
- char ch;
- CALL(serve_1xx(sock, &args));
- CALL(discard_body(sock));
- ONN("body was served twice", ne_sock_read(sock, &ch, 1) == 1);
- return OK;
- }
- /* regression test; fails with neon <0.22, where the request body was
- * served *every* time a 1xx response was received, rather than just
- * once. */
- static int expect_100_once(void)
- {
- ne_session *sess;
- ne_request *req;
- char body[BUFSIZ];
- CALL(make_session(&sess, serve_100_once, NULL));
- req = ne_request_create(sess, "GET", "/foo");
- ne_set_request_flag(req, NE_REQFLAG_EXPECT100, 1);
- ONN("expect100 flag ignored",
- ne_get_request_flag(req, NE_REQFLAG_EXPECT100) != 1);
- memset(body, 'A', sizeof(body));
- ne_set_request_body_buffer(req, body, sizeof(body));
- ONREQ(ne_request_dispatch(req));
- ne_request_destroy(req);
- return destroy_and_wait(sess);
- }
- /* regression test for enabling 100-continue without sending a body. */
- static int expect_100_nobody(void)
- {
- ne_session *sess;
- ne_request *req;
- CALL(make_session(&sess, serve_100_once, NULL));
-
- req = ne_request_create(sess, "GET", "/foo");
- ne_set_request_flag(req, NE_REQFLAG_EXPECT100, 1);
- ONREQ(ne_request_dispatch(req));
- ne_request_destroy(req);
- return destroy_and_wait(sess);
- }
- struct body {
- char *body;
- size_t size;
- };
- static int want_body(ne_socket *sock, void *userdata)
- {
- struct body *b = userdata;
- char *buf = ne_malloc(b->size);
- clength = 0;
- CALL(discard_request(sock));
- ONN("request has c-l header", clength == 0);
-
- ONN("request length", clength != b->size);
-
- NE_DEBUG(NE_DBG_HTTP,
- "reading body of %" NE_FMT_SIZE_T " bytes...\n", b->size);
-
- ON(ne_sock_fullread(sock, buf, b->size));
-
- ON(SEND_STRING(sock, RESP200 "Content-Length: 0\r\n\r\n"));
- ON(memcmp(buf, b->body, b->size));
-
- ne_free(buf);
- return OK;
- }
- static ssize_t provide_body(void *userdata, char *buf, size_t buflen)
- {
- static const char *pnt;
- static size_t left;
- struct body *b = userdata;
- if (buflen == 0) {
- pnt = b->body;
- left = b->size;
- } else {
- if (left < buflen) buflen = left;
- memcpy(buf, pnt, buflen);
- left -= buflen;
- }
-
- return buflen;
- }
- static int send_bodies(void)
- {
- unsigned int n, m;
- struct body bodies[] = {
- { "abcde", 5 },
- { "\0\0\0\0\0\0", 6 },
- { NULL, 50000 },
- { NULL }
- };
- #define BIG 2
- /* make the body with some cruft. */
- bodies[BIG].body = ne_malloc(bodies[BIG].size);
- for (n = 0; n < bodies[BIG].size; n++) {
- bodies[BIG].body[n] = (char)n%80;
- }
- for (m = 0; m < 2; m++) {
- for (n = 0; bodies[n].body != NULL; n++) {
- ne_session *sess;
- ne_request *req;
-
- CALL(session_server(&sess, want_body, &(bodies[n])));
- req = ne_request_create(sess, "PUT", "/");
- ON(req == NULL);
- if (m == 0) {
- ne_set_request_body_buffer(req, bodies[n].body, bodies[n].size);
- } else {
- ne_set_request_body_provider(req, bodies[n].size,
- provide_body, &bodies[n]);
- }
- ONREQ(ne_request_dispatch(req));
- ne_request_destroy(req);
- CALL(destroy_and_wait(sess));
- }
- }
- ne_free(bodies[BIG].body);
- return OK;
- }
- /* Utility function: run a request using the given server fn, and the
- * request should fail. If 'error' is non-NULL, it must be a substring
- * of the error string. */
- static int fail_request_with_error(int with_body, server_fn fn, void *ud,
- int forever, const char *error)
- {
- ne_session *sess;
- ne_request *req;
- unsigned int port;
- int ret;
- CALL(new_spawn_server(forever ? 100 : 1, fn, ud, &port));
- sess = ne_session_create("http", "localhost", port);
- /* Set default timeout, required by e.g. fail_excess_1xx. */
- ne_set_read_timeout(sess, 2);
- req = ne_request_create(sess, "GET", "/");
- if (with_body) {
- static const char *body = "random stuff";
-
- ne_set_request_body_buffer(req, body, strlen(body));
- }
- /* request should fail. */
- ret = ne_request_dispatch(req);
- ONN("request succeeded", ret == NE_OK);
- if (!forever) {
- /* reap the server, don't care what it's doing. */
- reap_server();
- }
- NE_DEBUG(NE_DBG_HTTP, "Response gave error `%s'\n", ne_get_error(sess));
-
- ONV(error && strstr(ne_get_error(sess), error) == NULL,
- ("failed with error `%s', no `%s'", ne_get_error(sess), error));
- if (!forever)
- ONV(any_request(sess, "/fail/to/connect") != NE_CONNECT,
- ("subsequent request re-used connection?"));
- ne_request_destroy(req);
- ne_session_destroy(sess);
- fail_request_last_port = port;
-
- return OK;
- }
- /* Run a random GET request which is given 'body' as the response; the
- * request must fail, and 'error' must be found in the error
- * string. */
- static int invalid_response_gives_error(const char *resp, const char *error)
- {
- return fail_request_with_error(0, single_serve_string, (void *)resp, 0, error);
- }
- /* Utility function: run a request using the given server fn, and the
- * request must fail. */
- static int fail_request(int with_body, server_fn fn, void *ud, int forever)
- {
- return fail_request_with_error(with_body, fn, ud, forever, NULL);
- }
- static int unbounded_headers(void)
- {
- struct infinite i = { RESP200, "x-foo: bar\r\n" };
- return fail_request(0, serve_infinite, &i, 0);
- }
- static int blank_response(void)
- {
- return fail_request(0, single_serve_string, "\r\n", 0);
- }
- static int serve_non_http(ne_socket *sock, void *ud)
- {
- SEND_STRING(sock, "Hello Mum.\n");
- ne_sock_readline(sock, buffer, BUFSIZ);
- return OK;
- }
- /* Test behaviour when not speaking to an HTTP server. Regression test
- * for infinite loop. */
- static int not_http(void)
- {
- return fail_request(0, serve_non_http, NULL, 0);
- }
- static int unbounded_folding(void)
- {
- struct infinite i = { "HTTP/1.0 200 OK\r\nFoo: bar\r\n",
- " hello there.\r\n" };
- return fail_request(0, serve_infinite, &i, 0);
- }
- static int serve_close(ne_socket *sock, void *ud)
- {
- /* do nothing; the socket will be closed. */
- return 0;
- }
- /* Returns non-zero if port is alive. */
- static int is_alive(int port)
- {
- ne_sock_addr *addr;
- ne_socket *sock = ne_sock_create();
- const ne_inet_addr *ia;
- int connected = 0;
- addr = ne_addr_resolve("localhost", 0);
- for (ia = ne_addr_first(addr); ia && !connected; ia = ne_addr_next(addr))
- connected = ne_sock_connect(sock, ia, port) == 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(fail_request_last_port);
- 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;
- unsigned int port;
- CALL(new_spawn_server(3, serve_close2, &count, &port));
- sess = ne_session_create("http", "localhost", port);
- 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 ne_off_t prog_last = -1, prog_total;
- #define FOFF "%" NE_FMT_NE_OFF_T
- /* callback for send_progress. */
- static void s_progress(void *userdata, ne_off_t prog, ne_off_t total)
- {
- NE_DEBUG(NE_DBG_HTTP,
- "progress callback: " FOFF "/" FOFF ".\n",
- prog, total);
- switch (prog_state) {
- case prog_error:
- case prog_done:
- return;
- case prog_transfer:
- if (total != prog_total) {
- t_context("total unexpected: " FOFF " not " FOFF "", total, prog_total);
- prog_state = prog_error;
- }
- else if (prog > total) {
- t_context("first progress was invalid (" FOFF "/" FOFF ")", prog, total);
- prog_state = prog_error;
- }
- else if (prog_last != -1 && prog_last > prog) {
- t_context("progress went backwards: " FOFF " to " FOFF, prog_last, prog);
- prog_state = prog_error;
- }
- else if (prog_last == prog) {
- t_context("no progress made! " FOFF " to " FOFF, prog_last, prog);
- prog_state = prog_error;
- }
- else if (prog == total) {
- prog_state = prog_done;
- }
- break;
- }
-
- prog_last = prog;
- }
- #undef FOFF
- 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 succeeded?!", 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", 4242, 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", "nonesuch.invalid", 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", 32767, NE_CONNECT);
- }
- /* Test that the origin server hostname is NOT resolved for a proxied
- * request. */
- static int proxy_no_resolve(void)
- {
- ne_session *sess;
- int ret;
-
- CALL(proxied_session_server(&sess, "http", "no.such.server.invalid", 80,
- single_serve_string,
- RESP200 "Content-Length: 0\r\n\r\n"));
-
- ret = any_request(sess, "/foo");
-
- ONN("origin server name resolved when proxy used", ret == NE_LOOKUP);
-
- return destroy_and_wait(sess);
- }
- /* 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);
- ne_close_connection(sess);
- 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_then_abort(ne_socket *sock, void *ud)
- {
- int *flag = ud;
- if (*flag == 1) {
- CALL(single_serve_string(sock,
- RESP200 "Content-Length: 0\r\n\r\n"
- RESP200 TE_CHUNKED "\r\n"
- "zzzzz\r\n"));
- *flag = 0;
- }
- exit(0);
- }
- /* Test that after an aborted request on a persistent 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;
- int flag = 1;
-
- /* 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, serve_then_abort, &flag));
- ONREQ(any_request(sess, "/first"));
- ONN("second request should fail", any_request(sess, "/second") == NE_OK);
- ne_close_connection(sess);
- CALL(await_server());
- /* A third attempt to connect to the server should fail to
- * connect, though this is racy since someone else might come
- * along and steal the port... oh well. */
- ONN("third request was retried",
- any_request(sess, "/third") != NE_CONNECT);
- 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));
- ne_close_connection(sess);
- ONV(strstr(ne_get_error(sess),
- "Could not parse response status line") == NULL,
- ("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");
- }
- #define VALID_ABCDE "abcde\r\n" CHUNK(0, "")
- static int fail_on_invalid(void)
- {
- static const struct {
- const char *resp, *error;
- } ts[] = {
- /* non-chunked TE. */
- { RESP200 "transfer-encoding: punked\r\n" "\r\n" ABCDE_CHUNKS ,
- "Unknown transfer-coding" },
- /* chunk without trailing CRLF */
- { RESP200 TE_CHUNKED "\r\n" "5\r\n" "abcdeFISH",
- "delimiter was invalid" },
- /* chunk with CR then EOF */
- { RESP200 TE_CHUNKED "\r\n" "5\r\n" "abcde\n",
- "not read chunk delimiter" },
- /* chunk with CR then notLF */
- { RESP200 TE_CHUNKED "\r\n" "5\r\n" "abcde\rZZZ",
- "delimiter was invalid" },
- /* chunk with CR then notLF */
- { RESP200 TE_CHUNKED "\r\n" "5\r\n" "abcde\r\r\n",
- "delimiter was invalid" },
- /* chunk with non-hex character */
- { RESP200 TE_CHUNKED "\r\n" "5Z\r\n" "abcde",
- "Could not parse chunk size" },
- /* chunk size overflow */
- { RESP200 TE_CHUNKED "\r\n" "800000000\r\n" "abcde\r\n",
- "Could not parse chunk size" },
- { RESP200 TE_CHUNKED "\r\n" "-8000\r\n" "abcde\r\n",
- "Could not parse chunk size" },
- { RESP200 TE_CHUNKED "\r\n" "0x5\r\n" VALID_ABCDE,
- "Could not parse chunk size" },
- { RESP200 TE_CHUNKED "\r\n" "+5\r\n" VALID_ABCDE,
- "Could not parse chunk size" },
- { RESP200 TE_CHUNKED "\r\n" "5 5\r\n" VALID_ABCDE,
- "Could not parse chunk size" },
- /* LF rather than CRLF in chunk-size is invalid. */
- { RESP200 TE_CHUNKED "\r\n" "5\n" VALID_ABCDE,
- "Invalid chunk-size line" },
- { RESP200 TE_CHUNKED "\r\n" ";5\r\n" VALID_ABCDE,
- "Could not parse chunk size" },
- /* EOF at chunk size */
- { RESP200 TE_CHUNKED "\r\n", "Could not read chunk size" },
- /* negative C-L */
- { RESP200 "Content-Length: -1\r\n" "\r\n" "abcde",
- "Invalid Content-Length" },
- /* invalid C-Ls */
- { RESP200 "Content-Length: 5, 3\r\n" "\r\n" "abcde",
- "Invalid Content-Length" },
- { RESP200 "Content-Length: 5z\r\n" "\r\n" "abcde",
- "Invalid Content-Length" },
- { RESP200 "Content-Length: z5\r\n" "\r\n" "abcde",
- "Invalid Content-Length" },
- /* stupidly-large C-L */
- { RESP200 "Content-Length: 99999999999999999999999999\r\n"
- "\r\n" "abcde",
- "Invalid Content-Length" },
-
- /* incompatible HTTP-version */
- { "HTTP/2.0 200 OK\r\n"
- "Content-Length: 0\r\n\r\n",
- "Incompatible HTTP version" },
- { "HTTP/0.9 200 OK\r\n"
- "Content-Length: 0\r\n\r\n",
- "Incompatible HTTP version" },
- { NULL, NULL }
- };
- unsigned n;
- for (n = 0; ts[n].resp; n++) {
- NE_DEBUG(NE_DBG_HTTP, "-- fail_on_invalid - test %u\n", 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"));
-
- ONREQ(any_request(sess, "/http11"));
-
- ONN("did not detect HTTP/1.1 compliance",
- ne_version_pre_http11(sess) != 0);
-
- ONREQ(any_request(sess, "/http10"));
- ONN("did not detect lack of HTTP/1.1 compliance",
- ne_version_pre_http11(sess) == 0);
- return destroy_and_wait(sess);
- }
- 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;
- ne_uri uri;
- char *u;
- 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;
- ONREQ(any_request(sess, "/foo"));
-
- ONN("first hook never called", args.result == -1);
- if (args.result) return FAIL;
- memset(&uri, 0, sizeof uri);
- ne_fill_server_uri(sess, &uri);
- uri.path = "/bar";
- args.uri = u = ne_uri_unparse(&uri);
- args.result = -1;
- ne_free(uri.host);
- ne_free(uri.scheme);
- /* force use of absoluteURI in request-uri */
- ne_session_proxy(sess, "localhost", 7777);
- ONREQ(any_request(sess, "/bar"));
-
- ONN("second hook never called", args.result == -1);
- if (args.result) return FAIL;
- ne_free(u);
- return destroy_and_wait(sess);
- }
- 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);
- return destroy_and_wait(sess);
- }
- static int abortive_reader(void *userdata, const char *buf, size_t len)
- {
- ne_session *sess = userdata;
- if (len == 5 && strncmp(buf, "abcde", 5) == 0) {
- ne_set_error(sess, "Reader callback failed");
- } else {
- ne_set_error(sess, "Reader callback called with length %" NE_FMT_SIZE_T,
- len);
- }
- return NE_ERROR;
- }
- static int abort_reader(void)
- {
- ne_session *sess;
- ne_request *req;
- int ret;
- CALL(make_session(&sess, single_serve_string,
- RESP200 "Content-Length: 5\r\n\r\n"
- "abcde"
- "HTTP/1.1 200 OK\r\n"
- "Content-Length: 0\r\n\r\n"));
- req = ne_request_create(sess, "GET", "/foo");
- ne_add_response_body_reader(req, ne_accept_2xx, abortive_reader, sess);
- ret = ne_request_dispatch(req);
- ONV(ret != NE_ERROR, ("request did not fail with NE_ERROR: %d", ret));
- ONV(strcmp(ne_get_error(sess), "Reader callback failed") != 0,
- ("unexpected session error string: %s", ne_get_error(sess)));
- ne_request_destroy(req);
- /* test that the connection was closed. */
- ONN("connection not closed after aborted response",
- any_2xx_request(sess, "/failmeplease") == OK);
- return destroy_and_wait(sess);
- }
- /* attempt and fail to send request from offset 500 of /dev/null. */
- static int send_bad_offset(void)
- {
- ne_session *sess;
- ne_request *req;
- int ret, fds[2];
- CALL(make_session(&sess, single_serve_string,
- RESP200 "Content-Length: 0\r\n" "\r\n"));
- /* create a pipe, on which seek is guaranteed to fail. */
- ONN("could not create pipe", pipe(fds) != 0);
- req = ne_request_create(sess, "PUT", "/null");
- ne_set_request_body_fd(req, fds[0], 500, 5);
-
- ret = ne_request_dispatch(req);
- close(fds[0]);
- close(fds[1]);
- ONN("request dispatched with bad offset!", ret == NE_OK);
- ONV(ret != NE_ERROR,
- ("request failed with unexpected error code %d: %s",
- ret, ne_get_error(sess)));
- ONV(strstr(ne_get_error(sess), "Could not seek") == NULL,
- ("bad error message from seek failure: %s", ne_get_error(sess)));
- reap_server();
- ne_request_destroy(req);
- ne_session_destroy(sess);
- return OK;
- }
- static void thook_create_req(ne_request *req, void *userdata,
- const char *method, const char *requri)
- {
- ne_buffer *buf = userdata;
- ne_buffer_concat(buf, "(create,", method, ",", requri, ")\n", NULL);
- }
- static void hook_pre_send(ne_request *req, void *userdata,
- ne_buffer *header)
- {
- ne_buffer *buf = userdata;
- ne_buffer_czappend(buf, "(pre-send)\n");
- }
- /* Returns a static string giving a comma-separated representation of
- * the status structure passed in. */
- static char *status_to_string(const ne_status *status)
- {
- static char sbuf[128];
- ne_snprintf(sbuf, sizeof sbuf, "HTTP/%d.%d,%d,%s",
- status->major_version, status->minor_version,
- status->code, status->reason_phrase);
- return sbuf;
- }
- static void hook_post_headers(ne_request *req, void *userdata,
- const ne_status *status)
- {
- ne_buffer *buf = userdata;
- ne_buffer_concat(buf, "(post-headers,", status_to_string(status), ")\n",
- NULL);
- }
- static int hook_post_send(ne_request *req, void *userdata,
- const ne_status *status)
- {
- ne_buffer *buf = userdata;
- ne_buffer_concat(buf, "(post-send,", status_to_string(status), ")\n",
- NULL);
- return NE_OK;
- }
- static void hook_destroy_req(ne_request *req, void *userdata)
- {
- ne_buffer *buf = userdata;
- ne_buffer_czappend(buf, "(destroy-req)\n");
- }
- static void hook_destroy_sess(void *userdata)
- {
- ne_buffer *buf = userdata;
- ne_buffer_czappend(buf, "(destroy-sess)\n");
- }
- static void hook_close_conn(void *userdata)
- {
- ne_buffer *buf = userdata;
- ne_buffer_czappend(buf, "(close-conn)\n");
- }
- static int hooks(void)
- {
- ne_buffer *buf = ne_buffer_create();
- ne_session *sess;
- struct many_serve_args args;
- args.str = RESP200 "Content-Length: 0\r\n" "\r\n";
- args.count = 3;
- CALL(make_session(&sess, many_serve_string, &args));
- ne_hook_create_request(sess, thook_create_req, buf);
- ne_hook_pre_send(sess, hook_pre_send, buf);
- ne_hook_post_headers(sess, hook_post_headers, buf);
- ne_hook_post_send(sess, hook_post_send, buf);
- ne_hook_destroy_request(sess, hook_destroy_req, buf);
- ne_hook_destroy_session(sess, hook_destroy_sess, buf);
- ne_hook_close_conn(sess, hook_close_conn, buf);
- CALL(any_2xx_request(sess, "/first"));
- ONCMP("(create,GET,/first)\n"
- "(pre-send)\n"
- "(post-headers,HTTP/1.1,200,OK)\n"
- "(post-send,HTTP/1.1,200,OK)\n"
- "(destroy-req)\n", buf->data, "hook ordering", "first result");
- ne_buffer_clear(buf);
- /* Unhook for mismatched fn/ud pointers: */
- ne_unhook_create_request(sess, hk_createreq, buf);
- ne_unhook_create_request(sess, thook_create_req, sess);
- /* Unhook real functions. */
- ne_unhook_pre_send(sess, hook_pre_send, buf);
- ne_unhook_destroy_request(sess, hook_destroy_req, buf);
- ne_unhook_post_headers(sess, hook_post_headers, buf);
- CALL(any_2xx_request(sess, "/second"));
- ONCMP("(create,GET,/second)\n"
- "(post-send,HTTP/1.1,200,OK)\n",
- buf->data, "hook ordering", "second result");
- ne_buffer_clear(buf);
- /* Double hook create, double hook then double unhook post. */
- ne_hook_create_request(sess, thook_create_req, buf);
- ne_hook_post_send(sess, hook_post_send, buf);
- ne_unhook_post_send(sess, hook_post_send, buf);
- ne_unhook_post_send(sess, hook_post_send, buf);
- CALL(any_2xx_request(sess, "/third"));
- ONCMP("(create,GET,/third)\n"
- "(create,GET,/third)\n",
- buf->data, "hook ordering", "third result");
- ne_buffer_clear(buf);
- ne_session_destroy(sess);
- CALL(await_server());
- ONCMP("(destroy-sess)\n"
- "(close-conn)\n", buf->data, "hook ordering", "first destroyed session");
- ne_buffer_clear(buf);
- sess = ne_session_create("http", "www.example.com", 80);
- ne_hook_destroy_session(sess, hook_destroy_sess, buf);
- ne_unhook_destroy_session(sess, hook_destroy_sess, buf);
- ne_session_destroy(sess);
- ONCMP("", buf->data, "hook ordering", "second destroyed session");
- ne_buffer_destroy(buf);
- return OK;
- }
- static void hook_self_destroy_req(ne_request *req, void *userdata)
- {
- ne_unhook_destroy_request(ne_get_session(req),
- hook_self_destroy_req, userdata);
- }
- /* Test that it's safe to call ne_unhook_destroy_request from a
- * destroy_request hook. */
- static int hook_self_destroy(void)
- {
- ne_session *sess = ne_session_create("http", "localhost", 1234);
-
- ne_hook_destroy_request(sess, hook_self_destroy_req, NULL);
- ne_request_destroy(ne_request_create(sess, "GET", "/"));
- ne_session_destroy(sess);
- return OK;
- }
- static int icy_protocol(void)
- {
- ne_session *sess;
-
- CALL(make_session(&sess, single_serve_string,
- "ICY 200 OK\r\n"
- "Content-Length: 0\r\n\r\n"));
- ne_set_session_flag(sess, NE_SESSFLAG_ICYPROTO, 1);
-
- ONREQ(any_request(sess, "/foo"));
- return destroy_and_wait(sess);
- }
- static void status_cb(void *userdata, ne_session_status status,
- const ne_session_status_info *info)
- {
- ne_buffer *buf = userdata;
- char scratch[512];
- switch (status) {
- case ne_status_lookup:
- ne_buffer_concat(buf, "lookup(", info->lu.hostname, ")-", NULL);
- break;
- case ne_status_connecting:
- ne_iaddr_print(info->ci.address, scratch, sizeof scratch);
- ne_buffer_concat(buf, "connecting(", info->lu.hostname,
- ",", scratch, ")-", NULL);
- break;
- case ne_status_disconnected:
- ne_buffer_czappend(buf, "dis");
- /* fallthrough */
- case ne_status_connected:
- ne_buffer_concat(buf, "connected(", info->cd.hostname,
- ")-", NULL);
- break;
- case ne_status_sending:
- case ne_status_recving:
- ne_snprintf(scratch, sizeof scratch,
- "%" NE_FMT_NE_OFF_T ",%" NE_FMT_NE_OFF_T,
- info->sr.progress, info->sr.total);
- ne_buffer_concat(buf,
- status == ne_status_sending ? "send" : "recv",
- "(", scratch, ")-", NULL);
- break;
- default:
- ne_buffer_czappend(buf, "bork!");
- break;
- }
- }
- static int status(void)
- {
- ne_session *sess;
- ne_buffer *buf = ne_buffer_create();
- const char *host, *addr = get_lh_addr();
- char expect[1024];
- CALL(make_session(&sess, single_serve_string, RESP200
- "Content-Length: 5\r\n\r\n" "abcde"));
- host = get_session_host();
- ne_snprintf(expect, sizeof expect,
- "connecting(%s,%s)-"
- "connected(%s)-"
- "send(0,5000)-"
- "send(5000,5000)-"
- "recv(0,5)-"
- "recv(5,5)-"
- "disconnected(%s)-",
- host, addr, host, host);
- ne_set_notifier(sess, status_cb, buf);
- CALL(any_2xx_request_body(sess, "/status"));
- CALL(destroy_and_wait(sess));
- ONV(strcmp(expect, buf->data),
- ("status event sequence mismatch: got [%s] not [%s]",
- buf->data, expect));
-
- ne_buffer_destroy(buf);
-
- return OK;
- }
- static int status_chunked(void)
- {
- ne_session *sess;
- ne_buffer *buf = ne_buffer_create();
- const char *host, *addr = get_lh_addr();
- char expect[1024];
- CALL(make_session(&sess, single_serve_string,
- RESP200 TE_CHUNKED "\r\n" ABCDE_CHUNKS));
- host = get_session_host();
- /* This sequence is not exactly guaranteed by the API, but it's
- * what the current implementation should do. */
- ne_snprintf(expect, sizeof expect,
- "connecting(%s,%s)-"
- "connected(%s)-"
- "send(0,5000)-"
- "send(5000,5000)-"
- "recv(0,-1)-"
- "recv(1,-1)-"
- "recv(2,-1)-"
- "recv(3,-1)-"
- "recv(4,-1)-"
- "recv(5,-1)-"
- "disconnected(%s)-",
- host, addr, host, host);
- ne_set_notifier(sess, status_cb, buf);
- CALL(any_2xx_request_body(sess, "/status"));
- CALL(destroy_and_wait(sess));
-
- ONV(strcmp(expect, buf->data),
- ("status event sequence mismatch: got [%s] not [%s]",
- buf->data, expect));
- ne_buffer_destroy(buf);
-
- return OK;
- }
- static int local_addr(void)
- {
- ne_session *sess;
- ne_inet_addr *ia = get_lh_inet_addr();
- CALL(make_session(&sess, single_serve_string, RESP200
- "Connection: close\r\n\r\n"));
- ne_set_localaddr(sess, ia);
- ONREQ(any_request(sess, "/foo"));
- ne_session_destroy(sess);
- ne_iaddr_free(ia);
- return reap_server();
- }
- /* Regression in 0.27.0, ne_set_progress(sess, NULL, NULL) should
- * register the progress callback. */
- static int dereg_progress(void)
- {
- ne_session *sess;
- CALL(make_session(&sess, single_serve_string,
- RESP200 TE_CHUNKED "\r\n" ABCDE_CHUNKS));
- ne_set_progress(sess, NULL, NULL);
- ONREQ(any_request(sess, "/foo"));
-
- return destroy_and_wait(sess);
- }
- static int addrlist(void)
- {
- ne_session *sess;
- ne_inet_addr *ia = get_lh_inet_addr();
- const ne_inet_addr *ial[1];
- unsigned int port;
- CALL(new_spawn_server(1, single_serve_string, EMPTY_RESP, &port));
- sess = ne_session_create("http", "www.example.com", port);
- ial[0] = ia;
- ne_set_addrlist(sess, ial, 1);
- CALL(any_2xx_request(sess, "/blah"));
- ne_iaddr_free(ia);
-
- return destroy_and_wait(sess);
- }
- static int socks_session(ne_session **sess, struct socks_server *srv,
- const char *hostname, unsigned int port,
- server_fn server, void *userdata)
- {
- unsigned int realport;
- srv->server = server;
- srv->userdata = userdata;
-
- CALL(new_spawn_server(1, socks_server, srv, &realport));
- *sess = ne_session_create("http", hostname, port);
- ne_session_socks_proxy(*sess, srv->version, "localhost", realport,
- srv->username, srv->password);
- return OK;
- }
- static int socks_proxy(void)
- {
- ne_session *sess;
- struct socks_server srv = {0};
- srv.version = NE_SOCK_SOCKSV5;
- srv.failure = fail_none;
- srv.expect_port = 4242;
- srv.expect_addr = NULL;
- srv.expect_fqdn = "socks.example.com";
- srv.username = "bloggs";
- srv.password = "guessme";
-
- CALL(socks_session(&sess, &srv, srv.expect_fqdn, srv.expect_port,
- single_serve_string, EMPTY_RESP));
- CALL(any_2xx_request(sess, "/blee"));
- return destroy_and_wait(sess);
- }
- static int socks_v4_proxy(void)
- {
- ne_session *sess;
- struct socks_server srv = {0};
- srv.version = NE_SOCK_SOCKSV4;
- srv.failure = fail_none;
- srv.expect_port = 4242;
- srv.expect_addr = ne_iaddr_parse("127.0.0.1", ne_iaddr_ipv4);
- srv.expect_fqdn = "localhost";
- srv.username = "bloggs";
- srv.password = "guessme";
-
- CALL(socks_session(&sess, &srv, srv.expect_fqdn, srv.expect_port,
- single_serve_string, EMPTY_RESP));
- CALL(any_2xx_request(sess, "/blee"));
- ne_iaddr_free(srv.expect_addr);
- return destroy_and_wait(sess);
- }
- /* Server function which serves the request body back as the response
- * body. */
- static int serve_mirror(ne_socket *sock, void *userdata)
- {
- char response[1024];
- CALL(discard_request(sock));
- ONV(clength == 0 || clength > sizeof buffer,
- ("C-L out of bounds: %lu", clength));
- ONV(ne_sock_fullread(sock, buffer, clength),
- ("read failed: %s", ne_sock_error(sock)));
-
- ne_snprintf(response, sizeof response,
- "HTTP/1.0 200 OK\r\n"
- "Content-Length: %lu\r\n"
- "\r\n", clength);
- ONN("send response header failed",
- server_send(sock, response, strlen(response)));
-
- ONN("send response body failed",
- server_send(sock, buffer, clength));
- ONV(ne_sock_read(sock, buffer, 1) != NE_SOCK_CLOSED,
- ("client sent data after request: %c", buffer[0]));
- return OK;
- }
- /* Test for ne_set_request_body_fd() bug in <= 0.29.3. */
- static int send_length(void)
- {
- ne_session *sess;
- ne_request *req;
- int fd;
- ne_buffer *buf = ne_buffer_create();
- fd = open("foobar.txt", O_RDONLY);
- ONV(fd < 0, ("open random.txt failed: %s", strerror(errno)));
- CALL(make_session(&sess, serve_mirror, NULL));
- req = ne_request_create(sess, "GET", "/foo");
-
- ne_set_request_body_fd(req, fd, 0, 3);
- ne_add_response_body_reader(req, ne_accept_2xx, collector, buf);
- ONREQ(ne_request_dispatch(req));
- ONCMP("foo", buf->data, "response body", "match");
- ne_request_destroy(req);
- ne_buffer_destroy(buf);
- close(fd);
- return destroy_and_wait(sess);
- }
- /* Test for error code for a SOCKS proxy failure, bug in <= 0.29.3. */
- static int socks_fail(void)
- {
- ne_session *sess;
- struct socks_server srv = {0};
- int ret;
- srv.version = NE_SOCK_SOCKSV5;
- srv.failure = fail_init_vers;
- srv.expect_port = 4242;
- srv.expect_addr = ne_iaddr_parse("127.0.0.1", ne_iaddr_ipv4);
- srv.expect_fqdn = "localhost";
- srv.username = "bloggs";
- srv.password = "guessme";
-
- CALL(socks_session(&sess, &srv, srv.expect_fqdn, srv.expect_port,
- single_serve_string, EMPTY_RESP));
- ret = any_request(sess, "/blee");
- ONV(ret != NE_ERROR,
- ("request failed with %d not NE_ERROR", ret));
- ONV(strstr(ne_get_error(sess),
- "Could not establish connection from SOCKS proxy") == NULL
- || strstr(ne_get_error(sess),
- "Invalid version in proxy response") == NULL,
- ("unexpected error string: %s", ne_get_error(sess)));
- ne_iaddr_free(srv.expect_addr);
- return destroy_and_wait(sess);
- }
- static int safe_flags(void)
- {
- ne_session *sess = ne_session_create("http", "localhost", 80);
- ne_request *req = ne_request_create(sess, "GET", "/");
- ne_set_request_flag(req, NE_REQFLAG_LAST, 0xAAAAAAAA);
- ONN("flags array bound check failed", ne_get_session(req) != sess);
- ne_request_destroy(req);
- ne_session_destroy(sess);
- return OK;
- }
- /* Hit the timeout (2 seconds) for reading interim responses. */
- static int fail_excess_1xx(void)
- {
- struct s1xx_args args = {20000000, 0};
- return fail_request_with_error(0, serve_1xx, &args, 0,
- "Timed out reading interim responses");
- }
- static int serve_check_reqline(ne_socket *sock, void *userdata)
- {
- const char *expect = userdata;
- ONN("failed to read request-line",
- ne_sock_readline(sock, buffer, BUFSIZ) <= 0);
- NE_DEBUG(NE_DBG_HTTP, "child: got request-line %s\n", buffer);
- if (strcmp(expect, buffer) != 0)
- return single_serve_string(sock, "HTTP/1.1 400 Bad Request-Line\r\n" EMPTY_RESP);
- else
- return single_serve_string(sock, RESP200 "Connection: close\r\n\r\n");
- }
- /* Test that various request target forms are allowed. */
- static int target_forms(void)
- {
- static const struct {
- const char *uri, *reqline;
- } ts[] = {
- { "/foo", "GET /foo HTTP/1.1\r\n" },
- { "/foo?bar", "GET /foo?bar HTTP/1.1\r\n" },
- { "ftp://www.example.com/foo", "GET ftp://www.example.com/foo HTTP/1.1\r\n" },
- { "*", "GET * HTTP/1.1\r\n" },
- { NULL, NULL }
- };
- unsigned n;
- for (n = 0; ts[n].uri; n++) {
- ne_session *sess;
- CALL(make_session(&sess, serve_check_reqline, (void *)ts[n].reqline));
- ONREQ(any_2xx_request(sess, ts[n].uri));
- CALL(destroy_and_wait(sess));
- }
- return OK;
- }
- /* Interim response callback. */
- static void test_interim(void *userdata, ne_request *req, const ne_status *st)
- {
- ne_buffer *buf = userdata;
- const char *val;
- ne_buffer_snprintf(buf, 15, "[code=%d]", st->code);
- val = ne_get_response_header(req, "X-Interim");
- ne_buffer_concat(buf, "[hdr=", val ? val : "null", "]", NULL);
- }
- /* Test for interim response headers. */
- static int interims(void)
- {
- ne_session *sess;
- ne_request *req;
- ne_buffer *buf = ne_buffer_create();
- CALL(make_session(&sess, single_serve_string,
- "HTTP/1.1 103 Early Stuff\r\n"
- "X-Interim: foobar\r\n" "\r\n"
- "HTTP/1.1 102 Stuff Is Happening\r\n"
- "X-Interim: bar, foo\r\n" "\r\n"
- "HTTP/1.1 200 Thank you kindly\r\n"
- "Content-Length: 0\r\n"
- "X-Not-Interim: finally\r\n" "\r\n"));
- req = ne_request_create(sess, "GET", "/");
- ne_add_interim_handler(req, test_interim, buf);
- ONREQ(ne_request_dispatch(req));
- ONN("interim header leaked to final response",
- ne_get_response_header(req, "X-Interim") != NULL);
- ne_request_destroy(req);
- CALL(destroy_and_wait(sess));
- ONCMPN("[code=103][hdr=foobar][code=102][hdr=bar, foo]", buf->data,
- "interim responses", "trace");
- ne_buffer_destroy(buf);
- return OK;
- }
- static int retry_408(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(multi_session_server(&sess, "http", "localhost",
- 2, single_serve_string,
- EMPTY_RESP
- "HTTP/1.1 408 Request Timeout\r\n"
- "Server: neon-test-server\r\n"
- "Content-Length: 0\r\n\r\n"));
- CALL(any_2xx_request(sess, "/first408"));
- CALL(any_2xx_request(sess, "/second408"));
- return destroy_and_wait(sess);
- }
- /* Ensure that only a 408 on a persisted connection is
- * retried. Otherwise it should just be handled as a final
- * response. */
- static int dont_retry_408(void)
- {
- ne_session *sess;
- CALL(make_session(&sess, single_serve_string,
- "HTTP/1.1 408 Request Timeout\r\n"
- "Server: neon-test-server\r\n"
- "Content-Length: 0\r\n\r\n"));
- /* Run any request, ensure it gets a 408 response. */
- CALL(run_request(sess, 408, construct_get, NULL));
- return destroy_and_wait(sess);
- }
- static const char *ipv6_check_result = "500 Something Went Wrong";
- #define V6_EXAMPLE_HOST "[2001:db8::cafe]"
- static void ipv6_host_checker(char *value)
- {
- NE_DEBUG(NE_DBG_HTTP, "child: header value [%s]\n", value);
- if (strcmp(value, V6_EXAMPLE_HOST) == 0)
- ipv6_check_result = "200 Good Header";
- else
- ipv6_check_result = "400 Bad Header";
- }
- /* Server which acts as a proxy accepting a CONNECT request. */
- static int serve_v6_check(ne_socket *sock, void *ud)
- {
- char resp[512];
- got_header = ipv6_host_checker;
- want_header = "Host";
- CALL(discard_request(sock));
- ne_snprintf(resp, sizeof resp, "HTTP/1.1 %s\r\n"
- "Content-Length: 0\r\n"
- "Server: serve_v6_check\r\n\r\n",
- ipv6_check_result);
- SEND_STRING(sock, resp);
- return 0;
- }
- static int ipv6_literal(void)
- {
- ne_session *sess;
- CALL(fakeproxied_session_server(&sess, "http", V6_EXAMPLE_HOST,
- 80, serve_v6_check, NULL));
- CALL(any_2xx_request(sess, "/ipv6ish"));
- return destroy_and_wait(sess);
- }
- static int targets(void)
- {
- struct {
- const char *scheme;
- const char *host;
- int port;
- const char *method;
- const char *target;
- const char *expected;
- } ts[] = {
- { "http", "example.com", 80, "GET", "/fish", "http://example.com/fish" },
- { "http", "example.com", 8080, "GET", "/fish", "http://example.com:8080/fish" },
- { "https", "example.com", 443, "GET", "/", "https://example.com/" },
- { "http", "proxy.example.com", 80, "GET", "ftp://example.com/fishfood", "ftp://example.com/fishfood" },
- { "https", "example.com", 443, "OPTIONS", "*", "https://example.com" },
- { NULL }
- };
- unsigned n;
- for (n = 0; ts[n].scheme != NULL; n++ ) {
- const ne_uri *uri, *uri2;
- ne_session *sess;
- ne_request *req;
- char *actual;
- sess = ne_session_create(ts[n].scheme, ts[n].host, ts[n].port);
- req = ne_request_create(sess, ts[n].method, ts[n].target);
- uri = ne_get_request_target(req);
- uri2 = ne_get_request_target(req);
- actual = uri ? ne_uri_unparse(uri) : NULL;
- ONCMP(ts[n].expected, actual, "request target", "URI");
- ONN("caching failed, different rv on second call", uri != uri2);
- if (actual) ne_free(actual);
- ne_request_destroy(req);
- ne_session_destroy(sess);
- }
- return OK;
- }
- /* TODO: test that ne_set_notifier(, NULL, NULL) DTRT too. */
- ne_test tests[] = {
- T(lookup_localhost),
- T(response_bodies),
- T(no_body_204),
- T(no_body_304),
- T(no_body_HEAD),
- T(reason_phrase),
- T(chunk_oversize),
- T(no_body_chunks),
- T(persist_http11),
- T(persist_chunked),
- T(persist_http10),
- T(persist_proxy_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(response_headers),
- T(reset_headers),
- T(iterate_none),
- T(iterate_many),
- T(skip_interim_1xx),
- T(skip_many_1xx),
- T(skip_1xx_hdrs),
- T(send_bodies),
- T(expect_100_once),
- T(expect_100_nobody),
- 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_on_invalid),
- T(read_timeout),
- 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(abort_reader),
- T(send_bad_offset),
- T(hooks),
- T(hook_self_destroy),
- T(icy_protocol),
- T(status),
- T(status_chunked),
- T(local_addr),
- T(dereg_progress),
- T(addrlist),
- T(socks_proxy),
- T(socks_v4_proxy),
- T(send_length),
- T(socks_fail),
- T(fail_lookup),
- T(fail_double_lookup),
- T(safe_flags),
- T(fail_excess_1xx),
- T(target_forms),
- T(interims),
- T(retry_408),
- T(dont_retry_408),
- T(ipv6_literal),
- #if 0
- T(redirect_error),
- #endif
- T(targets),
- T(NULL)
- };
|