1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 #include "test-utils.h"
6 const char *name, *value;
9 static struct RequestTest {
10 const char *description;
14 const char *method, *path;
15 SoupHTTPVersion version;
18 /**********************/
19 /*** VALID REQUESTS ***/
20 /**********************/
22 { "HTTP 1.0 request with no headers",
23 "GET / HTTP/1.0\r\n", -1,
25 "GET", "/", SOUP_HTTP_1_0,
30 "GET / HTTP/1.1\r\nHost: example.com\r\n", -1,
32 "GET", "/", SOUP_HTTP_1_1,
33 { { "Host", "example.com" },
38 { "Req w/ 1 header, no leading whitespace",
39 "GET / HTTP/1.1\r\nHost:example.com\r\n", -1,
41 "GET", "/", SOUP_HTTP_1_1,
42 { { "Host", "example.com" },
47 { "Req w/ 1 header including trailing whitespace",
48 "GET / HTTP/1.1\r\nHost: example.com \r\n", -1,
50 "GET", "/", SOUP_HTTP_1_1,
51 { { "Host", "example.com" },
56 { "Req w/ 1 header, wrapped",
57 "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\n", -1,
59 "GET", "/", SOUP_HTTP_1_1,
60 { { "Foo", "bar baz" },
65 { "Req w/ 1 header, wrapped with additional whitespace",
66 "GET / HTTP/1.1\r\nFoo: bar \r\n baz\r\n", -1,
68 "GET", "/", SOUP_HTTP_1_1,
69 { { "Foo", "bar baz" },
74 { "Req w/ 1 header, wrapped with tab",
75 "GET / HTTP/1.1\r\nFoo: bar\r\n\tbaz\r\n", -1,
77 "GET", "/", SOUP_HTTP_1_1,
78 { { "Foo", "bar baz" },
83 { "Req w/ 1 header, wrapped before value",
84 "GET / HTTP/1.1\r\nFoo:\r\n bar baz\r\n", -1,
86 "GET", "/", SOUP_HTTP_1_1,
87 { { "Foo", "bar baz" },
92 { "Req w/ 1 header with empty value",
93 "GET / HTTP/1.1\r\nHost:\r\n", -1,
95 "GET", "/", SOUP_HTTP_1_1,
101 { "Req w/ 2 headers",
102 "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n", -1,
104 "GET", "/", SOUP_HTTP_1_1,
105 { { "Host", "example.com" },
106 { "Connection", "close" },
111 { "Req w/ 3 headers",
112 "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\nBlah: blah\r\n", -1,
114 "GET", "/", SOUP_HTTP_1_1,
115 { { "Host", "example.com" },
116 { "Connection", "close" },
122 { "Req w/ 3 headers, 1st wrapped",
123 "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\nConnection: close\r\nBlah: blah\r\n", -1,
125 "GET", "/", SOUP_HTTP_1_1,
126 { { "Foo", "bar baz" },
127 { "Connection", "close" },
133 { "Req w/ 3 headers, 2nd wrapped",
134 "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
136 "GET", "/", SOUP_HTTP_1_1,
137 { { "Connection", "close" },
139 { "Foo", "bar baz" },
144 { "Req w/ 3 headers, 3rd wrapped",
145 "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
147 "GET", "/", SOUP_HTTP_1_1,
148 { { "Connection", "close" },
150 { "Foo", "bar baz" },
155 { "Req w/ same header multiple times",
156 "GET / HTTP/1.1\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
158 "GET", "/", SOUP_HTTP_1_1,
159 { { "Foo", "bar, baz, quux" },
164 { "Connection header on HTTP/1.0 message",
165 "GET / HTTP/1.0\r\nFoo: bar\r\nConnection: Bar, Quux\r\nBar: baz\r\nQuux: foo\r\n", -1,
167 "GET", "/", SOUP_HTTP_1_0,
169 { "Connection", "Bar, Quux" },
174 { "GET with full URI",
175 "GET http://example.com HTTP/1.1\r\n", -1,
177 "GET", "http://example.com", SOUP_HTTP_1_1,
181 { "GET with full URI in upper-case",
182 "GET HTTP://example.com HTTP/1.1\r\n", -1,
184 "GET", "HTTP://example.com", SOUP_HTTP_1_1,
188 /* It's better for this to be passed through: this means a SoupServer
189 * could implement ftp-over-http proxying, for instance
191 { "GET with full URI of unrecognised scheme",
192 "GET AbOuT: HTTP/1.1\r\n", -1,
194 "GET", "AbOuT:", SOUP_HTTP_1_1,
198 /****************************/
199 /*** RECOVERABLE REQUESTS ***/
200 /****************************/
202 /* RFC 2616 section 4.1 says we SHOULD accept this */
204 { "Spurious leading CRLF",
205 "\r\nGET / HTTP/1.1\r\nHost: example.com\r\n", -1,
207 "GET", "/", SOUP_HTTP_1_1,
208 { { "Host", "example.com" },
213 /* RFC 2616 section 3.1 says we MUST accept this */
215 { "HTTP/01.01 request",
216 "GET / HTTP/01.01\r\nHost: example.com\r\n", -1,
218 "GET", "/", SOUP_HTTP_1_1,
219 { { "Host", "example.com" },
224 /* RFC 2616 section 19.3 says we SHOULD accept these */
226 { "LF instead of CRLF after header",
227 "GET / HTTP/1.1\r\nHost: example.com\nConnection: close\n", -1,
229 "GET", "/", SOUP_HTTP_1_1,
230 { { "Host", "example.com" },
231 { "Connection", "close" },
236 { "LF instead of CRLF after Request-Line",
237 "GET / HTTP/1.1\nHost: example.com\r\n", -1,
239 "GET", "/", SOUP_HTTP_1_1,
240 { { "Host", "example.com" },
246 "GET / HTTP/1.1\r\na: b\r\nc: d\ne: f\r\ng: h\n", -1,
248 "GET", "/", SOUP_HTTP_1_1,
257 { "Req w/ incorrect whitespace in Request-Line",
258 "GET /\tHTTP/1.1\r\nHost: example.com\r\n", -1,
260 "GET", "/", SOUP_HTTP_1_1,
261 { { "Host", "example.com" },
266 { "Req w/ incorrect whitespace after Request-Line",
267 "GET / HTTP/1.1 \r\nHost: example.com\r\n", -1,
269 "GET", "/", SOUP_HTTP_1_1,
270 { { "Host", "example.com" },
275 /* If the request/status line is parseable, then we
276 * just ignore any invalid-looking headers after that.
280 { "Req w/ mangled header",
281 "GET / HTTP/1.1\r\nHost: example.com\r\nFoo one\r\nBar: two\r\n", -1,
283 "GET", "/", SOUP_HTTP_1_1,
284 { { "Host", "example.com" },
290 { "First header line is continuation",
291 "GET / HTTP/1.1\r\n b\r\nHost: example.com\r\nc: d\r\n", -1,
293 "GET", "/", SOUP_HTTP_1_1,
294 { { "Host", "example.com" },
300 { "Zero-length header name",
301 "GET / HTTP/1.1\r\na: b\r\n: example.com\r\nc: d\r\n", -1,
303 "GET", "/", SOUP_HTTP_1_1,
310 { "CR in header name",
311 "GET / HTTP/1.1\r\na: b\r\na\rb: cd\r\nx\r: y\r\n\rz: w\r\nc: d\r\n", -1,
313 "GET", "/", SOUP_HTTP_1_1,
320 { "CR in header value",
321 "GET / HTTP/1.1\r\na: b\r\nHost: example\rcom\r\np: \rq\r\ns: t\r\r\nc: d\r\n", -1,
323 "GET", "/", SOUP_HTTP_1_1,
325 { "Host", "example com" }, /* CR in the middle turns to space */
326 { "p", "q" }, /* CR at beginning is ignored */
327 { "s", "t" }, /* CR at end is ignored */
333 { "Tab in header name",
334 "GET / HTTP/1.1\r\na: b\r\na\tb: cd\r\nx\t: y\r\np: q\r\n\tz: w\r\nc: d\r\n", -1,
336 "GET", "/", SOUP_HTTP_1_1,
338 /* Tab anywhere in the header name causes it to be
339 * ignored... except at beginning of line where it's a
348 { "Tab in header value",
349 "GET / HTTP/1.1\r\na: b\r\nab: c\td\r\nx: \ty\r\nz: w\t\r\nc: d\r\n", -1,
351 "GET", "/", SOUP_HTTP_1_1,
353 { "ab", "c\td" }, /* internal tab preserved */
354 { "x", "y" }, /* leading tab ignored */
355 { "z", "w" }, /* trailing tab ignored */
361 /************************/
362 /*** INVALID REQUESTS ***/
363 /************************/
365 { "HTTP 0.9 request; not supported",
367 SOUP_STATUS_BAD_REQUEST,
372 { "HTTP 1.2 request (no such thing)",
373 "GET / HTTP/1.2\r\n", -1,
374 SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
379 { "HTTP 2000 request (no such thing)",
380 "GET / HTTP/2000.0\r\n", -1,
381 SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
386 { "Non-HTTP request",
387 "GET / SOUP/1.1\r\nHost: example.com\r\n", -1,
388 SOUP_STATUS_BAD_REQUEST,
393 { "Junk after Request-Line",
394 "GET / HTTP/1.1 blah\r\nHost: example.com\r\n", -1,
395 SOUP_STATUS_BAD_REQUEST,
401 "G\x00T / HTTP/1.1\r\nHost: example.com\r\n", 37,
402 SOUP_STATUS_BAD_REQUEST,
407 { "NUL at beginning of Method",
408 "\x00 / HTTP/1.1\r\nHost: example.com\r\n", 35,
409 SOUP_STATUS_BAD_REQUEST,
415 "GET /\x00 HTTP/1.1\r\nHost: example.com\r\n", 38,
416 SOUP_STATUS_BAD_REQUEST,
421 { "NUL in header name",
422 "GET / HTTP/1.1\r\n\x00: silly\r\n", 37,
423 SOUP_STATUS_BAD_REQUEST,
428 { "NUL in header value",
429 "GET / HTTP/1.1\r\nHost: example\x00com\r\n", 37,
430 SOUP_STATUS_BAD_REQUEST,
435 { "No terminating CRLF",
436 "GET / HTTP/1.1\r\nHost: example.com", -1,
437 SOUP_STATUS_BAD_REQUEST,
442 { "Unrecognized expectation",
443 "GET / HTTP/1.1\r\nHost: example.com\r\nExpect: the-impossible\r\n", -1,
444 SOUP_STATUS_EXPECTATION_FAILED,
449 static const int num_reqtests = G_N_ELEMENTS (reqtests);
451 static struct ResponseTest {
452 const char *description;
453 const char *response;
455 SoupHTTPVersion version;
457 const char *reason_phrase;
460 /***********************/
461 /*** VALID RESPONSES ***/
462 /***********************/
464 { "HTTP 1.0 response w/ no headers",
465 "HTTP/1.0 200 ok\r\n", -1,
466 SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
470 { "HTTP 1.1 response w/ no headers",
471 "HTTP/1.1 200 ok\r\n", -1,
472 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
476 { "Response w/ multi-word Reason-Phrase",
477 "HTTP/1.1 400 bad request\r\n", -1,
478 SOUP_HTTP_1_1, SOUP_STATUS_BAD_REQUEST, "bad request",
482 { "Response w/ 1 header",
483 "HTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
484 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
490 { "Response w/ 2 headers",
491 "HTTP/1.1 200 ok\r\nFoo: bar\r\nBaz: quux\r\n", -1,
492 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
499 { "Response w/ same header multiple times",
500 "HTTP/1.1 200 ok\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
501 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
502 { { "Foo", "bar, baz, quux" },
507 { "Response w/ no reason phrase",
508 "HTTP/1.1 200 \r\nFoo: bar\r\n", -1,
509 SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
515 { "Connection header on HTTP/1.0 message",
516 "HTTP/1.0 200 ok\r\nFoo: bar\r\nConnection: Bar\r\nBar: quux\r\n", -1,
517 SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
519 { "Connection", "Bar" },
524 /*****************************/
525 /*** RECOVERABLE RESPONSES ***/
526 /*****************************/
528 /* RFC 2616 section 3.1 says we MUST accept this */
530 { "HTTP/01.01 response",
531 "HTTP/01.01 200 ok\r\nFoo: bar\r\n", -1,
532 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
538 /* RFC 2616 section 19.3 says we SHOULD accept these */
540 { "Response w/ LF instead of CRLF after Status-Line",
541 "HTTP/1.1 200 ok\nFoo: bar\r\n", -1,
542 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
548 { "Response w/ incorrect spacing in Status-Line",
549 "HTTP/1.1 200\tok\r\nFoo: bar\r\n", -1,
550 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
556 { "Response w/ no reason phrase or preceding SP",
557 "HTTP/1.1 200\r\nFoo: bar\r\n", -1,
558 SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
564 { "Response w/ no whitespace after status code",
565 "HTTP/1.1 200ok\r\nFoo: bar\r\n", -1,
566 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
572 /* Shoutcast support */
573 { "Shoutcast server not-quite-HTTP",
574 "ICY 200 OK\r\nFoo: bar\r\n", -1,
575 SOUP_HTTP_1_0, SOUP_STATUS_OK, "OK",
581 /* qv bug 579318, do_bad_header_tests() below */
582 { "Response w/ mangled header",
583 "HTTP/1.1 200 ok\r\nFoo: one\r\nBar two:2\r\nBaz: three\r\n", -1,
584 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
592 { "HTTP 1.1 response with leading line break",
593 "\nHTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
594 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
599 /*************************/
600 /*** INVALID RESPONSES ***/
601 /*************************/
603 { "Invalid HTTP version",
604 "HTTP/1.2 200 OK\r\nFoo: bar\r\n", -1,
609 { "Non-HTTP response",
610 "SOUP/1.1 200 OK\r\nFoo: bar\r\n", -1,
615 { "Non-numeric status code",
616 "HTTP/1.1 XXX OK\r\nFoo: bar\r\n", -1,
622 "HTTP/1.1 OK\r\nFoo: bar\r\n", -1,
627 { "One-digit status code",
628 "HTTP/1.1 2 OK\r\nFoo: bar\r\n", -1,
633 { "Two-digit status code",
634 "HTTP/1.1 20 OK\r\nFoo: bar\r\n", -1,
639 { "Four-digit status code",
640 "HTTP/1.1 2000 OK\r\nFoo: bar\r\n", -1,
645 { "Status code < 100",
646 "HTTP/1.1 001 OK\r\nFoo: bar\r\n", -1,
651 { "Status code > 599",
652 "HTTP/1.1 600 OK\r\nFoo: bar\r\n", -1,
658 "\x00HTTP/1.1 200 OK\r\nFoo: bar\r\n", 28,
663 { "NUL in Reason Phrase",
664 "HTTP/1.1 200 O\x00K\r\nFoo: bar\r\n", 28,
669 { "NUL in header name",
670 "HTTP/1.1 200 OK\r\nF\x00oo: bar\r\n", 28,
675 { "NUL in header value",
676 "HTTP/1.1 200 OK\r\nFoo: b\x00ar\r\n", 28,
681 static const int num_resptests = G_N_ELEMENTS (resptests);
683 static struct QValueTest {
684 const char *header_value;
685 const char *acceptable[7];
686 const char *unacceptable[2];
688 { "text/plain; q=0.5, text/html,\t text/x-dvi; q=0.8, text/x-c",
689 { "text/html", "text/x-c", "text/x-dvi", "text/plain", NULL },
693 { "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5",
694 { "text/html;level=1", "text/html", "*/*", "text/html;level=2",
699 { "gzip;q=1.0, identity; q=0.5, *;q=0",
700 { "gzip", "identity", NULL },
704 static const int num_qvaluetests = G_N_ELEMENTS (qvaluetests);
707 print_header (const char *name, const char *value, gpointer data)
709 debug_printf (1, " '%s': '%s'\n", name, value);
713 check_headers (Header *headers, SoupMessageHeaders *hdrs)
715 GSList *header_names, *h;
716 SoupMessageHeadersIter iter;
717 const char *name, *value;
722 soup_message_headers_iter_init (&iter, hdrs);
723 while (soup_message_headers_iter_next (&iter, &name, &value)) {
724 if (!g_slist_find_custom (header_names, name,
725 (GCompareFunc)strcmp))
726 header_names = g_slist_append (header_names, (char *)name);
729 for (i = 0, h = header_names; headers[i].name && h; i++, h = h->next) {
730 if (strcmp (h->data, headers[i].name) != 0) {
734 value = soup_message_headers_get_list (hdrs, headers[i].name);
735 if (strcmp (value, headers[i].value) != 0) {
740 if (headers[i].name || h)
742 g_slist_free (header_names);
747 do_request_tests (void)
751 SoupHTTPVersion version;
752 SoupMessageHeaders *headers;
755 debug_printf (1, "Request tests\n");
756 for (i = 0; i < num_reqtests; i++) {
759 debug_printf (1, "%2d. %s (%s): ", i + 1, reqtests[i].description,
760 soup_status_get_phrase (reqtests[i].status));
762 headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
763 method = path = NULL;
765 if (reqtests[i].length == -1)
766 len = strlen (reqtests[i].request);
768 len = reqtests[i].length;
769 status = soup_headers_parse_request (reqtests[i].request, len,
770 headers, &method, &path,
772 if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
773 if ((reqtests[i].method && strcmp (reqtests[i].method, method) != 0) || !reqtests[i].method)
775 if ((reqtests[i].path && strcmp (reqtests[i].path, path) != 0) || !reqtests[i].path)
777 if (reqtests[i].version != version)
780 if (!check_headers (reqtests[i].headers, headers))
783 if (status != reqtests[i].status)
788 debug_printf (1, "OK!\n");
790 debug_printf (1, "BAD!\n");
792 if (reqtests[i].method) {
793 debug_printf (1, " expected: '%s' '%s' 'HTTP/1.%d'\n",
796 reqtests[i].version);
797 for (h = 0; reqtests[i].headers[h].name; h++) {
798 debug_printf (1, " '%s': '%s'\n",
799 reqtests[i].headers[h].name,
800 reqtests[i].headers[h].value);
803 debug_printf (1, " expected: %s\n",
804 soup_status_get_phrase (reqtests[i].status));
807 debug_printf (1, " got: '%s' '%s' 'HTTP/1.%d'\n",
808 method, path, version);
809 soup_message_headers_foreach (headers, print_header, NULL);
811 debug_printf (1, " got: %s\n",
812 soup_status_get_phrase (status));
818 soup_message_headers_free (headers);
820 debug_printf (1, "\n");
824 do_response_tests (void)
829 SoupHTTPVersion version;
830 SoupMessageHeaders *headers;
832 debug_printf (1, "Response tests\n");
833 for (i = 0; i < num_resptests; i++) {
836 debug_printf (1, "%2d. %s (%s): ", i + 1, resptests[i].description,
837 resptests[i].reason_phrase ? "should parse" : "should NOT parse");
839 headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
840 reason_phrase = NULL;
842 if (resptests[i].length == -1)
843 len = strlen (resptests[i].response);
845 len = resptests[i].length;
846 if (soup_headers_parse_response (resptests[i].response, len,
848 &status_code, &reason_phrase)) {
849 if (resptests[i].version != version)
851 if (resptests[i].status_code != status_code)
853 if ((resptests[i].reason_phrase && strcmp (resptests[i].reason_phrase, reason_phrase) != 0) || !resptests[i].reason_phrase)
856 if (!check_headers (resptests[i].headers, headers))
859 if (resptests[i].reason_phrase)
864 debug_printf (1, "OK!\n");
866 debug_printf (1, "BAD!\n");
868 if (resptests[i].reason_phrase) {
869 debug_printf (1, " expected: 'HTTP/1.%d' '%03d' '%s'\n",
870 resptests[i].version,
871 resptests[i].status_code,
872 resptests[i].reason_phrase);
873 for (h = 0; resptests[i].headers[h].name; h++) {
874 debug_printf (1, " '%s': '%s'\n",
875 resptests[i].headers[h].name,
876 resptests[i].headers[h].value);
879 debug_printf (1, " expected: parse error\n");
881 debug_printf (1, " got: 'HTTP/1.%d' '%03d' '%s'\n",
882 version, status_code, reason_phrase);
883 soup_message_headers_foreach (headers, print_header, NULL);
885 debug_printf (1, " got: parse error\n");
888 g_free (reason_phrase);
889 soup_message_headers_free (headers);
891 debug_printf (1, "\n");
895 do_qvalue_tests (void)
898 GSList *acceptable, *unacceptable, *iter;
901 debug_printf (1, "qvalue tests\n");
902 for (i = 0; i < num_qvaluetests; i++) {
903 debug_printf (1, "%2d. %s:\n", i + 1, qvaluetests[i].header_value);
906 acceptable = soup_header_parse_quality_list (qvaluetests[i].header_value,
909 debug_printf (1, " acceptable: ");
912 for (iter = acceptable, j = 0; iter; iter = iter->next, j++) {
913 debug_printf (1, "%s ", (char *)iter->data);
914 if (!qvaluetests[i].acceptable[j] ||
915 strcmp (iter->data, qvaluetests[i].acceptable[j]) != 0)
918 debug_printf (1, "\n");
919 soup_header_free_list (acceptable);
921 debug_printf (1, "(none)\n");
923 debug_printf (1, " WRONG! expected: ");
924 for (j = 0; qvaluetests[i].acceptable[j]; j++)
925 debug_printf (1, "%s ", qvaluetests[i].acceptable[j]);
926 debug_printf (1, "\n");
930 debug_printf (1, " unacceptable: ");
933 for (iter = unacceptable, j = 0; iter; iter = iter->next, j++) {
934 debug_printf (1, "%s ", (char *)iter->data);
935 if (!qvaluetests[i].unacceptable[j] ||
936 strcmp (iter->data, qvaluetests[i].unacceptable[j]) != 0)
939 debug_printf (1, "\n");
940 soup_header_free_list (unacceptable);
942 debug_printf (1, "(none)\n");
944 debug_printf (1, " WRONG! expected: ");
945 for (j = 0; qvaluetests[i].unacceptable[j]; j++)
946 debug_printf (1, "%s ", qvaluetests[i].unacceptable[j]);
947 debug_printf (1, "\n");
951 debug_printf (1, "\n");
955 #define RFC5987_TEST_FILENAME "t\xC3\xA9st.txt"
956 #define RFC5987_TEST_FALLBACK_FILENAME "test.txt"
958 #define RFC5987_TEST_HEADER_ENCODED "attachment; filename*=UTF-8''t%C3%A9st.txt"
960 #define RFC5987_TEST_HEADER_UTF8 "attachment; filename*=UTF-8''t%C3%A9st.txt; filename=\"test.txt\""
961 #define RFC5987_TEST_HEADER_ISO "attachment; filename=\"test.txt\"; filename*=iso-8859-1''t%E9st.txt"
962 #define RFC5987_TEST_HEADER_FALLBACK "attachment; filename*=Unknown''t%FF%FF%FFst.txt; filename=\"test.txt\""
965 do_content_disposition_tests (void)
967 SoupMessageHeaders *hdrs;
969 const char *header, *filename;
972 SoupMultipart *multipart;
973 SoupMessageBody *body;
975 debug_printf (1, "Content-Disposition tests\n");
977 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
978 params = g_hash_table_new (g_str_hash, g_str_equal);
979 g_hash_table_insert (params, "filename", RFC5987_TEST_FILENAME);
980 soup_message_headers_set_content_disposition (hdrs, "attachment", params);
981 g_hash_table_destroy (params);
983 header = soup_message_headers_get_one (hdrs, "Content-Disposition");
984 if (!strcmp (header, RFC5987_TEST_HEADER_ENCODED))
985 debug_printf (1, " encoded OK\n");
987 debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n",
988 RFC5987_TEST_HEADER_ENCODED, header);
993 soup_message_headers_clear (hdrs);
994 soup_message_headers_append (hdrs, "Content-Disposition",
995 RFC5987_TEST_HEADER_UTF8);
996 if (!soup_message_headers_get_content_disposition (hdrs,
999 debug_printf (1, " UTF-8 decoding FAILED!\n could not parse\n");
1003 g_free (disposition);
1005 filename = g_hash_table_lookup (params, "filename");
1007 debug_printf (1, " UTF-8 decoding FAILED!\n could not find filename\n");
1009 } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) {
1010 debug_printf (1, " UTF-8 decoding FAILED!\n expected: %s\n got: %s\n",
1011 RFC5987_TEST_FILENAME, filename);
1014 debug_printf (1, " UTF-8 decoded OK\n");
1015 g_hash_table_destroy (params);
1017 /* ISO-8859-1 decoding */
1018 soup_message_headers_clear (hdrs);
1019 soup_message_headers_append (hdrs, "Content-Disposition",
1020 RFC5987_TEST_HEADER_ISO);
1021 if (!soup_message_headers_get_content_disposition (hdrs,
1024 debug_printf (1, " iso-8859-1 decoding FAILED!\n could not parse\n");
1028 g_free (disposition);
1030 filename = g_hash_table_lookup (params, "filename");
1032 debug_printf (1, " iso-8859-1 decoding FAILED!\n could not find filename\n");
1034 } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) {
1035 debug_printf (1, " iso-8859-1 decoding FAILED!\n expected: %s\n got: %s\n",
1036 RFC5987_TEST_FILENAME, filename);
1039 debug_printf (1, " iso-8859-1 decoded OK\n");
1040 g_hash_table_destroy (params);
1043 soup_message_headers_clear (hdrs);
1044 soup_message_headers_append (hdrs, "Content-Disposition",
1045 RFC5987_TEST_HEADER_FALLBACK);
1046 if (!soup_message_headers_get_content_disposition (hdrs,
1049 debug_printf (1, " fallback decoding FAILED!\n could not parse\n");
1053 g_free (disposition);
1055 filename = g_hash_table_lookup (params, "filename");
1057 debug_printf (1, " fallback decoding FAILED!\n could not find filename\n");
1059 } else if (strcmp (filename, RFC5987_TEST_FALLBACK_FILENAME) != 0) {
1060 debug_printf (1, " fallback decoding FAILED!\n expected: %s\n got: %s\n",
1061 RFC5987_TEST_FALLBACK_FILENAME, filename);
1064 debug_printf (1, " fallback decoded OK\n");
1065 g_hash_table_destroy (params);
1067 soup_message_headers_free (hdrs);
1069 /* Ensure that soup-multipart always quotes filename (bug 641280) */
1070 multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART);
1071 buffer = soup_buffer_new (SOUP_MEMORY_STATIC, "foo", 3);
1072 soup_multipart_append_form_file (multipart, "test", "token",
1073 "text/plain", buffer);
1074 soup_buffer_free (buffer);
1076 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
1077 body = soup_message_body_new ();
1078 soup_multipart_to_message (multipart, hdrs, body);
1079 soup_message_headers_free (hdrs);
1080 soup_multipart_free (multipart);
1082 buffer = soup_message_body_flatten (body);
1083 soup_message_body_free (body);
1085 if (strstr (buffer->data, "filename=\"token\""))
1086 debug_printf (1, " SoupMultipart encoded filename correctly\n");
1088 debug_printf (1, " SoupMultipart encoded filename incorrectly!\n");
1091 soup_buffer_free (buffer);
1093 debug_printf (1, "\n");
1096 #define CONTENT_TYPE_TEST_MIME_TYPE "text/plain"
1097 #define CONTENT_TYPE_TEST_ATTRIBUTE "charset"
1098 #define CONTENT_TYPE_TEST_VALUE "US-ASCII"
1099 #define CONTENT_TYPE_TEST_HEADER "text/plain; charset=US-ASCII"
1101 #define CONTENT_TYPE_BAD_HEADER "plain text, not text/html"
1104 do_content_type_tests (void)
1106 SoupMessageHeaders *hdrs;
1108 const char *header, *mime_type;
1110 debug_printf (1, "Content-Type tests\n");
1112 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
1113 params = g_hash_table_new (g_str_hash, g_str_equal);
1114 g_hash_table_insert (params, CONTENT_TYPE_TEST_ATTRIBUTE,
1115 CONTENT_TYPE_TEST_VALUE);
1116 soup_message_headers_set_content_type (hdrs, CONTENT_TYPE_TEST_MIME_TYPE, params);
1117 g_hash_table_destroy (params);
1119 header = soup_message_headers_get_one (hdrs, "Content-Type");
1120 if (!strcmp (header, CONTENT_TYPE_TEST_HEADER))
1121 debug_printf (1, " encoded OK\n");
1123 debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n",
1124 CONTENT_TYPE_TEST_HEADER, header);
1128 soup_message_headers_clear (hdrs);
1129 soup_message_headers_append (hdrs, "Content-Type",
1130 CONTENT_TYPE_TEST_MIME_TYPE);
1131 /* Add a second Content-Type header: should be ignored */
1132 soup_message_headers_append (hdrs, "Content-Type",
1133 CONTENT_TYPE_TEST_MIME_TYPE);
1135 mime_type = soup_message_headers_get_content_type (hdrs, ¶ms);
1137 debug_printf (1, " decoding FAILED!\n could not parse\n");
1141 if (mime_type && strcmp (mime_type, CONTENT_TYPE_TEST_MIME_TYPE) != 0) {
1142 debug_printf (1, " decoding FAILED!\n bad returned MIME type: %s\n",
1145 } else if (params && g_hash_table_size (params) != 0) {
1146 debug_printf (1, " decoding FAILED!\n params contained %d params (should be 0)\n",
1147 g_hash_table_size (params));
1150 debug_printf (1, " decoded OK\n");
1153 g_hash_table_destroy (params);
1155 soup_message_headers_clear (hdrs);
1156 soup_message_headers_append (hdrs, "Content-Type",
1157 CONTENT_TYPE_BAD_HEADER);
1158 mime_type = soup_message_headers_get_content_type (hdrs, ¶ms);
1160 debug_printf (1, " Bad content rejection FAILED!\n");
1163 debug_printf (1, " Bad content rejection OK\n");
1165 soup_message_headers_free (hdrs);
1167 debug_printf (1, "\n");
1171 const char *name, *value;
1174 { "two", "test with spaces" },
1175 { "three", "test with \"quotes\" and \\s" },
1177 { "five", "test with \xC3\xA1\xC3\xA7\xC4\x89\xC3\xA8\xC3\xB1\xC5\xA3\xC5\xA1" }
1180 #define TEST_PARAMS_RESULT "one=foo, two=\"test with spaces\", three=\"test with \\\"quotes\\\" and \\\\s\", four, five*=UTF-8''test%20with%20%C3%A1%C3%A7%C4%89%C3%A8%C3%B1%C5%A3%C5%A1"
1183 do_append_param_tests (void)
1188 debug_printf (1, "soup_header_g_string_append_param() tests\n");
1190 params = g_string_new (NULL);
1191 for (i = 0; i < G_N_ELEMENTS (test_params); i++) {
1193 g_string_append (params, ", ");
1194 soup_header_g_string_append_param (params,
1195 test_params[i].name,
1196 test_params[i].value);
1198 if (strcmp (params->str, TEST_PARAMS_RESULT) != 0) {
1199 debug_printf (1, " FAILED!\n expected: %s\n got: %s\n",
1200 TEST_PARAMS_RESULT, params->str);
1203 debug_printf (1, " OK\n");
1204 g_string_free (params, TRUE);
1206 debug_printf (1, "\n");
1209 static const struct {
1210 const char *description, *name, *value;
1212 { "Empty name", "", "value" },
1213 { "Name with spaces", "na me", "value" },
1214 { "Name with colon", "na:me", "value" },
1215 { "Name with CR", "na\rme", "value" },
1216 { "Name with LF", "na\nme", "value" },
1217 { "Name with tab", "na\tme", "value" },
1218 { "Value with CR", "name", "val\rue" },
1219 { "Value with LF", "name", "val\nue" },
1220 { "Value with LWS", "name", "val\r\n ue" }
1224 do_bad_header_tests (void)
1226 SoupMessageHeaders *hdrs;
1229 debug_printf (1, "bad header rejection tests\n");
1231 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
1232 for (i = 0; i < G_N_ELEMENTS (bad_headers); i++) {
1233 debug_printf (1, " %s\n", bad_headers[i].description);
1234 expect_warning = TRUE;
1235 soup_message_headers_append (hdrs, bad_headers[i].name,
1236 bad_headers[i].value);
1237 if (expect_warning) {
1238 expect_warning = FALSE;
1239 debug_printf (1, " FAILED: soup_message_headers_append() did not reject it\n");
1243 soup_message_headers_free (hdrs);
1247 main (int argc, char **argv)
1249 test_init (argc, argv, NULL);
1251 do_request_tests ();
1252 do_response_tests ();
1254 do_content_disposition_tests ();
1255 do_content_type_tests ();
1256 do_append_param_tests ();
1257 do_bad_header_tests ();