7 #include <libsoup/soup.h>
9 #include "test-utils.h"
12 const char *name, *value;
15 static struct RequestTest {
16 const char *description;
20 const char *method, *path;
21 SoupHTTPVersion version;
24 /**********************/
25 /*** VALID REQUESTS ***/
26 /**********************/
28 { "HTTP 1.0 request with no headers",
29 "GET / HTTP/1.0\r\n", -1,
31 "GET", "/", SOUP_HTTP_1_0,
36 "GET / HTTP/1.1\r\nHost: example.com\r\n", -1,
38 "GET", "/", SOUP_HTTP_1_1,
39 { { "Host", "example.com" },
44 { "Req w/ 1 header, no leading whitespace",
45 "GET / HTTP/1.1\r\nHost:example.com\r\n", -1,
47 "GET", "/", SOUP_HTTP_1_1,
48 { { "Host", "example.com" },
53 { "Req w/ 1 header including trailing whitespace",
54 "GET / HTTP/1.1\r\nHost: example.com \r\n", -1,
56 "GET", "/", SOUP_HTTP_1_1,
57 { { "Host", "example.com" },
62 { "Req w/ 1 header, wrapped",
63 "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\n", -1,
65 "GET", "/", SOUP_HTTP_1_1,
66 { { "Foo", "bar baz" },
71 { "Req w/ 1 header, wrapped with additional whitespace",
72 "GET / HTTP/1.1\r\nFoo: bar \r\n baz\r\n", -1,
74 "GET", "/", SOUP_HTTP_1_1,
75 { { "Foo", "bar baz" },
80 { "Req w/ 1 header, wrapped with tab",
81 "GET / HTTP/1.1\r\nFoo: bar\r\n\tbaz\r\n", -1,
83 "GET", "/", SOUP_HTTP_1_1,
84 { { "Foo", "bar baz" },
89 { "Req w/ 1 header, wrapped before value",
90 "GET / HTTP/1.1\r\nFoo:\r\n bar baz\r\n", -1,
92 "GET", "/", SOUP_HTTP_1_1,
93 { { "Foo", "bar baz" },
98 { "Req w/ 1 header with empty value",
99 "GET / HTTP/1.1\r\nHost:\r\n", -1,
101 "GET", "/", SOUP_HTTP_1_1,
107 { "Req w/ 2 headers",
108 "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n", -1,
110 "GET", "/", SOUP_HTTP_1_1,
111 { { "Host", "example.com" },
112 { "Connection", "close" },
117 { "Req w/ 3 headers",
118 "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\nBlah: blah\r\n", -1,
120 "GET", "/", SOUP_HTTP_1_1,
121 { { "Host", "example.com" },
122 { "Connection", "close" },
128 { "Req w/ 3 headers, 1st wrapped",
129 "GET / HTTP/1.1\r\nFoo: bar\r\n baz\r\nConnection: close\r\nBlah: blah\r\n", -1,
131 "GET", "/", SOUP_HTTP_1_1,
132 { { "Foo", "bar baz" },
133 { "Connection", "close" },
139 { "Req w/ 3 headers, 2nd wrapped",
140 "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
142 "GET", "/", SOUP_HTTP_1_1,
143 { { "Connection", "close" },
145 { "Foo", "bar baz" },
150 { "Req w/ 3 headers, 3rd wrapped",
151 "GET / HTTP/1.1\r\nConnection: close\r\nBlah: blah\r\nFoo: bar\r\n baz\r\n", -1,
153 "GET", "/", SOUP_HTTP_1_1,
154 { { "Connection", "close" },
156 { "Foo", "bar baz" },
161 { "Req w/ same header multiple times",
162 "GET / HTTP/1.1\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
164 "GET", "/", SOUP_HTTP_1_1,
165 { { "Foo", "bar, baz, quux" },
170 { "Connection header on HTTP/1.0 message",
171 "GET / HTTP/1.0\r\nFoo: bar\r\nConnection: Bar, Quux\r\nBar: baz\r\nQuux: foo\r\n", -1,
173 "GET", "/", SOUP_HTTP_1_0,
175 { "Connection", "Bar, Quux" },
180 { "GET with full URI",
181 "GET http://example.com HTTP/1.1\r\n", -1,
183 "GET", "http://example.com", SOUP_HTTP_1_1,
187 { "GET with full URI in upper-case",
188 "GET HTTP://example.com HTTP/1.1\r\n", -1,
190 "GET", "HTTP://example.com", SOUP_HTTP_1_1,
194 /* It's better for this to be passed through: this means a SoupServer
195 * could implement ftp-over-http proxying, for instance
197 { "GET with full URI of unrecognised scheme",
198 "GET AbOuT: HTTP/1.1\r\n", -1,
200 "GET", "AbOuT:", SOUP_HTTP_1_1,
204 /****************************/
205 /*** RECOVERABLE REQUESTS ***/
206 /****************************/
208 /* RFC 2616 section 4.1 says we SHOULD accept this */
210 { "Spurious leading CRLF",
211 "\r\nGET / HTTP/1.1\r\nHost: example.com\r\n", -1,
213 "GET", "/", SOUP_HTTP_1_1,
214 { { "Host", "example.com" },
219 /* RFC 2616 section 3.1 says we MUST accept this */
221 { "HTTP/01.01 request",
222 "GET / HTTP/01.01\r\nHost: example.com\r\n", -1,
224 "GET", "/", SOUP_HTTP_1_1,
225 { { "Host", "example.com" },
230 /* RFC 2616 section 19.3 says we SHOULD accept these */
232 { "LF instead of CRLF after header",
233 "GET / HTTP/1.1\r\nHost: example.com\nConnection: close\n", -1,
235 "GET", "/", SOUP_HTTP_1_1,
236 { { "Host", "example.com" },
237 { "Connection", "close" },
242 { "LF instead of CRLF after Request-Line",
243 "GET / HTTP/1.1\nHost: example.com\r\n", -1,
245 "GET", "/", SOUP_HTTP_1_1,
246 { { "Host", "example.com" },
252 "GET / HTTP/1.1\r\na: b\r\nc: d\ne: f\r\ng: h\n", -1,
254 "GET", "/", SOUP_HTTP_1_1,
263 { "Req w/ incorrect whitespace in Request-Line",
264 "GET /\tHTTP/1.1\r\nHost: example.com\r\n", -1,
266 "GET", "/", SOUP_HTTP_1_1,
267 { { "Host", "example.com" },
272 { "Req w/ incorrect whitespace after Request-Line",
273 "GET / HTTP/1.1 \r\nHost: example.com\r\n", -1,
275 "GET", "/", SOUP_HTTP_1_1,
276 { { "Host", "example.com" },
281 /* If the request/status line is parseable, then we
282 * just ignore any invalid-looking headers after that.
286 { "Req w/ mangled header",
287 "GET / HTTP/1.1\r\nHost: example.com\r\nFoo one\r\nBar: two\r\n", -1,
289 "GET", "/", SOUP_HTTP_1_1,
290 { { "Host", "example.com" },
296 { "First header line is continuation",
297 "GET / HTTP/1.1\r\n b\r\nHost: example.com\r\nc: d\r\n", -1,
299 "GET", "/", SOUP_HTTP_1_1,
300 { { "Host", "example.com" },
306 { "Zero-length header name",
307 "GET / HTTP/1.1\r\na: b\r\n: example.com\r\nc: d\r\n", -1,
309 "GET", "/", SOUP_HTTP_1_1,
316 { "CR in header name",
317 "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,
319 "GET", "/", SOUP_HTTP_1_1,
326 { "CR in header value",
327 "GET / HTTP/1.1\r\na: b\r\nHost: example\rcom\r\np: \rq\r\ns: t\r\r\nc: d\r\n", -1,
329 "GET", "/", SOUP_HTTP_1_1,
331 { "Host", "example com" }, /* CR in the middle turns to space */
332 { "p", "q" }, /* CR at beginning is ignored */
333 { "s", "t" }, /* CR at end is ignored */
339 { "Tab in header name",
340 "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,
342 "GET", "/", SOUP_HTTP_1_1,
344 /* Tab anywhere in the header name causes it to be
345 * ignored... except at beginning of line where it's a
354 { "Tab in header value",
355 "GET / HTTP/1.1\r\na: b\r\nab: c\td\r\nx: \ty\r\nz: w\t\r\nc: d\r\n", -1,
357 "GET", "/", SOUP_HTTP_1_1,
359 { "ab", "c\td" }, /* internal tab preserved */
360 { "x", "y" }, /* leading tab ignored */
361 { "z", "w" }, /* trailing tab ignored */
367 /************************/
368 /*** INVALID REQUESTS ***/
369 /************************/
371 { "HTTP 0.9 request; not supported",
373 SOUP_STATUS_BAD_REQUEST,
378 { "HTTP 1.2 request (no such thing)",
379 "GET / HTTP/1.2\r\n", -1,
380 SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
385 { "HTTP 2000 request (no such thing)",
386 "GET / HTTP/2000.0\r\n", -1,
387 SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
392 { "Non-HTTP request",
393 "GET / SOUP/1.1\r\nHost: example.com\r\n", -1,
394 SOUP_STATUS_BAD_REQUEST,
399 { "Junk after Request-Line",
400 "GET / HTTP/1.1 blah\r\nHost: example.com\r\n", -1,
401 SOUP_STATUS_BAD_REQUEST,
407 "G\x00T / HTTP/1.1\r\nHost: example.com\r\n", 37,
408 SOUP_STATUS_BAD_REQUEST,
413 { "NUL at beginning of Method",
414 "\x00 / HTTP/1.1\r\nHost: example.com\r\n", 35,
415 SOUP_STATUS_BAD_REQUEST,
421 "GET /\x00 HTTP/1.1\r\nHost: example.com\r\n", 38,
422 SOUP_STATUS_BAD_REQUEST,
427 { "NUL in header name",
428 "GET / HTTP/1.1\r\n\x00: silly\r\n", 37,
429 SOUP_STATUS_BAD_REQUEST,
434 { "NUL in header value",
435 "GET / HTTP/1.1\r\nHost: example\x00com\r\n", 37,
436 SOUP_STATUS_BAD_REQUEST,
441 { "No terminating CRLF",
442 "GET / HTTP/1.1\r\nHost: example.com", -1,
443 SOUP_STATUS_BAD_REQUEST,
448 { "Unrecognized expectation",
449 "GET / HTTP/1.1\r\nHost: example.com\r\nExpect: the-impossible\r\n", -1,
450 SOUP_STATUS_EXPECTATION_FAILED,
455 static const int num_reqtests = G_N_ELEMENTS (reqtests);
457 static struct ResponseTest {
458 const char *description;
459 const char *response;
461 SoupHTTPVersion version;
463 const char *reason_phrase;
466 /***********************/
467 /*** VALID RESPONSES ***/
468 /***********************/
470 { "HTTP 1.0 response w/ no headers",
471 "HTTP/1.0 200 ok\r\n", -1,
472 SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
476 { "HTTP 1.1 response w/ no headers",
477 "HTTP/1.1 200 ok\r\n", -1,
478 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
482 { "Response w/ multi-word Reason-Phrase",
483 "HTTP/1.1 400 bad request\r\n", -1,
484 SOUP_HTTP_1_1, SOUP_STATUS_BAD_REQUEST, "bad request",
488 { "Response w/ 1 header",
489 "HTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
490 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
496 { "Response w/ 2 headers",
497 "HTTP/1.1 200 ok\r\nFoo: bar\r\nBaz: quux\r\n", -1,
498 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
505 { "Response w/ same header multiple times",
506 "HTTP/1.1 200 ok\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
507 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
508 { { "Foo", "bar, baz, quux" },
513 { "Response w/ no reason phrase",
514 "HTTP/1.1 200 \r\nFoo: bar\r\n", -1,
515 SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
521 { "Connection header on HTTP/1.0 message",
522 "HTTP/1.0 200 ok\r\nFoo: bar\r\nConnection: Bar\r\nBar: quux\r\n", -1,
523 SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
525 { "Connection", "Bar" },
530 /*****************************/
531 /*** RECOVERABLE RESPONSES ***/
532 /*****************************/
534 /* RFC 2616 section 3.1 says we MUST accept this */
536 { "HTTP/01.01 response",
537 "HTTP/01.01 200 ok\r\nFoo: bar\r\n", -1,
538 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
544 /* RFC 2616 section 19.3 says we SHOULD accept these */
546 { "Response w/ LF instead of CRLF after Status-Line",
547 "HTTP/1.1 200 ok\nFoo: bar\r\n", -1,
548 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
554 { "Response w/ incorrect spacing in Status-Line",
555 "HTTP/1.1 200\tok\r\nFoo: bar\r\n", -1,
556 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
562 { "Response w/ no reason phrase or preceding SP",
563 "HTTP/1.1 200\r\nFoo: bar\r\n", -1,
564 SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
570 { "Response w/ no whitespace after status code",
571 "HTTP/1.1 200ok\r\nFoo: bar\r\n", -1,
572 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
578 /* Shoutcast support */
579 { "Shoutcast server not-quite-HTTP",
580 "ICY 200 OK\r\nFoo: bar\r\n", -1,
581 SOUP_HTTP_1_0, SOUP_STATUS_OK, "OK",
587 /* qv bug 579318, do_bad_header_tests() below */
588 { "Response w/ mangled header",
589 "HTTP/1.1 200 ok\r\nFoo: one\r\nBar two:2\r\nBaz: three\r\n", -1,
590 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
598 { "HTTP 1.1 response with leading line break",
599 "\nHTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
600 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
605 /*************************/
606 /*** INVALID RESPONSES ***/
607 /*************************/
609 { "Invalid HTTP version",
610 "HTTP/1.2 200 OK\r\nFoo: bar\r\n", -1,
615 { "Non-HTTP response",
616 "SOUP/1.1 200 OK\r\nFoo: bar\r\n", -1,
621 { "Non-numeric status code",
622 "HTTP/1.1 XXX OK\r\nFoo: bar\r\n", -1,
628 "HTTP/1.1 OK\r\nFoo: bar\r\n", -1,
633 { "One-digit status code",
634 "HTTP/1.1 2 OK\r\nFoo: bar\r\n", -1,
639 { "Two-digit status code",
640 "HTTP/1.1 20 OK\r\nFoo: bar\r\n", -1,
645 { "Four-digit status code",
646 "HTTP/1.1 2000 OK\r\nFoo: bar\r\n", -1,
651 { "Status code < 100",
652 "HTTP/1.1 001 OK\r\nFoo: bar\r\n", -1,
657 { "Status code > 599",
658 "HTTP/1.1 600 OK\r\nFoo: bar\r\n", -1,
664 "\x00HTTP/1.1 200 OK\r\nFoo: bar\r\n", 28,
669 { "NUL in Reason Phrase",
670 "HTTP/1.1 200 O\x00K\r\nFoo: bar\r\n", 28,
675 { "NUL in header name",
676 "HTTP/1.1 200 OK\r\nF\x00oo: bar\r\n", 28,
681 { "NUL in header value",
682 "HTTP/1.1 200 OK\r\nFoo: b\x00ar\r\n", 28,
687 static const int num_resptests = G_N_ELEMENTS (resptests);
689 static struct QValueTest {
690 const char *header_value;
691 const char *acceptable[7];
692 const char *unacceptable[2];
694 { "text/plain; q=0.5, text/html,\t text/x-dvi; q=0.8, text/x-c",
695 { "text/html", "text/x-c", "text/x-dvi", "text/plain", NULL },
699 { "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5",
700 { "text/html;level=1", "text/html", "*/*", "text/html;level=2",
705 { "gzip;q=1.0, identity; q=0.5, *;q=0",
706 { "gzip", "identity", NULL },
710 static const int num_qvaluetests = G_N_ELEMENTS (qvaluetests);
713 print_header (const char *name, const char *value, gpointer data)
715 debug_printf (1, " '%s': '%s'\n", name, value);
719 check_headers (Header *headers, SoupMessageHeaders *hdrs)
721 GSList *header_names, *h;
722 SoupMessageHeadersIter iter;
723 const char *name, *value;
728 soup_message_headers_iter_init (&iter, hdrs);
729 while (soup_message_headers_iter_next (&iter, &name, &value)) {
730 if (!g_slist_find_custom (header_names, name,
731 (GCompareFunc)strcmp))
732 header_names = g_slist_append (header_names, (char *)name);
735 for (i = 0, h = header_names; headers[i].name && h; i++, h = h->next) {
736 if (strcmp (h->data, headers[i].name) != 0) {
740 value = soup_message_headers_get_list (hdrs, headers[i].name);
741 if (strcmp (value, headers[i].value) != 0) {
746 if (headers[i].name || h)
748 g_slist_free (header_names);
753 do_request_tests (void)
757 SoupHTTPVersion version;
758 SoupMessageHeaders *headers;
761 debug_printf (1, "Request tests\n");
762 for (i = 0; i < num_reqtests; i++) {
765 debug_printf (1, "%2d. %s (%s): ", i + 1, reqtests[i].description,
766 soup_status_get_phrase (reqtests[i].status));
768 headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
769 method = path = NULL;
771 if (reqtests[i].length == -1)
772 len = strlen (reqtests[i].request);
774 len = reqtests[i].length;
775 status = soup_headers_parse_request (reqtests[i].request, len,
776 headers, &method, &path,
778 if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
779 if ((reqtests[i].method && strcmp (reqtests[i].method, method) != 0) || !reqtests[i].method)
781 if ((reqtests[i].path && strcmp (reqtests[i].path, path) != 0) || !reqtests[i].path)
783 if (reqtests[i].version != version)
786 if (!check_headers (reqtests[i].headers, headers))
789 if (status != reqtests[i].status)
794 debug_printf (1, "OK!\n");
796 debug_printf (1, "BAD!\n");
798 if (reqtests[i].method) {
799 debug_printf (1, " expected: '%s' '%s' 'HTTP/1.%d'\n",
802 reqtests[i].version);
803 for (h = 0; reqtests[i].headers[h].name; h++) {
804 debug_printf (1, " '%s': '%s'\n",
805 reqtests[i].headers[h].name,
806 reqtests[i].headers[h].value);
809 debug_printf (1, " expected: %s\n",
810 soup_status_get_phrase (reqtests[i].status));
813 debug_printf (1, " got: '%s' '%s' 'HTTP/1.%d'\n",
814 method, path, version);
815 soup_message_headers_foreach (headers, print_header, NULL);
817 debug_printf (1, " got: %s\n",
818 soup_status_get_phrase (status));
824 soup_message_headers_free (headers);
826 debug_printf (1, "\n");
830 do_response_tests (void)
835 SoupHTTPVersion version;
836 SoupMessageHeaders *headers;
838 debug_printf (1, "Response tests\n");
839 for (i = 0; i < num_resptests; i++) {
842 debug_printf (1, "%2d. %s (%s): ", i + 1, resptests[i].description,
843 resptests[i].reason_phrase ? "should parse" : "should NOT parse");
845 headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
846 reason_phrase = NULL;
848 if (resptests[i].length == -1)
849 len = strlen (resptests[i].response);
851 len = resptests[i].length;
852 if (soup_headers_parse_response (resptests[i].response, len,
854 &status_code, &reason_phrase)) {
855 if (resptests[i].version != version)
857 if (resptests[i].status_code != status_code)
859 if ((resptests[i].reason_phrase && strcmp (resptests[i].reason_phrase, reason_phrase) != 0) || !resptests[i].reason_phrase)
862 if (!check_headers (resptests[i].headers, headers))
865 if (resptests[i].reason_phrase)
870 debug_printf (1, "OK!\n");
872 debug_printf (1, "BAD!\n");
874 if (resptests[i].reason_phrase) {
875 debug_printf (1, " expected: 'HTTP/1.%d' '%03d' '%s'\n",
876 resptests[i].version,
877 resptests[i].status_code,
878 resptests[i].reason_phrase);
879 for (h = 0; resptests[i].headers[h].name; h++) {
880 debug_printf (1, " '%s': '%s'\n",
881 resptests[i].headers[h].name,
882 resptests[i].headers[h].value);
885 debug_printf (1, " expected: parse error\n");
887 debug_printf (1, " got: 'HTTP/1.%d' '%03d' '%s'\n",
888 version, status_code, reason_phrase);
889 soup_message_headers_foreach (headers, print_header, NULL);
891 debug_printf (1, " got: parse error\n");
894 g_free (reason_phrase);
895 soup_message_headers_free (headers);
897 debug_printf (1, "\n");
901 do_qvalue_tests (void)
904 GSList *acceptable, *unacceptable, *iter;
907 debug_printf (1, "qvalue tests\n");
908 for (i = 0; i < num_qvaluetests; i++) {
909 debug_printf (1, "%2d. %s:\n", i + 1, qvaluetests[i].header_value);
912 acceptable = soup_header_parse_quality_list (qvaluetests[i].header_value,
915 debug_printf (1, " acceptable: ");
918 for (iter = acceptable, j = 0; iter; iter = iter->next, j++) {
919 debug_printf (1, "%s ", (char *)iter->data);
920 if (!qvaluetests[i].acceptable[j] ||
921 strcmp (iter->data, qvaluetests[i].acceptable[j]) != 0)
924 debug_printf (1, "\n");
925 soup_header_free_list (acceptable);
927 debug_printf (1, "(none)\n");
929 debug_printf (1, " WRONG! expected: ");
930 for (j = 0; qvaluetests[i].acceptable[j]; j++)
931 debug_printf (1, "%s ", qvaluetests[i].acceptable[j]);
932 debug_printf (1, "\n");
936 debug_printf (1, " unacceptable: ");
939 for (iter = unacceptable, j = 0; iter; iter = iter->next, j++) {
940 debug_printf (1, "%s ", (char *)iter->data);
941 if (!qvaluetests[i].unacceptable[j] ||
942 strcmp (iter->data, qvaluetests[i].unacceptable[j]) != 0)
945 debug_printf (1, "\n");
946 soup_header_free_list (unacceptable);
948 debug_printf (1, "(none)\n");
950 debug_printf (1, " WRONG! expected: ");
951 for (j = 0; qvaluetests[i].unacceptable[j]; j++)
952 debug_printf (1, "%s ", qvaluetests[i].unacceptable[j]);
953 debug_printf (1, "\n");
957 debug_printf (1, "\n");
961 #define RFC5987_TEST_FILENAME "t\xC3\xA9st.txt"
962 #define RFC5987_TEST_FALLBACK_FILENAME "test.txt"
964 #define RFC5987_TEST_HEADER_ENCODED "attachment; filename*=UTF-8''t%C3%A9st.txt"
966 #define RFC5987_TEST_HEADER_UTF8 "attachment; filename*=UTF-8''t%C3%A9st.txt; filename=\"test.txt\""
967 #define RFC5987_TEST_HEADER_ISO "attachment; filename=\"test.txt\"; filename*=iso-8859-1''t%E9st.txt"
968 #define RFC5987_TEST_HEADER_FALLBACK "attachment; filename*=Unknown''t%FF%FF%FFst.txt; filename=\"test.txt\""
971 do_content_disposition_tests (void)
973 SoupMessageHeaders *hdrs;
975 const char *header, *filename;
978 SoupMultipart *multipart;
979 SoupMessageBody *body;
981 debug_printf (1, "Content-Disposition tests\n");
983 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
984 params = g_hash_table_new (g_str_hash, g_str_equal);
985 g_hash_table_insert (params, "filename", RFC5987_TEST_FILENAME);
986 soup_message_headers_set_content_disposition (hdrs, "attachment", params);
987 g_hash_table_destroy (params);
989 header = soup_message_headers_get_one (hdrs, "Content-Disposition");
990 if (!strcmp (header, RFC5987_TEST_HEADER_ENCODED))
991 debug_printf (1, " encoded OK\n");
993 debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n",
994 RFC5987_TEST_HEADER_ENCODED, header);
999 soup_message_headers_clear (hdrs);
1000 soup_message_headers_append (hdrs, "Content-Disposition",
1001 RFC5987_TEST_HEADER_UTF8);
1002 if (!soup_message_headers_get_content_disposition (hdrs,
1005 debug_printf (1, " UTF-8 decoding FAILED!\n could not parse\n");
1009 g_free (disposition);
1011 filename = g_hash_table_lookup (params, "filename");
1013 debug_printf (1, " UTF-8 decoding FAILED!\n could not find filename\n");
1015 } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) {
1016 debug_printf (1, " UTF-8 decoding FAILED!\n expected: %s\n got: %s\n",
1017 RFC5987_TEST_FILENAME, filename);
1020 debug_printf (1, " UTF-8 decoded OK\n");
1021 g_hash_table_destroy (params);
1023 /* ISO-8859-1 decoding */
1024 soup_message_headers_clear (hdrs);
1025 soup_message_headers_append (hdrs, "Content-Disposition",
1026 RFC5987_TEST_HEADER_ISO);
1027 if (!soup_message_headers_get_content_disposition (hdrs,
1030 debug_printf (1, " iso-8859-1 decoding FAILED!\n could not parse\n");
1034 g_free (disposition);
1036 filename = g_hash_table_lookup (params, "filename");
1038 debug_printf (1, " iso-8859-1 decoding FAILED!\n could not find filename\n");
1040 } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) {
1041 debug_printf (1, " iso-8859-1 decoding FAILED!\n expected: %s\n got: %s\n",
1042 RFC5987_TEST_FILENAME, filename);
1045 debug_printf (1, " iso-8859-1 decoded OK\n");
1046 g_hash_table_destroy (params);
1049 soup_message_headers_clear (hdrs);
1050 soup_message_headers_append (hdrs, "Content-Disposition",
1051 RFC5987_TEST_HEADER_FALLBACK);
1052 if (!soup_message_headers_get_content_disposition (hdrs,
1055 debug_printf (1, " fallback decoding FAILED!\n could not parse\n");
1059 g_free (disposition);
1061 filename = g_hash_table_lookup (params, "filename");
1063 debug_printf (1, " fallback decoding FAILED!\n could not find filename\n");
1065 } else if (strcmp (filename, RFC5987_TEST_FALLBACK_FILENAME) != 0) {
1066 debug_printf (1, " fallback decoding FAILED!\n expected: %s\n got: %s\n",
1067 RFC5987_TEST_FALLBACK_FILENAME, filename);
1070 debug_printf (1, " fallback decoded OK\n");
1071 g_hash_table_destroy (params);
1073 soup_message_headers_free (hdrs);
1075 /* Ensure that soup-multipart always quotes filename (bug 641280) */
1076 multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART);
1077 buffer = soup_buffer_new (SOUP_MEMORY_STATIC, "foo", 3);
1078 soup_multipart_append_form_file (multipart, "test", "token",
1079 "text/plain", buffer);
1080 soup_buffer_free (buffer);
1082 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
1083 body = soup_message_body_new ();
1084 soup_multipart_to_message (multipart, hdrs, body);
1085 soup_message_headers_free (hdrs);
1086 soup_multipart_free (multipart);
1088 buffer = soup_message_body_flatten (body);
1089 soup_message_body_free (body);
1091 if (strstr (buffer->data, "filename=\"token\""))
1092 debug_printf (1, " SoupMultipart encoded filename correctly\n");
1094 debug_printf (1, " SoupMultipart encoded filename incorrectly!\n");
1097 soup_buffer_free (buffer);
1099 debug_printf (1, "\n");
1102 #define CONTENT_TYPE_TEST_MIME_TYPE "text/plain"
1103 #define CONTENT_TYPE_TEST_ATTRIBUTE "charset"
1104 #define CONTENT_TYPE_TEST_VALUE "US-ASCII"
1105 #define CONTENT_TYPE_TEST_HEADER "text/plain; charset=US-ASCII"
1107 #define CONTENT_TYPE_BAD_HEADER "plain text, not text/html"
1110 do_content_type_tests (void)
1112 SoupMessageHeaders *hdrs;
1114 const char *header, *mime_type;
1116 debug_printf (1, "Content-Type tests\n");
1118 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
1119 params = g_hash_table_new (g_str_hash, g_str_equal);
1120 g_hash_table_insert (params, CONTENT_TYPE_TEST_ATTRIBUTE,
1121 CONTENT_TYPE_TEST_VALUE);
1122 soup_message_headers_set_content_type (hdrs, CONTENT_TYPE_TEST_MIME_TYPE, params);
1123 g_hash_table_destroy (params);
1125 header = soup_message_headers_get_one (hdrs, "Content-Type");
1126 if (!strcmp (header, CONTENT_TYPE_TEST_HEADER))
1127 debug_printf (1, " encoded OK\n");
1129 debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n",
1130 CONTENT_TYPE_TEST_HEADER, header);
1134 soup_message_headers_clear (hdrs);
1135 soup_message_headers_append (hdrs, "Content-Type",
1136 CONTENT_TYPE_TEST_MIME_TYPE);
1137 /* Add a second Content-Type header: should be ignored */
1138 soup_message_headers_append (hdrs, "Content-Type",
1139 CONTENT_TYPE_TEST_MIME_TYPE);
1141 mime_type = soup_message_headers_get_content_type (hdrs, ¶ms);
1143 debug_printf (1, " decoding FAILED!\n could not parse\n");
1147 if (mime_type && strcmp (mime_type, CONTENT_TYPE_TEST_MIME_TYPE) != 0) {
1148 debug_printf (1, " decoding FAILED!\n bad returned MIME type: %s\n",
1151 } else if (params && g_hash_table_size (params) != 0) {
1152 debug_printf (1, " decoding FAILED!\n params contained %d params (should be 0)\n",
1153 g_hash_table_size (params));
1156 debug_printf (1, " decoded OK\n");
1159 g_hash_table_destroy (params);
1161 soup_message_headers_clear (hdrs);
1162 soup_message_headers_append (hdrs, "Content-Type",
1163 CONTENT_TYPE_BAD_HEADER);
1164 mime_type = soup_message_headers_get_content_type (hdrs, ¶ms);
1166 debug_printf (1, " Bad content rejection FAILED!\n");
1169 debug_printf (1, " Bad content rejection OK\n");
1171 soup_message_headers_free (hdrs);
1173 debug_printf (1, "\n");
1177 const char *name, *value;
1180 { "two", "test with spaces" },
1181 { "three", "test with \"quotes\" and \\s" },
1183 { "five", "test with \xC3\xA1\xC3\xA7\xC4\x89\xC3\xA8\xC3\xB1\xC5\xA3\xC5\xA1" }
1186 #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"
1189 do_append_param_tests (void)
1194 debug_printf (1, "soup_header_g_string_append_param() tests\n");
1196 params = g_string_new (NULL);
1197 for (i = 0; i < G_N_ELEMENTS (test_params); i++) {
1199 g_string_append (params, ", ");
1200 soup_header_g_string_append_param (params,
1201 test_params[i].name,
1202 test_params[i].value);
1204 if (strcmp (params->str, TEST_PARAMS_RESULT) != 0) {
1205 debug_printf (1, " FAILED!\n expected: %s\n got: %s\n",
1206 TEST_PARAMS_RESULT, params->str);
1209 debug_printf (1, " OK\n");
1210 g_string_free (params, TRUE);
1212 debug_printf (1, "\n");
1215 static const struct {
1216 const char *description, *name, *value;
1218 { "Empty name", "", "value" },
1219 { "Name with spaces", "na me", "value" },
1220 { "Name with colon", "na:me", "value" },
1221 { "Name with CR", "na\rme", "value" },
1222 { "Name with LF", "na\nme", "value" },
1223 { "Name with tab", "na\tme", "value" },
1224 { "Value with CR", "name", "val\rue" },
1225 { "Value with LF", "name", "val\nue" },
1226 { "Value with LWS", "name", "val\r\n ue" }
1230 do_bad_header_tests (void)
1232 SoupMessageHeaders *hdrs;
1235 debug_printf (1, "bad header rejection tests\n");
1237 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
1238 for (i = 0; i < G_N_ELEMENTS (bad_headers); i++) {
1239 debug_printf (1, " %s\n", bad_headers[i].description);
1240 expect_warning = TRUE;
1241 soup_message_headers_append (hdrs, bad_headers[i].name,
1242 bad_headers[i].value);
1243 if (expect_warning) {
1244 expect_warning = FALSE;
1245 debug_printf (1, " FAILED: soup_message_headers_append() did not reject it\n");
1249 soup_message_headers_free (hdrs);
1253 main (int argc, char **argv)
1255 test_init (argc, argv, NULL);
1257 do_request_tests ();
1258 do_response_tests ();
1260 do_content_disposition_tests ();
1261 do_content_type_tests ();
1262 do_append_param_tests ();
1263 do_bad_header_tests ();