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 /****************************/
181 /*** RECOVERABLE REQUESTS ***/
182 /****************************/
184 /* RFC 2616 section 4.1 says we SHOULD accept this */
186 { "Spurious leading CRLF",
187 "\r\nGET / HTTP/1.1\r\nHost: example.com\r\n", -1,
189 "GET", "/", SOUP_HTTP_1_1,
190 { { "Host", "example.com" },
195 /* RFC 2616 section 3.1 says we MUST accept this */
197 { "HTTP/01.01 request",
198 "GET / HTTP/01.01\r\nHost: example.com\r\n", -1,
200 "GET", "/", SOUP_HTTP_1_1,
201 { { "Host", "example.com" },
206 /* RFC 2616 section 19.3 says we SHOULD accept these */
208 { "LF instead of CRLF after header",
209 "GET / HTTP/1.1\nHost: example.com\nConnection: close\n", -1,
211 "GET", "/", SOUP_HTTP_1_1,
212 { { "Host", "example.com" },
213 { "Connection", "close" },
218 { "LF instead of CRLF after Request-Line",
219 "GET / HTTP/1.1\nHost: example.com\r\n", -1,
221 "GET", "/", SOUP_HTTP_1_1,
222 { { "Host", "example.com" },
227 { "Req w/ incorrect whitespace in Request-Line",
228 "GET /\tHTTP/1.1\r\nHost: example.com\r\n", -1,
230 "GET", "/", SOUP_HTTP_1_1,
231 { { "Host", "example.com" },
236 { "Req w/ incorrect whitespace after Request-Line",
237 "GET / HTTP/1.1 \r\nHost: example.com\r\n", -1,
239 "GET", "/", SOUP_HTTP_1_1,
240 { { "Host", "example.com" },
245 /* qv bug 579318, do_bad_header_tests() below */
246 { "Req w/ mangled header",
247 "GET / HTTP/1.1\r\nHost: example.com\r\nFoo one\r\nBar: two\r\n", -1,
249 "GET", "/", SOUP_HTTP_1_1,
250 { { "Host", "example.com" },
256 /************************/
257 /*** INVALID REQUESTS ***/
258 /************************/
260 { "HTTP 0.9 request; not supported",
262 SOUP_STATUS_BAD_REQUEST,
267 { "HTTP 1.2 request (no such thing)",
268 "GET / HTTP/1.2\r\n", -1,
269 SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
274 { "HTTP 2000 request (no such thing)",
275 "GET / HTTP/2000.0\r\n", -1,
276 SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
281 { "Non-HTTP request",
282 "GET / SOUP/1.1\r\nHost: example.com\r\n", -1,
283 SOUP_STATUS_BAD_REQUEST,
288 { "Junk after Request-Line",
289 "GET / HTTP/1.1 blah\r\nHost: example.com\r\n", -1,
290 SOUP_STATUS_BAD_REQUEST,
296 "G\x00T / HTTP/1.1\r\nHost: example.com\r\n", 37,
297 SOUP_STATUS_BAD_REQUEST,
303 "GET /\x00 HTTP/1.1\r\nHost: example.com\r\n", 38,
304 SOUP_STATUS_BAD_REQUEST,
310 "GET / HTTP/1.1\r\nHost: example\x00com\r\n", 37,
311 SOUP_STATUS_BAD_REQUEST,
316 { "No terminating CRLF",
317 "GET / HTTP/1.1\r\nHost: example.com", -1,
318 SOUP_STATUS_BAD_REQUEST,
323 { "Unrecognized expectation",
324 "GET / HTTP/1.1\r\nHost: example.com\r\nExpect: the-impossible\r\n", -1,
325 SOUP_STATUS_EXPECTATION_FAILED,
330 static const int num_reqtests = G_N_ELEMENTS (reqtests);
332 static struct ResponseTest {
333 const char *description;
334 const char *response;
336 SoupHTTPVersion version;
338 const char *reason_phrase;
341 /***********************/
342 /*** VALID RESPONSES ***/
343 /***********************/
345 { "HTTP 1.0 response w/ no headers",
346 "HTTP/1.0 200 ok\r\n", -1,
347 SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
351 { "HTTP 1.1 response w/ no headers",
352 "HTTP/1.1 200 ok\r\n", -1,
353 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
357 { "Response w/ multi-word Reason-Phrase",
358 "HTTP/1.1 400 bad request\r\n", -1,
359 SOUP_HTTP_1_1, SOUP_STATUS_BAD_REQUEST, "bad request",
363 { "Response w/ 1 header",
364 "HTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
365 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
371 { "Response w/ 2 headers",
372 "HTTP/1.1 200 ok\r\nFoo: bar\r\nBaz: quux\r\n", -1,
373 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
380 { "Response w/ same header multiple times",
381 "HTTP/1.1 200 ok\r\nFoo: bar\r\nFoo: baz\r\nFoo: quux\r\n", -1,
382 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
383 { { "Foo", "bar, baz, quux" },
388 { "Response w/ no reason phrase",
389 "HTTP/1.1 200 \r\nFoo: bar\r\n", -1,
390 SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
396 { "Connection header on HTTP/1.0 message",
397 "HTTP/1.0 200 ok\r\nFoo: bar\r\nConnection: Bar\r\nBar: quux\r\n", -1,
398 SOUP_HTTP_1_0, SOUP_STATUS_OK, "ok",
400 { "Connection", "Bar" },
405 /*****************************/
406 /*** RECOVERABLE RESPONSES ***/
407 /*****************************/
409 /* RFC 2616 section 3.1 says we MUST accept this */
411 { "HTTP/01.01 response",
412 "HTTP/01.01 200 ok\r\nFoo: bar\r\n", -1,
413 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
419 /* RFC 2616 section 19.3 says we SHOULD accept these */
421 { "Response w/ LF instead of CRLF after Status-Line",
422 "HTTP/1.1 200 ok\nFoo: bar\r\n", -1,
423 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
429 { "Response w/ incorrect spacing in Status-Line",
430 "HTTP/1.1 200\tok\r\nFoo: bar\r\n", -1,
431 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
437 { "Response w/ no reason phrase or preceding SP",
438 "HTTP/1.1 200\r\nFoo: bar\r\n", -1,
439 SOUP_HTTP_1_1, SOUP_STATUS_OK, "",
445 { "Response w/ no whitespace after status code",
446 "HTTP/1.1 200ok\r\nFoo: bar\r\n", -1,
447 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
453 /* Shoutcast support */
454 { "Shoutcast server not-quite-HTTP",
455 "ICY 200 OK\r\nFoo: bar\r\n", -1,
456 SOUP_HTTP_1_0, SOUP_STATUS_OK, "OK",
462 /* qv bug 579318, do_bad_header_tests() below */
463 { "Response w/ mangled header",
464 "HTTP/1.1 200 ok\r\nFoo: one\r\nBar two:2\r\nBaz: three\r\n", -1,
465 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
473 { "HTTP 1.1 response with leading line break",
474 "\nHTTP/1.1 200 ok\r\nFoo: bar\r\n", -1,
475 SOUP_HTTP_1_1, SOUP_STATUS_OK, "ok",
480 /*************************/
481 /*** INVALID RESPONSES ***/
482 /*************************/
484 { "Invalid HTTP version",
485 "HTTP/1.2 200 OK\r\nFoo: bar\r\n", -1,
490 { "Non-HTTP response",
491 "SOUP/1.1 200 OK\r\nFoo: bar\r\n", -1,
496 { "Non-numeric status code",
497 "HTTP/1.1 XXX OK\r\nFoo: bar\r\n", -1,
503 "HTTP/1.1 OK\r\nFoo: bar\r\n", -1,
508 { "One-digit status code",
509 "HTTP/1.1 2 OK\r\nFoo: bar\r\n", -1,
514 { "Two-digit status code",
515 "HTTP/1.1 20 OK\r\nFoo: bar\r\n", -1,
520 { "Four-digit status code",
521 "HTTP/1.1 2000 OK\r\nFoo: bar\r\n", -1,
526 { "Status code < 100",
527 "HTTP/1.1 001 OK\r\nFoo: bar\r\n", -1,
532 { "Status code > 599",
533 "HTTP/1.1 600 OK\r\nFoo: bar\r\n", -1,
538 { "NUL in Reason Phrase",
539 "HTTP/1.1 200 O\x00K\r\nFoo: bar\r\n", 28,
545 "HTTP/1.1 200 OK\r\nFoo: b\x00ar\r\n", 28,
550 static const int num_resptests = G_N_ELEMENTS (resptests);
552 static struct QValueTest {
553 const char *header_value;
554 const char *acceptable[7];
555 const char *unacceptable[2];
557 { "text/plain; q=0.5, text/html,\t text/x-dvi; q=0.8, text/x-c",
558 { "text/html", "text/x-c", "text/x-dvi", "text/plain", NULL },
562 { "text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5",
563 { "text/html;level=1", "text/html", "*/*", "text/html;level=2",
568 { "gzip;q=1.0, identity; q=0.5, *;q=0",
569 { "gzip", "identity", NULL },
573 static const int num_qvaluetests = G_N_ELEMENTS (qvaluetests);
576 print_header (const char *name, const char *value, gpointer data)
578 debug_printf (1, " '%s': '%s'\n", name, value);
582 check_headers (Header *headers, SoupMessageHeaders *hdrs)
584 GSList *header_names, *h;
585 SoupMessageHeadersIter iter;
586 const char *name, *value;
591 soup_message_headers_iter_init (&iter, hdrs);
592 while (soup_message_headers_iter_next (&iter, &name, &value)) {
593 if (!g_slist_find_custom (header_names, name,
594 (GCompareFunc)strcmp))
595 header_names = g_slist_append (header_names, (char *)name);
598 for (i = 0, h = header_names; headers[i].name && h; i++, h = h->next) {
599 if (strcmp (h->data, headers[i].name) != 0) {
603 value = soup_message_headers_get_list (hdrs, headers[i].name);
604 if (strcmp (value, headers[i].value) != 0) {
609 if (headers[i].name || h)
611 g_slist_free (header_names);
616 do_request_tests (void)
620 SoupHTTPVersion version;
621 SoupMessageHeaders *headers;
624 debug_printf (1, "Request tests\n");
625 for (i = 0; i < num_reqtests; i++) {
628 debug_printf (1, "%2d. %s (%s): ", i + 1, reqtests[i].description,
629 soup_status_get_phrase (reqtests[i].status));
631 headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
632 method = path = NULL;
634 if (reqtests[i].length == -1)
635 len = strlen (reqtests[i].request);
637 len = reqtests[i].length;
638 status = soup_headers_parse_request (reqtests[i].request, len,
639 headers, &method, &path,
641 if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
642 if ((reqtests[i].method && strcmp (reqtests[i].method, method) != 0) || !reqtests[i].method)
644 if ((reqtests[i].path && strcmp (reqtests[i].path, path) != 0) || !reqtests[i].path)
646 if (reqtests[i].version != version)
649 if (!check_headers (reqtests[i].headers, headers))
652 if (status != reqtests[i].status)
657 debug_printf (1, "OK!\n");
659 debug_printf (1, "BAD!\n");
661 if (reqtests[i].method) {
662 debug_printf (1, " expected: '%s' '%s' 'HTTP/1.%d'\n",
665 reqtests[i].version);
666 for (h = 0; reqtests[i].headers[h].name; h++) {
667 debug_printf (1, " '%s': '%s'\n",
668 reqtests[i].headers[h].name,
669 reqtests[i].headers[h].value);
672 debug_printf (1, " expected: %s\n",
673 soup_status_get_phrase (reqtests[i].status));
676 debug_printf (1, " got: '%s' '%s' 'HTTP/1.%d'\n",
677 method, path, version);
678 soup_message_headers_foreach (headers, print_header, NULL);
680 debug_printf (1, " got: %s\n",
681 soup_status_get_phrase (status));
687 soup_message_headers_free (headers);
689 debug_printf (1, "\n");
693 do_response_tests (void)
698 SoupHTTPVersion version;
699 SoupMessageHeaders *headers;
701 debug_printf (1, "Response tests\n");
702 for (i = 0; i < num_resptests; i++) {
705 debug_printf (1, "%2d. %s (%s): ", i + 1, resptests[i].description,
706 resptests[i].reason_phrase ? "should parse" : "should NOT parse");
708 headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
709 reason_phrase = NULL;
711 if (resptests[i].length == -1)
712 len = strlen (resptests[i].response);
714 len = resptests[i].length;
715 if (soup_headers_parse_response (resptests[i].response, len,
717 &status_code, &reason_phrase)) {
718 if (resptests[i].version != version)
720 if (resptests[i].status_code != status_code)
722 if ((resptests[i].reason_phrase && strcmp (resptests[i].reason_phrase, reason_phrase) != 0) || !resptests[i].reason_phrase)
725 if (!check_headers (resptests[i].headers, headers))
728 if (resptests[i].reason_phrase)
733 debug_printf (1, "OK!\n");
735 debug_printf (1, "BAD!\n");
737 if (resptests[i].reason_phrase) {
738 debug_printf (1, " expected: 'HTTP/1.%d' '%03d' '%s'\n",
739 resptests[i].version,
740 resptests[i].status_code,
741 resptests[i].reason_phrase);
742 for (h = 0; resptests[i].headers[h].name; h++) {
743 debug_printf (1, " '%s': '%s'\n",
744 resptests[i].headers[h].name,
745 resptests[i].headers[h].value);
748 debug_printf (1, " expected: parse error\n");
750 debug_printf (1, " got: 'HTTP/1.%d' '%03d' '%s'\n",
751 version, status_code, reason_phrase);
752 soup_message_headers_foreach (headers, print_header, NULL);
754 debug_printf (1, " got: parse error\n");
757 g_free (reason_phrase);
758 soup_message_headers_free (headers);
760 debug_printf (1, "\n");
764 do_qvalue_tests (void)
767 GSList *acceptable, *unacceptable, *iter;
770 debug_printf (1, "qvalue tests\n");
771 for (i = 0; i < num_qvaluetests; i++) {
772 debug_printf (1, "%2d. %s:\n", i + 1, qvaluetests[i].header_value);
775 acceptable = soup_header_parse_quality_list (qvaluetests[i].header_value,
778 debug_printf (1, " acceptable: ");
781 for (iter = acceptable, j = 0; iter; iter = iter->next, j++) {
782 debug_printf (1, "%s ", (char *)iter->data);
783 if (!qvaluetests[i].acceptable[j] ||
784 strcmp (iter->data, qvaluetests[i].acceptable[j]) != 0)
787 debug_printf (1, "\n");
788 soup_header_free_list (acceptable);
790 debug_printf (1, "(none)\n");
792 debug_printf (1, " WRONG! expected: ");
793 for (j = 0; qvaluetests[i].acceptable[j]; j++)
794 debug_printf (1, "%s ", qvaluetests[i].acceptable[j]);
795 debug_printf (1, "\n");
799 debug_printf (1, " unacceptable: ");
802 for (iter = unacceptable, j = 0; iter; iter = iter->next, j++) {
803 debug_printf (1, "%s ", (char *)iter->data);
804 if (!qvaluetests[i].unacceptable[j] ||
805 strcmp (iter->data, qvaluetests[i].unacceptable[j]) != 0)
808 debug_printf (1, "\n");
809 soup_header_free_list (unacceptable);
811 debug_printf (1, "(none)\n");
813 debug_printf (1, " WRONG! expected: ");
814 for (j = 0; qvaluetests[i].unacceptable[j]; j++)
815 debug_printf (1, "%s ", qvaluetests[i].unacceptable[j]);
816 debug_printf (1, "\n");
820 debug_printf (1, "\n");
824 #define RFC5987_TEST_FILENAME "t\xC3\xA9st.txt"
825 #define RFC5987_TEST_FALLBACK_FILENAME "test.txt"
827 #define RFC5987_TEST_HEADER_ENCODED "attachment; filename*=UTF-8''t%C3%A9st.txt"
829 #define RFC5987_TEST_HEADER_UTF8 "attachment; filename*=UTF-8''t%C3%A9st.txt; filename=\"test.txt\""
830 #define RFC5987_TEST_HEADER_ISO "attachment; filename=\"test.txt\"; filename*=iso-8859-1''t%E9st.txt"
831 #define RFC5987_TEST_HEADER_FALLBACK "attachment; filename*=Unknown''t%FF%FF%FFst.txt; filename=\"test.txt\""
834 do_content_disposition_tests (void)
836 SoupMessageHeaders *hdrs;
838 const char *header, *filename;
841 SoupMultipart *multipart;
842 SoupMessageBody *body;
844 debug_printf (1, "Content-Disposition tests\n");
846 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
847 params = g_hash_table_new (g_str_hash, g_str_equal);
848 g_hash_table_insert (params, "filename", RFC5987_TEST_FILENAME);
849 soup_message_headers_set_content_disposition (hdrs, "attachment", params);
850 g_hash_table_destroy (params);
852 header = soup_message_headers_get_one (hdrs, "Content-Disposition");
853 if (!strcmp (header, RFC5987_TEST_HEADER_ENCODED))
854 debug_printf (1, " encoded OK\n");
856 debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n",
857 RFC5987_TEST_HEADER_ENCODED, header);
862 soup_message_headers_clear (hdrs);
863 soup_message_headers_append (hdrs, "Content-Disposition",
864 RFC5987_TEST_HEADER_UTF8);
865 if (!soup_message_headers_get_content_disposition (hdrs,
868 debug_printf (1, " UTF-8 decoding FAILED!\n could not parse\n");
872 g_free (disposition);
874 filename = g_hash_table_lookup (params, "filename");
876 debug_printf (1, " UTF-8 decoding FAILED!\n could not find filename\n");
878 } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) {
879 debug_printf (1, " UTF-8 decoding FAILED!\n expected: %s\n got: %s\n",
880 RFC5987_TEST_FILENAME, filename);
883 debug_printf (1, " UTF-8 decoded OK\n");
884 g_hash_table_destroy (params);
886 /* ISO-8859-1 decoding */
887 soup_message_headers_clear (hdrs);
888 soup_message_headers_append (hdrs, "Content-Disposition",
889 RFC5987_TEST_HEADER_ISO);
890 if (!soup_message_headers_get_content_disposition (hdrs,
893 debug_printf (1, " iso-8859-1 decoding FAILED!\n could not parse\n");
897 g_free (disposition);
899 filename = g_hash_table_lookup (params, "filename");
901 debug_printf (1, " iso-8859-1 decoding FAILED!\n could not find filename\n");
903 } else if (strcmp (filename, RFC5987_TEST_FILENAME) != 0) {
904 debug_printf (1, " iso-8859-1 decoding FAILED!\n expected: %s\n got: %s\n",
905 RFC5987_TEST_FILENAME, filename);
908 debug_printf (1, " iso-8859-1 decoded OK\n");
909 g_hash_table_destroy (params);
912 soup_message_headers_clear (hdrs);
913 soup_message_headers_append (hdrs, "Content-Disposition",
914 RFC5987_TEST_HEADER_FALLBACK);
915 if (!soup_message_headers_get_content_disposition (hdrs,
918 debug_printf (1, " fallback decoding FAILED!\n could not parse\n");
922 g_free (disposition);
924 filename = g_hash_table_lookup (params, "filename");
926 debug_printf (1, " fallback decoding FAILED!\n could not find filename\n");
928 } else if (strcmp (filename, RFC5987_TEST_FALLBACK_FILENAME) != 0) {
929 debug_printf (1, " fallback decoding FAILED!\n expected: %s\n got: %s\n",
930 RFC5987_TEST_FALLBACK_FILENAME, filename);
933 debug_printf (1, " fallback decoded OK\n");
934 g_hash_table_destroy (params);
936 soup_message_headers_free (hdrs);
938 /* Ensure that soup-multipart always quotes filename (bug 641280) */
939 multipart = soup_multipart_new (SOUP_FORM_MIME_TYPE_MULTIPART);
940 buffer = soup_buffer_new (SOUP_MEMORY_STATIC, "foo", 3);
941 soup_multipart_append_form_file (multipart, "test", "token",
942 "text/plain", buffer);
943 soup_buffer_free (buffer);
945 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
946 body = soup_message_body_new ();
947 soup_multipart_to_message (multipart, hdrs, body);
948 soup_message_headers_free (hdrs);
949 soup_multipart_free (multipart);
951 buffer = soup_message_body_flatten (body);
952 soup_message_body_free (body);
954 if (strstr (buffer->data, "filename=\"token\""))
955 debug_printf (1, " SoupMultipart encoded filename correctly\n");
957 debug_printf (1, " SoupMultipart encoded filename incorrectly!\n");
960 soup_buffer_free (buffer);
962 debug_printf (1, "\n");
965 #define CONTENT_TYPE_TEST_MIME_TYPE "text/plain"
966 #define CONTENT_TYPE_TEST_ATTRIBUTE "charset"
967 #define CONTENT_TYPE_TEST_VALUE "US-ASCII"
968 #define CONTENT_TYPE_TEST_HEADER "text/plain; charset=US-ASCII"
970 #define CONTENT_TYPE_BAD_HEADER "plain text, not text/html"
973 do_content_type_tests (void)
975 SoupMessageHeaders *hdrs;
977 const char *header, *mime_type;
979 debug_printf (1, "Content-Type tests\n");
981 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
982 params = g_hash_table_new (g_str_hash, g_str_equal);
983 g_hash_table_insert (params, CONTENT_TYPE_TEST_ATTRIBUTE,
984 CONTENT_TYPE_TEST_VALUE);
985 soup_message_headers_set_content_type (hdrs, CONTENT_TYPE_TEST_MIME_TYPE, params);
986 g_hash_table_destroy (params);
988 header = soup_message_headers_get_one (hdrs, "Content-Type");
989 if (!strcmp (header, CONTENT_TYPE_TEST_HEADER))
990 debug_printf (1, " encoded OK\n");
992 debug_printf (1, " encoding FAILED!\n expected: %s\n got: %s\n",
993 CONTENT_TYPE_TEST_HEADER, header);
997 soup_message_headers_clear (hdrs);
998 soup_message_headers_append (hdrs, "Content-Type",
999 CONTENT_TYPE_TEST_MIME_TYPE);
1000 /* Add a second Content-Type header: should be ignored */
1001 soup_message_headers_append (hdrs, "Content-Type",
1002 CONTENT_TYPE_TEST_MIME_TYPE);
1004 mime_type = soup_message_headers_get_content_type (hdrs, ¶ms);
1006 debug_printf (1, " decoding FAILED!\n could not parse\n");
1010 if (mime_type && strcmp (mime_type, CONTENT_TYPE_TEST_MIME_TYPE) != 0) {
1011 debug_printf (1, " decoding FAILED!\n bad returned MIME type: %s\n",
1014 } else if (params && g_hash_table_size (params) != 0) {
1015 debug_printf (1, " decoding FAILED!\n params contained %d params (should be 0)\n",
1016 g_hash_table_size (params));
1019 debug_printf (1, " decoded OK\n");
1022 g_hash_table_destroy (params);
1024 soup_message_headers_clear (hdrs);
1025 soup_message_headers_append (hdrs, "Content-Type",
1026 CONTENT_TYPE_BAD_HEADER);
1027 mime_type = soup_message_headers_get_content_type (hdrs, ¶ms);
1029 debug_printf (1, " Bad content rejection FAILED!\n");
1032 debug_printf (1, " Bad content rejection OK\n");
1034 soup_message_headers_free (hdrs);
1036 debug_printf (1, "\n");
1040 const char *name, *value;
1043 { "two", "test with spaces" },
1044 { "three", "test with \"quotes\" and \\s" },
1046 { "five", "test with \xC3\xA1\xC3\xA7\xC4\x89\xC3\xA8\xC3\xB1\xC5\xA3\xC5\xA1" }
1049 #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"
1052 do_append_param_tests (void)
1057 debug_printf (1, "soup_header_g_string_append_param() tests\n");
1059 params = g_string_new (NULL);
1060 for (i = 0; i < G_N_ELEMENTS (test_params); i++) {
1062 g_string_append (params, ", ");
1063 soup_header_g_string_append_param (params,
1064 test_params[i].name,
1065 test_params[i].value);
1067 if (strcmp (params->str, TEST_PARAMS_RESULT) != 0) {
1068 debug_printf (1, " FAILED!\n expected: %s\n got: %s\n",
1069 TEST_PARAMS_RESULT, params->str);
1072 debug_printf (1, " OK\n");
1073 g_string_free (params, TRUE);
1075 debug_printf (1, "\n");
1078 static const struct {
1079 const char *description, *name, *value;
1081 { "Empty name", "", "value" },
1082 { "Name with spaces", "na me", "value" },
1083 { "Name with colon", "na:me", "value" },
1084 { "Name with CR", "na\rme", "value" },
1085 { "Name with LF", "na\nme", "value" },
1086 { "Name with tab", "na\tme", "value" },
1087 { "Value with CR", "name", "val\rue" },
1088 { "Value with LF", "name", "val\nue" },
1089 { "Value with LWS", "name", "val\r\n ue" }
1093 do_bad_header_tests (void)
1095 SoupMessageHeaders *hdrs;
1098 debug_printf (1, "bad header rejection tests\n");
1100 hdrs = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
1101 for (i = 0; i < G_N_ELEMENTS (bad_headers); i++) {
1102 debug_printf (1, " %s\n", bad_headers[i].description);
1103 expect_warning = TRUE;
1104 soup_message_headers_append (hdrs, bad_headers[i].name,
1105 bad_headers[i].value);
1106 if (expect_warning) {
1107 expect_warning = FALSE;
1108 debug_printf (1, " FAILED: soup_message_headers_append() did not reject it\n");
1112 soup_message_headers_free (hdrs);
1116 main (int argc, char **argv)
1118 test_init (argc, argv, NULL);
1120 do_request_tests ();
1121 do_response_tests ();
1123 do_content_disposition_tests ();
1124 do_content_type_tests ();
1125 do_append_param_tests ();
1126 do_bad_header_tests ();