11 #include "libsoup/soup.h"
13 #include "test-utils.h"
15 SoupBuffer *full_response;
20 get_full_response (void)
26 if (!g_file_get_contents (SRCDIR "/index.txt", &contents, &length, &error)) {
27 fprintf (stderr, "Could not read index.txt: %s\n",
32 full_response = soup_buffer_new (SOUP_MEMORY_TAKE, contents, length);
33 debug_printf (1, "Total response length is %d\n\n", (int)length);
37 check_part (SoupMessageHeaders *headers, const char *body, gsize body_len,
38 gboolean check_start_end, int expected_start, int expected_end)
40 goffset start, end, total_length;
42 debug_printf (1, " Content-Range: %s\n",
43 soup_message_headers_get_one (headers, "Content-Range"));
45 if (!soup_message_headers_get_content_range (headers, &start, &end, &total_length)) {
46 debug_printf (1, " Could not find/parse Content-Range\n");
51 if (total_length != full_response->length && total_length != -1) {
52 debug_printf (1, " Unexpected total length %" G_GINT64_FORMAT " in response\n",
58 if (check_start_end) {
59 if ((expected_start >= 0 && start != expected_start) ||
60 (expected_start < 0 && start != full_response->length + expected_start)) {
61 debug_printf (1, " Unexpected range start %" G_GINT64_FORMAT " in response\n",
67 if ((expected_end >= 0 && end != expected_end) ||
68 (expected_end < 0 && end != full_response->length - 1)) {
69 debug_printf (1, " Unexpected range end %" G_GINT64_FORMAT " in response\n",
76 if (end - start + 1 != body_len) {
77 debug_printf (1, " Range length (%d) does not match body length (%d)\n",
78 (int)(end - start) + 1,
84 memcpy (test_response + start, body, body_len);
88 do_single_range (SoupSession *session, SoupMessage *msg,
91 const char *content_type;
93 debug_printf (1, " Range: %s\n",
94 soup_message_headers_get_one (msg->request_headers, "Range"));
96 soup_session_send_message (session, msg);
98 if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
99 debug_printf (1, " Unexpected status %d %s\n",
100 msg->status_code, msg->reason_phrase);
101 g_object_unref (msg);
106 content_type = soup_message_headers_get_content_type (
107 msg->response_headers, NULL);
108 if (content_type && !strcmp (content_type, "multipart/byteranges")) {
109 debug_printf (1, " Response body should not have been multipart/byteranges\n");
110 g_object_unref (msg);
115 check_part (msg->response_headers, msg->response_body->data,
116 msg->response_body->length, TRUE, start, end);
117 g_object_unref (msg);
121 request_single_range (SoupSession *session, const char *uri,
126 msg = soup_message_new ("GET", uri);
127 soup_message_headers_set_range (msg->request_headers, start, end);
128 do_single_range (session, msg, start, end);
132 do_multi_range (SoupSession *session, SoupMessage *msg,
133 int expected_return_ranges)
135 SoupMultipart *multipart;
136 const char *content_type;
139 debug_printf (1, " Range: %s\n",
140 soup_message_headers_get_one (msg->request_headers, "Range"));
142 soup_session_send_message (session, msg);
144 if (msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
145 debug_printf (1, " Unexpected status %d %s\n",
146 msg->status_code, msg->reason_phrase);
147 g_object_unref (msg);
152 content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
153 if (!content_type || strcmp (content_type, "multipart/byteranges") != 0) {
154 debug_printf (1, " Response Content-Type (%s) was not multipart/byteranges\n",
156 g_object_unref (msg);
161 multipart = soup_multipart_new_from_message (msg->response_headers,
164 debug_printf (1, " Could not parse multipart\n");
165 g_object_unref (msg);
170 length = soup_multipart_get_length (multipart);
171 if (length != expected_return_ranges) {
172 debug_printf (1, " Expected %d ranges, got %d\n",
173 expected_return_ranges, length);
177 for (i = 0; i < length; i++) {
178 SoupMessageHeaders *headers;
181 debug_printf (1, " Part %d\n", i + 1);
182 soup_multipart_get_part (multipart, i, &headers, &body);
183 check_part (headers, body->data, body->length, FALSE, 0, 0);
186 soup_multipart_free (multipart);
187 g_object_unref (msg);
191 request_double_range (SoupSession *session, const char *uri,
192 int first_start, int first_end,
193 int second_start, int second_end,
194 int expected_return_ranges)
199 msg = soup_message_new ("GET", uri);
200 ranges[0].start = first_start;
201 ranges[0].end = first_end;
202 ranges[1].start = second_start;
203 ranges[1].end = second_end;
204 soup_message_headers_set_ranges (msg->request_headers, ranges, 2);
206 if (expected_return_ranges == 1) {
207 do_single_range (session, msg,
208 MIN (first_start, second_start),
209 MAX (first_end, second_end));
211 do_multi_range (session, msg, expected_return_ranges);
215 request_triple_range (SoupSession *session, const char *uri,
216 int first_start, int first_end,
217 int second_start, int second_end,
218 int third_start, int third_end,
219 int expected_return_ranges)
224 msg = soup_message_new ("GET", uri);
225 ranges[0].start = first_start;
226 ranges[0].end = first_end;
227 ranges[1].start = second_start;
228 ranges[1].end = second_end;
229 ranges[2].start = third_start;
230 ranges[2].end = third_end;
231 soup_message_headers_set_ranges (msg->request_headers, ranges, 3);
233 if (expected_return_ranges == 1) {
234 do_single_range (session, msg,
235 MIN (first_start, MIN (second_start, third_start)),
236 MAX (first_end, MAX (second_end, third_end)));
238 do_multi_range (session, msg, expected_return_ranges);
242 do_range_test (SoupSession *session, const char *uri, gboolean expect_coalesce)
244 int twelfths = full_response->length / 12;
246 memset (test_response, 0, full_response->length);
248 /* We divide the response into 12 ranges and request them
251 * 0: A (first single request)
252 * 1: D (2nd part of triple request)
253 * 2: C (1st part of double request)
254 * 3: D (1st part of triple request)
255 * 4: F (trickier overlapping request)
256 * 5: C (2nd part of double request)
257 * 6: D (3rd part of triple request)
258 * 7: E (overlapping request)
259 * 8: E (overlapping request)
260 * 9: F (trickier overlapping request)
261 * 10: F (trickier overlapping request)
262 * 11: B (second and third single requests)
265 /* A: 0, simple request */
266 debug_printf (1, "Requesting %d-%d\n", 0 * twelfths, 1 * twelfths);
267 request_single_range (session, uri,
268 0 * twelfths, 1 * twelfths);
270 /* B: 11, end-relative request. These two are mostly redundant
271 * in terms of data coverage, but they may still catch
272 * Range-header-generating bugs.
274 debug_printf (1, "Requesting %d-\n", 11 * twelfths);
275 request_single_range (session, uri,
277 debug_printf (1, "Requesting -%d\n", 1 * twelfths);
278 request_single_range (session, uri,
282 debug_printf (1, "Requesting %d-%d,%d-%d\n",
283 2 * twelfths, 3 * twelfths,
284 5 * twelfths, 6 * twelfths);
285 request_double_range (session, uri,
286 2 * twelfths, 3 * twelfths,
287 5 * twelfths, 6 * twelfths,
291 debug_printf (1, "Requesting %d-%d,%d-%d,%d-%d\n",
292 3 * twelfths, 4 * twelfths,
293 1 * twelfths, 2 * twelfths,
294 6 * twelfths, 7 * twelfths);
295 request_triple_range (session, uri,
296 3 * twelfths, 4 * twelfths,
297 1 * twelfths, 2 * twelfths,
298 6 * twelfths, 7 * twelfths,
301 /* E: 7 and 8: should coalesce into a single response */
302 debug_printf (1, "Requesting %d-%d,%d-%d (can coalesce)\n",
303 7 * twelfths, 8 * twelfths,
304 8 * twelfths, 9 * twelfths);
305 request_double_range (session, uri,
306 7 * twelfths, 8 * twelfths,
307 8 * twelfths, 9 * twelfths,
308 expect_coalesce ? 1 : 2);
310 /* F: 4, 9, 10: 9 and 10 should coalesce even though 4 was
311 * requested between them. (Also, they actually overlap in
312 * this case, as opposed to just touching.)
314 debug_printf (1, "Requesting %d-%d,%d-%d,%d-%d (can partially coalesce)\n",
315 9 * twelfths, 10 * twelfths + 5,
316 4 * twelfths, 5 * twelfths,
317 10 * twelfths - 5, 11 * twelfths);
318 request_triple_range (session, uri,
319 9 * twelfths, 10 * twelfths + 5,
320 4 * twelfths, 5 * twelfths,
321 10 * twelfths - 5, 11 * twelfths,
322 expect_coalesce ? 2 : 3);
324 if (memcmp (full_response->data, test_response, full_response->length) != 0) {
325 debug_printf (1, "\nfull_response and test_response don't match\n");
331 server_handler (SoupServer *server,
335 SoupClientContext *client,
338 soup_message_set_status (msg, SOUP_STATUS_OK);
339 soup_message_body_append_buffer (msg->response_body,
344 main (int argc, char **argv)
346 SoupSession *session;
350 test_init (argc, argv, NULL);
353 get_full_response ();
354 test_response = g_malloc0 (full_response->length);
356 session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
358 debug_printf (1, "1. Testing against apache\n");
359 do_range_test (session, "http://127.0.0.1:47524/", FALSE);
361 debug_printf (1, "\n2. Testing against SoupServer\n");
362 server = soup_test_server_new (FALSE);
363 soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
364 base_uri = g_strdup_printf ("http://127.0.0.1:%u/",
365 soup_server_get_port (server));
366 do_range_test (session, base_uri, TRUE);
368 soup_test_server_quit_unref (server);
370 soup_test_session_abort_unref (session);
372 soup_buffer_free (full_response);
373 g_free (test_response);