1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 #include "test-utils.h"
5 SoupBuffer *full_response;
10 get_full_response (void)
16 if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
17 g_printerr ("Could not read index.txt: %s\n",
22 full_response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
23 debug_printf (1, "Total response length is %d\n\n", (int)length);
27 check_part (SoupMessageHeaders *headers, const char *body, gsize body_len,
28 gboolean check_start_end, int expected_start, int expected_end)
30 goffset start, end, total_length;
32 debug_printf (1, " Content-Range: %s\n",
33 soup_message_headers_get_one (headers, "Content-Range"));
35 if (!soup_message_headers_get_content_range (headers, &start, &end, &total_length)) {
36 debug_printf (1, " Could not find/parse Content-Range\n");
41 if (total_length != full_response->length && total_length != -1) {
42 debug_printf (1, " Unexpected total length %" G_GINT64_FORMAT " in response\n",
48 if (check_start_end) {
49 if ((expected_start >= 0 && start != expected_start) ||
50 (expected_start < 0 && start != full_response->length + expected_start)) {
51 debug_printf (1, " Unexpected range start %" G_GINT64_FORMAT " in response\n",
57 if ((expected_end >= 0 && end != expected_end) ||
58 (expected_end < 0 && end != full_response->length - 1)) {
59 debug_printf (1, " Unexpected range end %" G_GINT64_FORMAT " in response\n",
66 if (end - start + 1 != body_len) {
67 debug_printf (1, " Range length (%d) does not match body length (%d)\n",
68 (int)(end - start) + 1,
74 memcpy (test_response + start, body, body_len);
78 do_single_range (SoupSession *session, SoupMessage *msg,
81 const char *content_type;
83 debug_printf (1, " Range: %s\n",
84 soup_message_headers_get_one (msg->request_headers, "Range"));
86 soup_session_send_message (session, msg);
88 if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
89 debug_printf (1, " Unexpected status %d %s\n",
90 msg->status_code, msg->reason_phrase);
96 content_type = soup_message_headers_get_content_type (
97 msg->response_headers, NULL);
98 if (content_type && !strcmp (content_type, "multipart/byteranges")) {
99 debug_printf (1, " Response body should not have been multipart/byteranges\n");
100 g_object_unref (msg);
105 check_part (msg->response_headers, msg->response_body->data,
106 msg->response_body->length, TRUE, start, end);
107 g_object_unref (msg);
111 request_single_range (SoupSession *session, const char *uri,
116 msg = soup_message_new ("GET", uri);
117 soup_message_headers_set_range (msg->request_headers, start, end);
118 do_single_range (session, msg, start, end);
122 do_multi_range (SoupSession *session, SoupMessage *msg,
123 int expected_return_ranges)
125 SoupMultipart *multipart;
126 const char *content_type;
129 debug_printf (1, " Range: %s\n",
130 soup_message_headers_get_one (msg->request_headers, "Range"));
132 soup_session_send_message (session, msg);
134 if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
135 debug_printf (1, " Unexpected status %d %s\n",
136 msg->status_code, msg->reason_phrase);
137 g_object_unref (msg);
142 content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
143 if (!content_type || strcmp (content_type, "multipart/byteranges") != 0) {
144 debug_printf (1, " Response Content-Type (%s) was not multipart/byteranges\n",
146 g_object_unref (msg);
151 multipart = soup_multipart_new_from_message (msg->response_headers,
154 debug_printf (1, " Could not parse multipart\n");
155 g_object_unref (msg);
160 length = soup_multipart_get_length (multipart);
161 if (length != expected_return_ranges) {
162 debug_printf (1, " Expected %d ranges, got %d\n",
163 expected_return_ranges, length);
167 for (i = 0; i < length; i++) {
168 SoupMessageHeaders *headers;
171 debug_printf (1, " Part %d\n", i + 1);
172 soup_multipart_get_part (multipart, i, &headers, &body);
173 check_part (headers, body->data, body->length, FALSE, 0, 0);
176 soup_multipart_free (multipart);
177 g_object_unref (msg);
181 request_double_range (SoupSession *session, const char *uri,
182 int first_start, int first_end,
183 int second_start, int second_end,
184 int expected_return_ranges)
189 msg = soup_message_new ("GET", uri);
190 ranges[0].start = first_start;
191 ranges[0].end = first_end;
192 ranges[1].start = second_start;
193 ranges[1].end = second_end;
194 soup_message_headers_set_ranges (msg->request_headers, ranges, 2);
196 if (expected_return_ranges == 1) {
197 do_single_range (session, msg,
198 MIN (first_start, second_start),
199 MAX (first_end, second_end));
201 do_multi_range (session, msg, expected_return_ranges);
205 request_triple_range (SoupSession *session, const char *uri,
206 int first_start, int first_end,
207 int second_start, int second_end,
208 int third_start, int third_end,
209 int expected_return_ranges)
214 msg = soup_message_new ("GET", uri);
215 ranges[0].start = first_start;
216 ranges[0].end = first_end;
217 ranges[1].start = second_start;
218 ranges[1].end = second_end;
219 ranges[2].start = third_start;
220 ranges[2].end = third_end;
221 soup_message_headers_set_ranges (msg->request_headers, ranges, 3);
223 if (expected_return_ranges == 1) {
224 do_single_range (session, msg,
225 MIN (first_start, MIN (second_start, third_start)),
226 MAX (first_end, MAX (second_end, third_end)));
228 do_multi_range (session, msg, expected_return_ranges);
232 do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce)
234 int twelfths = full_response->length / 12;
236 memset (test_response, 0, full_response->length);
238 /* We divide the response into 12 ranges and request them
241 * 0: A (first single request)
242 * 1: D (2nd part of triple request)
243 * 2: C (1st part of double request)
244 * 3: D (1st part of triple request)
245 * 4: F (trickier overlapping request)
246 * 5: C (2nd part of double request)
247 * 6: D (3rd part of triple request)
248 * 7: E (overlapping request)
249 * 8: E (overlapping request)
250 * 9: F (trickier overlapping request)
251 * 10: F (trickier overlapping request)
252 * 11: B (second and third single requests)
255 /* A: 0, simple request */
256 debug_printf (1, "Requesting %d-%d\n", 0 * twelfths, 1 * twelfths);
257 request_single_range (session, uri,
258 0 * twelfths, 1 * twelfths);
260 /* B: 11, end-relative request. These two are mostly redundant
261 * in terms of data coverage, but they may still catch
262 * Range-header-generating bugs.
264 debug_printf (1, "Requesting %d-\n", 11 * twelfths);
265 request_single_range (session, uri,
267 debug_printf (1, "Requesting -%d\n", 1 * twelfths);
268 request_single_range (session, uri,
272 debug_printf (1, "Requesting %d-%d,%d-%d\n",
273 2 * twelfths, 3 * twelfths,
274 5 * twelfths, 6 * twelfths);
275 request_double_range (session, uri,
276 2 * twelfths, 3 * twelfths,
277 5 * twelfths, 6 * twelfths,
281 debug_printf (1, "Requesting %d-%d,%d-%d,%d-%d\n",
282 3 * twelfths, 4 * twelfths,
283 1 * twelfths, 2 * twelfths,
284 6 * twelfths, 7 * twelfths);
285 request_triple_range (session, uri,
286 3 * twelfths, 4 * twelfths,
287 1 * twelfths, 2 * twelfths,
288 6 * twelfths, 7 * twelfths,
291 /* E: 7 and 8: should coalesce into a single response */
292 debug_printf (1, "Requesting %d-%d,%d-%d (can coalesce)\n",
293 7 * twelfths, 8 * twelfths,
294 8 * twelfths, 9 * twelfths);
295 request_double_range (session, uri,
296 7 * twelfths, 8 * twelfths,
297 8 * twelfths, 9 * twelfths,
298 expect_coalesce ? 1 : 2);
300 /* F: 4, 9, 10: 9 and 10 should coalesce even though 4 was
301 * requested between them. (Also, they actually overlap in
302 * this case, as opposed to just touching.)
304 debug_printf (1, "Requesting %d-%d,%d-%d,%d-%d (can partially coalesce)\n",
305 9 * twelfths, 10 * twelfths + 5,
306 4 * twelfths, 5 * twelfths,
307 10 * twelfths - 5, 11 * twelfths);
308 request_triple_range (session, uri,
309 9 * twelfths, 10 * twelfths + 5,
310 4 * twelfths, 5 * twelfths,
311 10 * twelfths - 5, 11 * twelfths,
312 expect_coalesce ? 2 : 3);
314 if (memcmp (full_response->data, test_response, full_response->length) != 0) {
315 debug_printf (1, "\nfull_response and test_response don't match\n");
321 server_handler (SoupServer *server,
325 SoupClientContext *client,
328 soup_message_set_status (msg, SOUP_STATUS_OK);
329 soup_message_body_append_buffer (msg->response_body,
334 main (int argc, char **argv)
336 SoupSession *session;
340 test_init (argc, argv, NULL);
343 get_full_response ();
344 test_response = g_malloc0 (full_response->length);
346 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
348 debug_printf (1, "1. Testing against apache\n");
349 do_range_test (session, "http://127.0.0.1:47524/", FALSE);
351 debug_printf (1, "\n2. Testing against SoupServer\n");
352 server = soup_test_server_new (FALSE);
353 soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
354 base_uri = g_strdup_printf ("http://127.0.0.1:%u/",
355 soup_server_get_port (server));
356 do_range_test (session, base_uri, TRUE);
358 soup_test_server_quit_unref (server);
360 soup_test_session_abort_unref (session);
362 soup_buffer_free (full_response);
363 g_free (test_response);