compress.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. /*
  2. tests for compressed response handling.
  3. Copyright (C) 2001-2008, Joe Orton <[email protected]>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. */
  16. #include "config.h"
  17. #ifdef HAVE_UNISTD_H
  18. #include <unistd.h>
  19. #endif
  20. #include "ne_compress.h"
  21. #include "ne_auth.h"
  22. #include "tests.h"
  23. #include "child.h"
  24. #include "utils.h"
  25. static enum { f_partial = 0, f_mismatch, f_complete } failed;
  26. static const char newsfn[] = "random.txt", hellofn[] = "hello.txt";
  27. static int init(void)
  28. {
  29. return lookup_localhost();
  30. }
  31. #define EXTRA_DEBUG 0 /* disabled by default */
  32. static int reader(void *ud, const char *block, size_t len)
  33. {
  34. struct string *b = ud;
  35. #if EXTRA_DEBUG
  36. NE_DEBUG(NE_DBG_HTTP, "reader: got (%d): [[[%.*s]]]\n", (int)len,
  37. (int)len, block);
  38. #endif
  39. if (failed == f_mismatch) return -1;
  40. /* catch multiple len == 0 call as issued by 0.25.0 only: */
  41. if (failed == f_complete) {
  42. NE_DEBUG(NE_DBG_HTTP, "reader: called after complete, len=%d\n",
  43. (int)len);
  44. failed = f_mismatch;
  45. return -1;
  46. }
  47. if (failed == f_partial && len == 0) {
  48. if (b->len != 0) {
  49. NE_DEBUG(NE_DBG_HTTP, "reader: got length %d at EOF\n",
  50. (int)b->len);
  51. failed = f_mismatch;
  52. } else {
  53. failed = f_complete;
  54. }
  55. return 0;
  56. }
  57. if (len > b->len || memcmp(b->data, block, len) != 0) {
  58. NE_DEBUG(NE_DBG_HTTP, "reader: failed, got [[%.*s]] not [[%.*s]]\n",
  59. (int)len, block, (int)b->len, b->data);
  60. failed = f_mismatch;
  61. return -1;
  62. } else {
  63. b->data += len;
  64. b->len -= len;
  65. #if EXTRA_DEBUG
  66. NE_DEBUG(NE_DBG_HTTP, "reader: OK, %d bytes remaining\n",
  67. (int)b->len);
  68. #endif
  69. }
  70. return 0;
  71. }
  72. static int do_fetch(const char *realfn, const char *gzipfn,
  73. int chunked, int expect_fail)
  74. {
  75. ne_session *sess;
  76. ne_request *req;
  77. int ret;
  78. ne_buffer *buf = ne_buffer_create();
  79. struct serve_file_args sfargs;
  80. ne_decompress *dc;
  81. struct string body;
  82. CALL(file_to_buffer(realfn, buf));
  83. body.data = buf->data;
  84. body.len = buf->used - 1;
  85. failed = f_partial;
  86. if (gzipfn) {
  87. sfargs.fname = gzipfn;
  88. sfargs.headers = "Content-Encoding: gzip\r\n";
  89. } else {
  90. sfargs.fname = realfn;
  91. sfargs.headers = NULL;
  92. }
  93. sfargs.chunks = chunked;
  94. CALL(make_session(&sess, serve_file, &sfargs));
  95. req = ne_request_create(sess, "GET", "/");
  96. dc = ne_decompress_reader(req, ne_accept_2xx, reader, &body);
  97. #ifdef NE_DEBUGGING
  98. ne_debug_init(ne_debug_stream, ne_debug_mask & ~NE_DBG_HTTPBODY);
  99. #endif
  100. ret = ne_request_dispatch(req);
  101. #ifdef NE_DEBUGGING
  102. ne_debug_init(ne_debug_stream, ne_debug_mask | NE_DBG_HTTPBODY);
  103. #endif
  104. ONN("file not served", ne_get_status(req)->code != 200);
  105. ONN("decompress succeeded", expect_fail && !ret);
  106. ONV(!expect_fail && ret, ("decompress failed: %s", ne_get_error(sess)));
  107. NE_DEBUG(NE_DBG_HTTP, "session error: %s\n", ne_get_error(sess));
  108. ne_decompress_destroy(dc);
  109. ne_request_destroy(req);
  110. ne_session_destroy(sess);
  111. ne_buffer_destroy(buf);
  112. if (expect_fail) {
  113. /* if the decompress callback fails, the connection may
  114. * be aborted and hence the server will abort. */
  115. reap_server();
  116. } else {
  117. CALL(await_server());
  118. }
  119. if (!expect_fail) {
  120. ONN("inflated response truncated", failed == f_partial);
  121. ONN("inflated response mismatch", failed == f_mismatch);
  122. }
  123. return OK;
  124. }
  125. static int fetch(const char *realfn, const char *gzipfn, int chunked)
  126. {
  127. return do_fetch(realfn, gzipfn, chunked, 0);
  128. }
  129. /* Test the no-compression case. */
  130. static int not_compressed(void)
  131. {
  132. return fetch(newsfn, NULL, 0);
  133. }
  134. static int simple(void)
  135. {
  136. return fetch(newsfn, "file1.gz", 0);
  137. }
  138. /* Triggers -fsanitizer=shift. */
  139. static int hello(void)
  140. {
  141. return fetch(hellofn, "hello.gz", 0);
  142. }
  143. /* file1.gz has an embedded filename. */
  144. static int withname(void)
  145. {
  146. return fetch(newsfn, "file2.gz", 0);
  147. }
  148. /* deliver various different sizes of chunks: tests the various
  149. * decoding cases. */
  150. static int chunked_1b_wn(void)
  151. {
  152. return fetch(newsfn, "file2.gz", 1);
  153. }
  154. static int chunked_1b(void)
  155. {
  156. return fetch(newsfn, "file1.gz", 1);
  157. }
  158. static int chunked_12b(void)
  159. {
  160. return fetch(newsfn, "file2.gz", 12);
  161. }
  162. static int chunked_20b(void)
  163. {
  164. return fetch(newsfn, "file2.gz", 20);
  165. }
  166. static int chunked_10b(void)
  167. {
  168. return fetch(newsfn, "file1.gz", 10);
  169. }
  170. static int chunked_10b_wn(void)
  171. {
  172. return fetch(newsfn, "file2.gz", 10);
  173. }
  174. static int fail_trailing(void)
  175. {
  176. return do_fetch(newsfn, "trailing.gz", 0, 1);
  177. }
  178. static int fail_trailing_1b(void)
  179. {
  180. return do_fetch(newsfn, "trailing.gz", 1, 1);
  181. }
  182. static int fail_truncate(void)
  183. {
  184. return do_fetch(newsfn, "truncated.gz", 0, 1);
  185. }
  186. static int fail_bad_csum(void)
  187. {
  188. return do_fetch(newsfn, "badcsum.gz", 0, 1);
  189. }
  190. static int fail_corrupt1(void)
  191. {
  192. return do_fetch(newsfn, "corrupt1.gz", 0, 1);
  193. }
  194. static int fail_corrupt2(void)
  195. {
  196. return do_fetch(newsfn, "corrupt2.gz", 0, 1);
  197. }
  198. static int fail_empty(void)
  199. {
  200. return do_fetch(newsfn, "empty.gz", 0, 1);
  201. }
  202. static int notcomp_empty(void)
  203. {
  204. return fetch("empty.gz", NULL, 0);
  205. }
  206. static int auth_cb(void *userdata, const char *realm, int tries,
  207. char *un, char *pw)
  208. {
  209. strcpy(un, "foo");
  210. strcpy(pw, "bar");
  211. return tries;
  212. }
  213. static int retry_compress_helper(ne_accept_response acceptor,
  214. struct double_serve_args *args,
  215. struct string *expect)
  216. {
  217. ne_session *sess;
  218. ne_request *req;
  219. ne_decompress *dc;
  220. CALL(make_session(&sess, double_serve_sstring, args));
  221. ne_set_server_auth(sess, auth_cb, NULL);
  222. req = ne_request_create(sess, "GET", "/");
  223. dc = ne_decompress_reader(req, acceptor, reader, expect);
  224. failed = f_partial;
  225. ONREQ(ne_request_dispatch(req));
  226. ne_decompress_destroy(dc);
  227. ONN("got bad response body", failed != f_complete);
  228. ne_request_destroy(req);
  229. return destroy_and_wait(sess);
  230. }
  231. #define SSTRING(x) { x, sizeof(x) - 1 }
  232. static struct double_serve_args retry_gz_args = {
  233. SSTRING("HTTP/1.1 401 Get Away\r\n"
  234. "Content-Encoding: gzip\r\n"
  235. "WWW-Authenticate: Basic realm=WallyWorld\r\n"
  236. "Content-Length: 5\r\n"
  237. "\r\n"
  238. "abcde"),
  239. SSTRING("HTTP/1.1 200 OK\r\n"
  240. "Server: foo\r\n"
  241. "Content-Length: 5\r\n"
  242. "Connection: close\r\n"
  243. "\r\n"
  244. "hello")
  245. };
  246. /* Test where the response to the retried request does *not* have
  247. * a content-encoding, whereas the original 401 response did. */
  248. static int retry_notcompress(void)
  249. {
  250. struct string expect = { "hello", 5 };
  251. return retry_compress_helper(ne_accept_2xx, &retry_gz_args, &expect);
  252. }
  253. static struct double_serve_args retry_gz_args2 = {
  254. SSTRING("HTTP/1.1 401 Get Away\r\n"
  255. "Content-Encoding: gzip\r\n"
  256. "WWW-Authenticate: Basic realm=WallyWorld\r\n"
  257. "Content-Length: 25\r\n"
  258. "\r\n"
  259. "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xcb\x48\xcd\xc9\xc9\x07"
  260. "\x00\x86\xa6\x10\x36\x05\x00\x00\x00"),
  261. SSTRING("HTTP/1.1 200 OK\r\n"
  262. "Server: foo\r\n"
  263. "Content-Encoding: gzip\r\n"
  264. "Content-Length: 25\r\n"
  265. "Connection: close\r\n"
  266. "\r\n"
  267. "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\x2b\xcf\x2f\xca\x49\x01"
  268. "\x00\x43\x11\x77\x3a\x05\x00\x00\x00")
  269. };
  270. static int retry_accept(void *ud, ne_request *req, const ne_status *st)
  271. {
  272. struct string *expect = ud;
  273. NE_DEBUG(NE_DBG_HTTP, "retry_accept callback for %d response\n",
  274. st->code);
  275. if (expect->len == 4 && strcmp(expect->data, "fish") == 0) {
  276. /* first time through */
  277. expect->data = "hello";
  278. } else {
  279. expect->data = "world";
  280. }
  281. expect->len = 5;
  282. failed = f_partial; /* reset the state */
  283. return 1;
  284. }
  285. /* Test where the response to the retried request *does* have a
  286. * content-encoding, as did the original 401 response. */
  287. static int retry_compress(void)
  288. {
  289. struct string expect = { "fish", 4 };
  290. return retry_compress_helper(retry_accept, &retry_gz_args2, &expect);
  291. }
  292. #define READER_ABORT_ERR "reader_abort error string"
  293. static int reader_abort(void *ud, const char *buf, size_t len)
  294. {
  295. ne_session *sess = ud;
  296. ne_set_error(sess, READER_ABORT_ERR);
  297. return len;
  298. }
  299. /* check that a callback abort does abort the response */
  300. static int compress_abort(void)
  301. {
  302. ne_session *sess;
  303. ne_request *req;
  304. struct serve_file_args sfargs;
  305. ne_decompress *dc;
  306. int ret;
  307. sfargs.fname = "file1.gz";
  308. sfargs.headers = "Content-Encoding: gzip\r\n";
  309. sfargs.chunks = 0;
  310. CALL(make_session(&sess, serve_file, &sfargs));
  311. req = ne_request_create(sess, "GET", "/abort");
  312. dc = ne_decompress_reader(req, ne_accept_2xx, reader_abort, sess);
  313. ret = ne_request_dispatch(req);
  314. reap_server();
  315. ONN("request was not aborted", ret != NE_ERROR);
  316. ONV(strcmp(ne_get_error(sess), READER_ABORT_ERR),
  317. ("session error was %s not %s",
  318. ne_get_error(sess), READER_ABORT_ERR));
  319. reap_server();
  320. ne_decompress_destroy(dc);
  321. ne_request_destroy(req);
  322. ne_session_destroy(sess);
  323. return OK;
  324. }
  325. ne_test tests[] = {
  326. T_LEAKY(init),
  327. T(not_compressed),
  328. T(simple),
  329. T(hello),
  330. T(withname),
  331. T(fail_trailing),
  332. T(fail_trailing_1b),
  333. T(fail_bad_csum),
  334. T(fail_truncate),
  335. T(fail_corrupt1),
  336. T(fail_corrupt2),
  337. T(fail_empty),
  338. T(notcomp_empty),
  339. T(chunked_1b),
  340. T(chunked_1b_wn),
  341. T(chunked_12b),
  342. T(chunked_20b),
  343. T(chunked_10b),
  344. T(chunked_10b_wn),
  345. T(retry_notcompress),
  346. T(retry_compress),
  347. T(compress_abort),
  348. T(NULL)
  349. };