1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2008 Red Hat, Inc.
6 #include "test-utils.h"
10 SoupSession *async_session, *sync_session;
20 TestRequest requests[3];
25 static TestCase tests[] = {
26 /* A redirecty response to a GET or HEAD should cause a redirect */
28 { { { "GET", "/301", 301 },
30 { NULL } }, 200, NULL },
31 { { { "GET", "/302", 302 },
33 { NULL } }, 200, NULL },
34 { { { "GET", "/303", 303 },
36 { NULL } }, 200, NULL },
37 { { { "GET", "/307", 307 },
39 { NULL } }, 200, NULL },
40 { { { "GET", "/308", 308 },
42 { NULL } }, 200, NULL },
43 { { { "HEAD", "/301", 301 },
45 { NULL } }, 200, "551190" },
46 { { { "HEAD", "/302", 302 },
48 { NULL } }, 200, "551190" },
49 /* 303 is a nonsensical response to HEAD, but some sites do
52 { { { "HEAD", "/303", 303 },
54 { NULL } }, 200, "600830" },
55 { { { "HEAD", "/307", 307 },
57 { NULL } }, 200, "551190" },
58 { { { "HEAD", "/308", 308 },
60 { NULL } }, 200, "551190" },
62 /* A non-redirecty response to a GET or HEAD should not */
64 { { { "GET", "/300", 300 },
65 { NULL } }, 300, NULL },
66 { { { "GET", "/304", 304 },
67 { NULL } }, 304, NULL },
68 { { { "GET", "/305", 305 },
69 { NULL } }, 305, NULL },
70 { { { "GET", "/306", 306 },
71 { NULL } }, 306, NULL },
72 { { { "HEAD", "/300", 300 },
73 { NULL } }, 300, "551190" },
74 { { { "HEAD", "/304", 304 },
75 { NULL } }, 304, "551190" },
76 { { { "HEAD", "/305", 305 },
77 { NULL } }, 305, "551190" },
78 { { { "HEAD", "/306", 306 },
79 { NULL } }, 306, "551190" },
81 /* Test double-redirect */
83 { { { "GET", "/301/302", 301 },
84 { "GET", "/302", 302 },
85 { "GET", "/", 200 } }, 200, NULL },
86 { { { "HEAD", "/301/302", 301 },
87 { "HEAD", "/302", 302 },
88 { "HEAD", "/", 200 } }, 200, "551190" },
90 /* POST should only automatically redirect on 301, 302 and 303 */
92 { { { "POST", "/301", 301 },
94 { NULL } }, 200, "586692" },
95 { { { "POST", "/302", 302 },
97 { NULL } }, 200, NULL },
98 { { { "POST", "/303", 303 },
100 { NULL } }, 200, NULL },
101 { { { "POST", "/307", 307 },
102 { NULL } }, 307, NULL },
104 /* Test behavior with recoverably-bad Location header */
105 { { { "GET", "/bad", 302 },
106 { "GET", "/bad%20with%20spaces", 200 },
107 { NULL } }, 200, "566530" },
109 /* Test behavior with irrecoverably-bad Location header */
110 { { { "GET", "/bad-no-host", 302 },
111 { NULL } }, SOUP_STATUS_MALFORMED, "528882" },
113 /* Test infinite redirection */
114 { { { "GET", "/bad-recursive", 302, TRUE },
115 { NULL } }, SOUP_STATUS_TOO_MANY_REDIRECTS, "604383" },
117 /* Test redirection to a different server */
118 { { { "GET", "/server2", 302 },
119 { "GET", "/on-server2", 200 },
120 { NULL } }, 200, NULL },
122 static const int n_tests = G_N_ELEMENTS (tests);
125 got_headers (SoupMessage *msg, gpointer user_data)
127 TestRequest **treq = user_data;
128 const char *location;
130 debug_printf (2, " -> %d %s\n", msg->status_code,
132 location = soup_message_headers_get_one (msg->response_headers,
135 debug_printf (2, " Location: %s\n", location);
137 if (!(*treq)->method)
140 soup_test_assert_message_status (msg, (*treq)->status_code);
144 restarted (SoupMessage *msg, gpointer user_data)
146 TestRequest **treq = user_data;
147 SoupURI *uri = soup_message_get_uri (msg);
149 debug_printf (2, " %s %s\n", msg->method, uri->path);
151 if ((*treq)->method && !(*treq)->repeat)
154 soup_test_assert ((*treq)->method,
155 "Expected to be done");
157 g_assert_cmpstr (msg->method, ==, (*treq)->method);
158 g_assert_cmpstr (uri->path, ==, (*treq)->path);
162 do_message_api_test (SoupSession *session, TestCase *test)
169 g_test_bug (test->bugref);
171 uri = soup_uri_new_with_base (base_uri, test->requests[0].path);
172 msg = soup_message_new_from_uri (test->requests[0].method, uri);
175 if (msg->method == SOUP_METHOD_POST) {
176 soup_message_set_request (msg, "text/plain",
179 strlen ("post body"));
182 treq = &test->requests[0];
183 g_signal_connect (msg, "got_headers",
184 G_CALLBACK (got_headers), &treq);
185 g_signal_connect (msg, "restarted",
186 G_CALLBACK (restarted), &treq);
188 soup_session_send_message (session, msg);
190 soup_test_assert_message_status (msg, test->final_status);
192 g_object_unref (msg);
196 do_request_api_test (SoupSession *session, TestCase *test)
199 SoupRequestHTTP *reqh;
202 GInputStream *stream;
203 GError *error = NULL;
206 g_test_bug (test->bugref);
208 uri = soup_uri_new_with_base (base_uri, test->requests[0].path);
209 reqh = soup_session_request_http_uri (session,
210 test->requests[0].method,
213 g_assert_no_error (error);
215 g_error_free (error);
219 msg = soup_request_http_get_message (reqh);
220 if (msg->method == SOUP_METHOD_POST) {
221 soup_message_set_request (msg, "text/plain",
224 strlen ("post body"));
227 treq = &test->requests[0];
228 g_signal_connect (msg, "got_headers",
229 G_CALLBACK (got_headers), &treq);
230 g_signal_connect (msg, "restarted",
231 G_CALLBACK (restarted), &treq);
233 stream = soup_test_request_send (SOUP_REQUEST (reqh), NULL, 0, &error);
235 if (SOUP_STATUS_IS_TRANSPORT_ERROR (test->final_status) &&
236 test->final_status != SOUP_STATUS_MALFORMED) {
237 g_assert_error (error, SOUP_HTTP_ERROR, test->final_status);
238 g_clear_error (&error);
240 g_assert_null (stream);
241 g_clear_object (&stream);
243 g_object_unref (msg);
244 g_object_unref (reqh);
248 g_assert_no_error (error);
250 g_error_free (error);
251 g_object_unref (msg);
252 g_object_unref (reqh);
256 soup_test_request_read_all (SOUP_REQUEST (reqh), stream, NULL, &error);
257 g_assert_no_error (error);
258 g_clear_error (&error);
260 soup_test_request_close_stream (SOUP_REQUEST (reqh), stream, NULL, &error);
261 g_assert_no_error (error);
262 g_clear_error (&error);
263 g_object_unref (stream);
265 if (test->final_status == SOUP_STATUS_MALFORMED)
266 g_assert_cmpint (msg->status_code, ==, test->requests[0].status_code);
268 g_assert_cmpint (msg->status_code, ==, test->final_status);
270 g_object_unref (msg);
271 g_object_unref (reqh);
275 do_async_msg_api_test (gconstpointer test)
277 do_message_api_test (async_session, (TestCase *)test);
281 do_async_req_api_test (gconstpointer test)
283 do_request_api_test (async_session, (TestCase *)test);
287 do_sync_msg_api_test (gconstpointer test)
289 do_message_api_test (sync_session, (TestCase *)test);
293 do_sync_req_api_test (gconstpointer test)
295 do_request_api_test (sync_session, (TestCase *)test);
299 server_callback (SoupServer *server, SoupMessage *msg,
300 const char *path, GHashTable *query,
301 SoupClientContext *context, gpointer data)
306 /* Make sure that a HTTP/1.0 redirect doesn't cause an
307 * HTTP/1.0 re-request. (#521848)
309 if (soup_message_get_http_version (msg) == SOUP_HTTP_1_0) {
310 soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
314 if (g_str_has_prefix (path, "/bad")) {
315 if (!strcmp (path, "/bad")) {
316 soup_message_set_status (msg, SOUP_STATUS_FOUND);
317 soup_message_headers_replace (msg->response_headers,
320 } else if (!strcmp (path, "/bad-recursive")) {
321 soup_message_set_status (msg, SOUP_STATUS_FOUND);
322 soup_message_headers_replace (msg->response_headers,
325 } else if (!strcmp (path, "/bad-no-host")) {
326 soup_message_set_status (msg, SOUP_STATUS_FOUND);
327 soup_message_headers_replace (msg->response_headers,
330 } else if (!strcmp (path, "/bad with spaces"))
331 soup_message_set_status (msg, SOUP_STATUS_OK);
333 soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
335 } else if (!strcmp (path, "/server2")) {
336 soup_message_set_status (msg, SOUP_STATUS_FOUND);
337 soup_message_headers_replace (msg->response_headers,
341 } else if (!strcmp (path, "/")) {
342 if (msg->method != SOUP_METHOD_GET &&
343 msg->method != SOUP_METHOD_HEAD) {
344 soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
348 /* Make sure that redirecting a POST clears the body */
349 if (msg->request_body->length) {
350 soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
354 soup_message_set_status (msg, SOUP_STATUS_OK);
356 /* FIXME: this is wrong, though it doesn't matter for
357 * the purposes of this test, and to do the right
358 * thing currently we'd have to set Content-Length by
361 if (msg->method != SOUP_METHOD_HEAD) {
362 soup_message_set_response (msg, "text/plain",
369 status_code = strtoul (path + 1, &remainder, 10);
370 if (!SOUP_STATUS_IS_REDIRECTION (status_code) ||
371 (*remainder && *remainder != '/')) {
372 soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
376 /* See above comment re bug 521848. We only test this on the
377 * double-redirects so that we get connection-reuse testing
378 * the rest of the time.
380 if (*remainder == '/')
381 soup_message_set_http_version (msg, SOUP_HTTP_1_0);
383 soup_message_set_redirect (msg, status_code,
384 *remainder ? remainder : "/");
388 server2_callback (SoupServer *server, SoupMessage *msg,
389 const char *path, GHashTable *query,
390 SoupClientContext *context, gpointer data)
392 soup_message_set_status (msg, SOUP_STATUS_OK);
396 main (int argc, char **argv)
399 SoupServer *server, *server2;
404 test_init (argc, argv, NULL);
406 server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
407 soup_server_add_handler (server, NULL,
408 server_callback, NULL, NULL);
409 base_uri = soup_test_server_get_uri (server, "http", NULL);
411 server2 = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
412 soup_server_add_handler (server2, NULL,
413 server2_callback, NULL, NULL);
414 uri2 = soup_test_server_get_uri (server2, "http", NULL);
415 soup_uri_set_path (uri2, "/on-server2");
416 server2_uri = soup_uri_to_string (uri2, FALSE);
417 soup_uri_free (uri2);
419 loop = g_main_loop_new (NULL, TRUE);
421 async_session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
422 SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
424 sync_session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
426 for (n = 0; n < n_tests; n++) {
427 path = g_strdup_printf ("/redirect/async/msg/%d-%s-%d", n,
428 tests[n].requests[0].method,
429 tests[n].requests[0].status_code);
430 g_test_add_data_func (path, &tests[n], do_async_msg_api_test);
433 path = g_strdup_printf ("/redirect/async/req/%d-%s-%d", n,
434 tests[n].requests[0].method,
435 tests[n].requests[0].status_code);
436 g_test_add_data_func (path, &tests[n], do_async_req_api_test);
439 path = g_strdup_printf ("/redirect/sync/msg/%d-%s-%d", n,
440 tests[n].requests[0].method,
441 tests[n].requests[0].status_code);
442 g_test_add_data_func (path, &tests[n], do_sync_msg_api_test);
445 path = g_strdup_printf ("/redirect/sync/req/%d-%s-%d", n,
446 tests[n].requests[0].method,
447 tests[n].requests[0].status_code);
448 g_test_add_data_func (path, &tests[n], do_sync_req_api_test);
454 g_main_loop_unref (loop);
455 soup_uri_free (base_uri);
456 soup_test_server_quit_unref (server);
457 g_free (server2_uri);
458 soup_test_server_quit_unref (server2);
460 soup_test_session_abort_unref (async_session);
461 soup_test_session_abort_unref (sync_session);