1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ |
---|
2 | /* |
---|
3 | * soup-message.c: HTTP request/response |
---|
4 | * |
---|
5 | * Copyright (C) 2000-2003, Ximian, Inc. |
---|
6 | */ |
---|
7 | |
---|
8 | #include <string.h> |
---|
9 | |
---|
10 | #include "soup-auth.h" |
---|
11 | #include "soup-marshal.h" |
---|
12 | #include "soup-message.h" |
---|
13 | #include "soup-message-private.h" |
---|
14 | #include "soup-misc.h" |
---|
15 | #include "soup-uri.h" |
---|
16 | |
---|
17 | #define PARENT_TYPE G_TYPE_OBJECT |
---|
18 | static GObjectClass *parent_class; |
---|
19 | |
---|
20 | enum { |
---|
21 | WROTE_INFORMATIONAL, |
---|
22 | WROTE_HEADERS, |
---|
23 | WROTE_CHUNK, |
---|
24 | WROTE_BODY, |
---|
25 | |
---|
26 | GOT_INFORMATIONAL, |
---|
27 | GOT_HEADERS, |
---|
28 | GOT_CHUNK, |
---|
29 | GOT_BODY, |
---|
30 | |
---|
31 | RESTARTED, |
---|
32 | FINISHED, |
---|
33 | |
---|
34 | LAST_SIGNAL |
---|
35 | }; |
---|
36 | |
---|
37 | static guint signals[LAST_SIGNAL] = { 0 }; |
---|
38 | |
---|
39 | static void wrote_body (SoupMessage *req); |
---|
40 | static void got_headers (SoupMessage *req); |
---|
41 | static void got_chunk (SoupMessage *req); |
---|
42 | static void got_body (SoupMessage *req); |
---|
43 | static void restarted (SoupMessage *req); |
---|
44 | static void finished (SoupMessage *req); |
---|
45 | static void free_chunks (SoupMessage *msg); |
---|
46 | |
---|
47 | static void |
---|
48 | init (GObject *object) |
---|
49 | { |
---|
50 | SoupMessage *msg = SOUP_MESSAGE (object); |
---|
51 | |
---|
52 | msg->priv = g_new0 (SoupMessagePrivate, 1); |
---|
53 | |
---|
54 | msg->status = SOUP_MESSAGE_STATUS_IDLE; |
---|
55 | |
---|
56 | msg->request_headers = g_hash_table_new (soup_str_case_hash, |
---|
57 | soup_str_case_equal); |
---|
58 | |
---|
59 | msg->response_headers = g_hash_table_new (soup_str_case_hash, |
---|
60 | soup_str_case_equal); |
---|
61 | |
---|
62 | msg->priv->http_version = SOUP_HTTP_1_1; |
---|
63 | } |
---|
64 | |
---|
65 | static void |
---|
66 | finalize (GObject *object) |
---|
67 | { |
---|
68 | SoupMessage *msg = SOUP_MESSAGE (object); |
---|
69 | |
---|
70 | soup_message_io_stop (msg); |
---|
71 | |
---|
72 | if (msg->priv->uri) |
---|
73 | soup_uri_free (msg->priv->uri); |
---|
74 | |
---|
75 | if (msg->request.owner == SOUP_BUFFER_SYSTEM_OWNED) |
---|
76 | g_free (msg->request.body); |
---|
77 | if (msg->response.owner == SOUP_BUFFER_SYSTEM_OWNED) |
---|
78 | g_free (msg->response.body); |
---|
79 | free_chunks (msg); |
---|
80 | |
---|
81 | soup_message_clear_headers (msg->request_headers); |
---|
82 | g_hash_table_destroy (msg->request_headers); |
---|
83 | |
---|
84 | soup_message_clear_headers (msg->response_headers); |
---|
85 | g_hash_table_destroy (msg->response_headers); |
---|
86 | |
---|
87 | g_slist_foreach (msg->priv->content_handlers, (GFunc) g_free, NULL); |
---|
88 | g_slist_free (msg->priv->content_handlers); |
---|
89 | |
---|
90 | g_free ((char *) msg->reason_phrase); |
---|
91 | |
---|
92 | g_free (msg->priv); |
---|
93 | |
---|
94 | G_OBJECT_CLASS (parent_class)->finalize (object); |
---|
95 | } |
---|
96 | |
---|
97 | static void |
---|
98 | class_init (GObjectClass *object_class) |
---|
99 | { |
---|
100 | SoupMessageClass *message_class = SOUP_MESSAGE_CLASS (object_class); |
---|
101 | |
---|
102 | parent_class = g_type_class_ref (PARENT_TYPE); |
---|
103 | |
---|
104 | /* virtual method definition */ |
---|
105 | message_class->wrote_body = wrote_body; |
---|
106 | message_class->got_headers = got_headers; |
---|
107 | message_class->got_chunk = got_chunk; |
---|
108 | message_class->got_body = got_body; |
---|
109 | message_class->restarted = restarted; |
---|
110 | message_class->finished = finished; |
---|
111 | |
---|
112 | /* virtual method override */ |
---|
113 | object_class->finalize = finalize; |
---|
114 | |
---|
115 | /* signals */ |
---|
116 | signals[WROTE_INFORMATIONAL] = |
---|
117 | g_signal_new ("wrote_informational", |
---|
118 | G_OBJECT_CLASS_TYPE (object_class), |
---|
119 | G_SIGNAL_RUN_FIRST, |
---|
120 | G_STRUCT_OFFSET (SoupMessageClass, wrote_informational), |
---|
121 | NULL, NULL, |
---|
122 | soup_marshal_NONE__NONE, |
---|
123 | G_TYPE_NONE, 0); |
---|
124 | signals[WROTE_HEADERS] = |
---|
125 | g_signal_new ("wrote_headers", |
---|
126 | G_OBJECT_CLASS_TYPE (object_class), |
---|
127 | G_SIGNAL_RUN_FIRST, |
---|
128 | G_STRUCT_OFFSET (SoupMessageClass, wrote_headers), |
---|
129 | NULL, NULL, |
---|
130 | soup_marshal_NONE__NONE, |
---|
131 | G_TYPE_NONE, 0); |
---|
132 | signals[WROTE_CHUNK] = |
---|
133 | g_signal_new ("wrote_chunk", |
---|
134 | G_OBJECT_CLASS_TYPE (object_class), |
---|
135 | G_SIGNAL_RUN_FIRST, |
---|
136 | G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk), |
---|
137 | NULL, NULL, |
---|
138 | soup_marshal_NONE__NONE, |
---|
139 | G_TYPE_NONE, 0); |
---|
140 | signals[WROTE_BODY] = |
---|
141 | g_signal_new ("wrote_body", |
---|
142 | G_OBJECT_CLASS_TYPE (object_class), |
---|
143 | G_SIGNAL_RUN_FIRST, |
---|
144 | G_STRUCT_OFFSET (SoupMessageClass, wrote_body), |
---|
145 | NULL, NULL, |
---|
146 | soup_marshal_NONE__NONE, |
---|
147 | G_TYPE_NONE, 0); |
---|
148 | |
---|
149 | signals[GOT_INFORMATIONAL] = |
---|
150 | g_signal_new ("got_informational", |
---|
151 | G_OBJECT_CLASS_TYPE (object_class), |
---|
152 | G_SIGNAL_RUN_FIRST, |
---|
153 | G_STRUCT_OFFSET (SoupMessageClass, got_informational), |
---|
154 | NULL, NULL, |
---|
155 | soup_marshal_NONE__NONE, |
---|
156 | G_TYPE_NONE, 0); |
---|
157 | signals[GOT_HEADERS] = |
---|
158 | g_signal_new ("got_headers", |
---|
159 | G_OBJECT_CLASS_TYPE (object_class), |
---|
160 | G_SIGNAL_RUN_FIRST, |
---|
161 | G_STRUCT_OFFSET (SoupMessageClass, got_headers), |
---|
162 | NULL, NULL, |
---|
163 | soup_marshal_NONE__NONE, |
---|
164 | G_TYPE_NONE, 0); |
---|
165 | signals[GOT_CHUNK] = |
---|
166 | g_signal_new ("got_chunk", |
---|
167 | G_OBJECT_CLASS_TYPE (object_class), |
---|
168 | G_SIGNAL_RUN_FIRST, |
---|
169 | G_STRUCT_OFFSET (SoupMessageClass, got_chunk), |
---|
170 | NULL, NULL, |
---|
171 | soup_marshal_NONE__NONE, |
---|
172 | G_TYPE_NONE, 0); |
---|
173 | signals[GOT_BODY] = |
---|
174 | g_signal_new ("got_body", |
---|
175 | G_OBJECT_CLASS_TYPE (object_class), |
---|
176 | G_SIGNAL_RUN_FIRST, |
---|
177 | G_STRUCT_OFFSET (SoupMessageClass, got_body), |
---|
178 | NULL, NULL, |
---|
179 | soup_marshal_NONE__NONE, |
---|
180 | G_TYPE_NONE, 0); |
---|
181 | |
---|
182 | signals[RESTARTED] = |
---|
183 | g_signal_new ("restarted", |
---|
184 | G_OBJECT_CLASS_TYPE (object_class), |
---|
185 | G_SIGNAL_RUN_FIRST, |
---|
186 | G_STRUCT_OFFSET (SoupMessageClass, restarted), |
---|
187 | NULL, NULL, |
---|
188 | soup_marshal_NONE__NONE, |
---|
189 | G_TYPE_NONE, 0); |
---|
190 | signals[FINISHED] = |
---|
191 | g_signal_new ("finished", |
---|
192 | G_OBJECT_CLASS_TYPE (object_class), |
---|
193 | G_SIGNAL_RUN_FIRST, |
---|
194 | G_STRUCT_OFFSET (SoupMessageClass, finished), |
---|
195 | NULL, NULL, |
---|
196 | soup_marshal_NONE__NONE, |
---|
197 | G_TYPE_NONE, 0); |
---|
198 | } |
---|
199 | |
---|
200 | SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE) |
---|
201 | |
---|
202 | |
---|
203 | /** |
---|
204 | * soup_message_new: |
---|
205 | * @method: the HTTP method for the created request |
---|
206 | * @uri_string: the destination endpoint (as a string) |
---|
207 | * |
---|
208 | * Creates a new empty #SoupMessage, which will connect to @uri |
---|
209 | * |
---|
210 | * Return value: the new #SoupMessage (or %NULL if @uri could not |
---|
211 | * be parsed). |
---|
212 | */ |
---|
213 | SoupMessage * |
---|
214 | soup_message_new (const char *method, const char *uri_string) |
---|
215 | { |
---|
216 | SoupMessage *msg; |
---|
217 | SoupUri *uri; |
---|
218 | |
---|
219 | uri = soup_uri_new (uri_string); |
---|
220 | if (!uri) |
---|
221 | return NULL; |
---|
222 | |
---|
223 | if (!uri->host) { |
---|
224 | soup_uri_free (uri); |
---|
225 | return NULL; |
---|
226 | } |
---|
227 | |
---|
228 | msg = g_object_new (SOUP_TYPE_MESSAGE, NULL); |
---|
229 | msg->method = method ? method : SOUP_METHOD_GET; |
---|
230 | msg->priv->uri = uri; |
---|
231 | |
---|
232 | return msg; |
---|
233 | } |
---|
234 | |
---|
235 | /** |
---|
236 | * soup_message_new_from_uri: |
---|
237 | * @method: the HTTP method for the created request |
---|
238 | * @uri: the destination endpoint (as a #SoupUri) |
---|
239 | * |
---|
240 | * Creates a new empty #SoupMessage, which will connect to @uri |
---|
241 | * |
---|
242 | * Return value: the new #SoupMessage |
---|
243 | */ |
---|
244 | SoupMessage * |
---|
245 | soup_message_new_from_uri (const char *method, const SoupUri *uri) |
---|
246 | { |
---|
247 | SoupMessage *msg; |
---|
248 | |
---|
249 | msg = g_object_new (SOUP_TYPE_MESSAGE, NULL); |
---|
250 | msg->method = method ? method : SOUP_METHOD_GET; |
---|
251 | msg->priv->uri = soup_uri_copy (uri); |
---|
252 | |
---|
253 | return msg; |
---|
254 | } |
---|
255 | |
---|
256 | /** |
---|
257 | * soup_message_set_request: |
---|
258 | * @msg: the message |
---|
259 | * @content_type: MIME Content-Type of the body |
---|
260 | * @req_owner: the #SoupOwnership of the passed data buffer. |
---|
261 | * @req_body: a data buffer containing the body of the message request. |
---|
262 | * @req_length: the byte length of @req_body. |
---|
263 | * |
---|
264 | * Convenience function to set the request body of a #SoupMessage |
---|
265 | */ |
---|
266 | void |
---|
267 | soup_message_set_request (SoupMessage *msg, |
---|
268 | const char *content_type, |
---|
269 | SoupOwnership req_owner, |
---|
270 | char *req_body, |
---|
271 | gulong req_length) |
---|
272 | { |
---|
273 | g_return_if_fail (SOUP_IS_MESSAGE (msg)); |
---|
274 | g_return_if_fail (content_type != NULL); |
---|
275 | g_return_if_fail (req_body != NULL || req_length == 0); |
---|
276 | |
---|
277 | soup_message_add_header (msg->request_headers, |
---|
278 | "Content-Type", content_type); |
---|
279 | msg->request.owner = req_owner; |
---|
280 | msg->request.body = req_body; |
---|
281 | msg->request.length = req_length; |
---|
282 | } |
---|
283 | |
---|
284 | /** |
---|
285 | * soup_message_set_response: |
---|
286 | * @msg: the message |
---|
287 | * @content_type: MIME Content-Type of the body |
---|
288 | * @resp_owner: the #SoupOwnership of the passed data buffer. |
---|
289 | * @resp_body: a data buffer containing the body of the message response. |
---|
290 | * @resp_length: the byte length of @resp_body. |
---|
291 | * |
---|
292 | * Convenience function to set the response body of a #SoupMessage |
---|
293 | */ |
---|
294 | void |
---|
295 | soup_message_set_response (SoupMessage *msg, |
---|
296 | const char *content_type, |
---|
297 | SoupOwnership resp_owner, |
---|
298 | char *resp_body, |
---|
299 | gulong resp_length) |
---|
300 | { |
---|
301 | g_return_if_fail (SOUP_IS_MESSAGE (msg)); |
---|
302 | g_return_if_fail (content_type != NULL); |
---|
303 | g_return_if_fail (resp_body != NULL || resp_length == 0); |
---|
304 | |
---|
305 | soup_message_add_header (msg->response_headers, |
---|
306 | "Content-Type", content_type); |
---|
307 | msg->response.owner = resp_owner; |
---|
308 | msg->response.body = resp_body; |
---|
309 | msg->response.length = resp_length; |
---|
310 | } |
---|
311 | |
---|
312 | /** |
---|
313 | * soup_message_wrote_informational: |
---|
314 | * @msg: a #SoupMessage |
---|
315 | * |
---|
316 | * Emits the %wrote_informational signal, indicating that the IO layer |
---|
317 | * finished writing an informational (1xx) response for @msg. |
---|
318 | **/ |
---|
319 | void |
---|
320 | soup_message_wrote_informational (SoupMessage *msg) |
---|
321 | { |
---|
322 | g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0); |
---|
323 | } |
---|
324 | |
---|
325 | /** |
---|
326 | * soup_message_wrote_headers: |
---|
327 | * @msg: a #SoupMessage |
---|
328 | * |
---|
329 | * Emits the %wrote_headers signal, indicating that the IO layer |
---|
330 | * finished writing the (non-informational) headers for @msg. |
---|
331 | **/ |
---|
332 | void |
---|
333 | soup_message_wrote_headers (SoupMessage *msg) |
---|
334 | { |
---|
335 | g_signal_emit (msg, signals[WROTE_HEADERS], 0); |
---|
336 | } |
---|
337 | |
---|
338 | /** |
---|
339 | * soup_message_wrote_chunk: |
---|
340 | * @msg: a #SoupMessage |
---|
341 | * |
---|
342 | * Emits the %wrote_chunk signal, indicating that the IO layer |
---|
343 | * finished writing a chunk of @msg's body. |
---|
344 | **/ |
---|
345 | void |
---|
346 | soup_message_wrote_chunk (SoupMessage *msg) |
---|
347 | { |
---|
348 | g_signal_emit (msg, signals[WROTE_CHUNK], 0); |
---|
349 | } |
---|
350 | |
---|
351 | static void |
---|
352 | wrote_body (SoupMessage *req) |
---|
353 | { |
---|
354 | g_object_ref (req); |
---|
355 | soup_message_run_handlers (req, SOUP_HANDLER_POST_REQUEST); |
---|
356 | g_object_unref (req); |
---|
357 | } |
---|
358 | |
---|
359 | /** |
---|
360 | * soup_message_wrote_body: |
---|
361 | * @msg: a #SoupMessage |
---|
362 | * |
---|
363 | * Emits the %wrote_body signal, indicating that the IO layer finished |
---|
364 | * writing the body for @msg. |
---|
365 | **/ |
---|
366 | void |
---|
367 | soup_message_wrote_body (SoupMessage *msg) |
---|
368 | { |
---|
369 | g_signal_emit (msg, signals[WROTE_BODY], 0); |
---|
370 | } |
---|
371 | |
---|
372 | /** |
---|
373 | * soup_message_got_informational: |
---|
374 | * @msg: a #SoupMessage |
---|
375 | * |
---|
376 | * Emits the %got_informational signal, indicating that the IO layer |
---|
377 | * read a complete informational (1xx) response for @msg. |
---|
378 | **/ |
---|
379 | void |
---|
380 | soup_message_got_informational (SoupMessage *msg) |
---|
381 | { |
---|
382 | g_signal_emit (msg, signals[GOT_INFORMATIONAL], 0); |
---|
383 | } |
---|
384 | |
---|
385 | static void |
---|
386 | got_headers (SoupMessage *req) |
---|
387 | { |
---|
388 | g_object_ref (req); |
---|
389 | soup_message_run_handlers (req, SOUP_HANDLER_PRE_BODY); |
---|
390 | if (SOUP_MESSAGE_IS_STARTING (req)) |
---|
391 | g_signal_stop_emission (req, signals[GOT_HEADERS], 0); |
---|
392 | g_object_unref (req); |
---|
393 | } |
---|
394 | |
---|
395 | /** |
---|
396 | * soup_message_got_headers: |
---|
397 | * @msg: a #SoupMessage |
---|
398 | * |
---|
399 | * Emits the %got_headers signal, indicating that the IO layer |
---|
400 | * finished reading the (non-informational) headers for @msg. |
---|
401 | **/ |
---|
402 | void |
---|
403 | soup_message_got_headers (SoupMessage *msg) |
---|
404 | { |
---|
405 | g_signal_emit (msg, signals[GOT_HEADERS], 0); |
---|
406 | } |
---|
407 | |
---|
408 | static void |
---|
409 | got_chunk (SoupMessage *req) |
---|
410 | { |
---|
411 | g_object_ref (req); |
---|
412 | soup_message_run_handlers (req, SOUP_HANDLER_BODY_CHUNK); |
---|
413 | if (SOUP_MESSAGE_IS_STARTING (req)) |
---|
414 | g_signal_stop_emission (req, signals[GOT_CHUNK], 0); |
---|
415 | g_object_unref (req); |
---|
416 | } |
---|
417 | |
---|
418 | /** |
---|
419 | * soup_message_got_chunk: |
---|
420 | * @msg: a #SoupMessage |
---|
421 | * |
---|
422 | * Emits the %got_chunk signal, indicating that the IO layer finished |
---|
423 | * reading a chunk of @msg's body. |
---|
424 | **/ |
---|
425 | void |
---|
426 | soup_message_got_chunk (SoupMessage *msg) |
---|
427 | { |
---|
428 | g_signal_emit (msg, signals[GOT_CHUNK], 0); |
---|
429 | } |
---|
430 | |
---|
431 | static void |
---|
432 | got_body (SoupMessage *req) |
---|
433 | { |
---|
434 | g_object_ref (req); |
---|
435 | soup_message_run_handlers (req, SOUP_HANDLER_POST_BODY); |
---|
436 | if (SOUP_MESSAGE_IS_STARTING (req)) |
---|
437 | g_signal_stop_emission (req, signals[GOT_BODY], 0); |
---|
438 | g_object_unref (req); |
---|
439 | } |
---|
440 | |
---|
441 | /** |
---|
442 | * soup_message_got_body: |
---|
443 | * @msg: a #SoupMessage |
---|
444 | * |
---|
445 | * Emits the %got_body signal, indicating that the IO layer finished |
---|
446 | * reading the body for @msg. |
---|
447 | **/ |
---|
448 | void |
---|
449 | soup_message_got_body (SoupMessage *msg) |
---|
450 | { |
---|
451 | g_signal_emit (msg, signals[GOT_BODY], 0); |
---|
452 | } |
---|
453 | |
---|
454 | static void |
---|
455 | restarted (SoupMessage *req) |
---|
456 | { |
---|
457 | soup_message_io_stop (req); |
---|
458 | } |
---|
459 | |
---|
460 | /** |
---|
461 | * soup_message_restarted: |
---|
462 | * @msg: a #SoupMessage |
---|
463 | * |
---|
464 | * Emits the %restarted signal, indicating that @msg should be |
---|
465 | * requeued. |
---|
466 | **/ |
---|
467 | void |
---|
468 | soup_message_restarted (SoupMessage *msg) |
---|
469 | { |
---|
470 | g_signal_emit (msg, signals[RESTARTED], 0); |
---|
471 | } |
---|
472 | |
---|
473 | static void |
---|
474 | finished (SoupMessage *req) |
---|
475 | { |
---|
476 | soup_message_io_stop (req); |
---|
477 | req->status = SOUP_MESSAGE_STATUS_FINISHED; |
---|
478 | } |
---|
479 | |
---|
480 | /** |
---|
481 | * soup_message_finished: |
---|
482 | * @msg: a #SoupMessage |
---|
483 | * |
---|
484 | * Emits the %finished signal, indicating that @msg has been completely |
---|
485 | * processed. |
---|
486 | **/ |
---|
487 | void |
---|
488 | soup_message_finished (SoupMessage *msg) |
---|
489 | { |
---|
490 | g_signal_emit (msg, signals[FINISHED], 0); |
---|
491 | } |
---|
492 | |
---|
493 | static gboolean |
---|
494 | free_header_list (gpointer name, gpointer vals, gpointer user_data) |
---|
495 | { |
---|
496 | g_free (name); |
---|
497 | g_slist_foreach (vals, (GFunc) g_free, NULL); |
---|
498 | g_slist_free (vals); |
---|
499 | |
---|
500 | return TRUE; |
---|
501 | } |
---|
502 | |
---|
503 | /** |
---|
504 | * soup_message_clear_headers: |
---|
505 | * @hash: a header table (the %request_headers or %response_headers |
---|
506 | * field of a #SoupMessage) |
---|
507 | * |
---|
508 | * Clears @hash. |
---|
509 | **/ |
---|
510 | void |
---|
511 | soup_message_clear_headers (GHashTable *hash) |
---|
512 | { |
---|
513 | g_return_if_fail (hash != NULL); |
---|
514 | |
---|
515 | g_hash_table_foreach_remove (hash, free_header_list, NULL); |
---|
516 | } |
---|
517 | |
---|
518 | /** |
---|
519 | * soup_message_remove_header: |
---|
520 | * @hash: a header table (the %request_headers or %response_headers |
---|
521 | * field of a #SoupMessage) |
---|
522 | * @name: the header name to remove |
---|
523 | * |
---|
524 | * Removes @name from @hash. If there are multiple values for @name, |
---|
525 | * they are all removed. |
---|
526 | **/ |
---|
527 | void |
---|
528 | soup_message_remove_header (GHashTable *hash, const char *name) |
---|
529 | { |
---|
530 | gpointer old_key, old_vals; |
---|
531 | |
---|
532 | g_return_if_fail (hash != NULL); |
---|
533 | g_return_if_fail (name != NULL || name[0] != '\0'); |
---|
534 | |
---|
535 | if (g_hash_table_lookup_extended (hash, name, &old_key, &old_vals)) { |
---|
536 | g_hash_table_remove (hash, name); |
---|
537 | free_header_list (old_key, old_vals, NULL); |
---|
538 | } |
---|
539 | } |
---|
540 | |
---|
541 | /** |
---|
542 | * soup_message_add_header: |
---|
543 | * @hash: a header table (the %request_headers or %response_headers |
---|
544 | * field of a #SoupMessage) |
---|
545 | * @name: the header name to add |
---|
546 | * @value: the value of the new header |
---|
547 | * |
---|
548 | * Adds a header with name @name and value @value to @hash. If there |
---|
549 | * was already a header with name @name, this one does not replace it, |
---|
550 | * it is merely added to it. |
---|
551 | **/ |
---|
552 | void |
---|
553 | soup_message_add_header (GHashTable *hash, const char *name, const char *value) |
---|
554 | { |
---|
555 | GSList *old_value; |
---|
556 | |
---|
557 | g_return_if_fail (hash != NULL); |
---|
558 | g_return_if_fail (name != NULL || name [0] != '\0'); |
---|
559 | g_return_if_fail (value != NULL); |
---|
560 | |
---|
561 | old_value = g_hash_table_lookup (hash, name); |
---|
562 | |
---|
563 | if (old_value) |
---|
564 | g_slist_append (old_value, g_strdup (value)); |
---|
565 | else { |
---|
566 | g_hash_table_insert (hash, g_strdup (name), |
---|
567 | g_slist_append (NULL, g_strdup (value))); |
---|
568 | } |
---|
569 | } |
---|
570 | |
---|
571 | /** |
---|
572 | * soup_message_get_header: |
---|
573 | * @hash: a header table (the %request_headers or %response_headers |
---|
574 | * field of a #SoupMessage) |
---|
575 | * @name: header name. |
---|
576 | * |
---|
577 | * Finds the first header in @hash with name @name. |
---|
578 | * |
---|
579 | * Return value: the header's value or %NULL if not found. |
---|
580 | **/ |
---|
581 | const char * |
---|
582 | soup_message_get_header (GHashTable *hash, const char *name) |
---|
583 | { |
---|
584 | GSList *vals; |
---|
585 | |
---|
586 | g_return_val_if_fail (hash != NULL, NULL); |
---|
587 | g_return_val_if_fail (name != NULL || name [0] != '\0', NULL); |
---|
588 | |
---|
589 | vals = g_hash_table_lookup (hash, name); |
---|
590 | if (vals) |
---|
591 | return vals->data; |
---|
592 | |
---|
593 | return NULL; |
---|
594 | } |
---|
595 | |
---|
596 | /** |
---|
597 | * soup_message_get_header_list: |
---|
598 | * @hash: a header table (the %request_headers or %response_headers |
---|
599 | * field of a #SoupMessage) |
---|
600 | * @name: header name. |
---|
601 | * |
---|
602 | * Finds all headers in @hash with name @name. |
---|
603 | * |
---|
604 | * Return value: a (possibly empty) list of values of headers with |
---|
605 | * name @name. The caller should not modify or free this list. |
---|
606 | **/ |
---|
607 | const GSList * |
---|
608 | soup_message_get_header_list (GHashTable *hash, const char *name) |
---|
609 | { |
---|
610 | g_return_val_if_fail (hash != NULL, NULL); |
---|
611 | g_return_val_if_fail (name != NULL || name [0] != '\0', NULL); |
---|
612 | |
---|
613 | return g_hash_table_lookup (hash, name); |
---|
614 | } |
---|
615 | |
---|
616 | typedef struct { |
---|
617 | GHFunc func; |
---|
618 | gpointer user_data; |
---|
619 | } SoupMessageForeachHeaderData; |
---|
620 | |
---|
621 | static void |
---|
622 | foreach_value_in_list (gpointer name, gpointer value, gpointer user_data) |
---|
623 | { |
---|
624 | GSList *vals = value; |
---|
625 | SoupMessageForeachHeaderData *data = user_data; |
---|
626 | |
---|
627 | while (vals) { |
---|
628 | (*data->func) (name, vals->data, data->user_data); |
---|
629 | vals = vals->next; |
---|
630 | } |
---|
631 | } |
---|
632 | |
---|
633 | /** |
---|
634 | * soup_message_foreach_header: |
---|
635 | * @hash: a header table (the %request_headers or %response_headers |
---|
636 | * field of a #SoupMessage) |
---|
637 | * @func: callback function to run for each header |
---|
638 | * @user_data: data to pass to @func |
---|
639 | * |
---|
640 | * Calls @func once for each header value in @hash. (If there are |
---|
641 | * headers will multiple values, @func will be called once on each |
---|
642 | * value.) |
---|
643 | **/ |
---|
644 | void |
---|
645 | soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data) |
---|
646 | { |
---|
647 | SoupMessageForeachHeaderData data; |
---|
648 | |
---|
649 | g_return_if_fail (hash != NULL); |
---|
650 | g_return_if_fail (func != NULL); |
---|
651 | |
---|
652 | data.func = func; |
---|
653 | data.user_data = user_data; |
---|
654 | g_hash_table_foreach (hash, foreach_value_in_list, &data); |
---|
655 | } |
---|
656 | |
---|
657 | /** |
---|
658 | * soup_message_cleanup_response: |
---|
659 | * @req: a #SoupMessage |
---|
660 | * |
---|
661 | * Cleans up all response data on @req, so that the request can be sent |
---|
662 | * again and receive a new response. (Eg, as a result of a redirect or |
---|
663 | * authorization request.) |
---|
664 | **/ |
---|
665 | void |
---|
666 | soup_message_cleanup_response (SoupMessage *req) |
---|
667 | { |
---|
668 | if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED) |
---|
669 | g_free (req->response.body); |
---|
670 | |
---|
671 | req->response.owner = 0; |
---|
672 | req->response.body = NULL; |
---|
673 | req->response.length = 0; |
---|
674 | |
---|
675 | free_chunks (req); |
---|
676 | |
---|
677 | soup_message_clear_headers (req->response_headers); |
---|
678 | |
---|
679 | req->status_code = SOUP_STATUS_NONE; |
---|
680 | if (req->reason_phrase) { |
---|
681 | g_free ((char *) req->reason_phrase); |
---|
682 | req->reason_phrase = NULL; |
---|
683 | } |
---|
684 | } |
---|
685 | |
---|
686 | /** |
---|
687 | * soup_message_set_flags: |
---|
688 | * @msg: a #SoupMessage |
---|
689 | * @flags: a set of #SoupMessageFlags values |
---|
690 | * |
---|
691 | * Sets the specified flags on @msg. |
---|
692 | **/ |
---|
693 | void |
---|
694 | soup_message_set_flags (SoupMessage *msg, guint flags) |
---|
695 | { |
---|
696 | g_return_if_fail (SOUP_IS_MESSAGE (msg)); |
---|
697 | |
---|
698 | msg->priv->msg_flags = flags; |
---|
699 | } |
---|
700 | |
---|
701 | /** |
---|
702 | * soup_message_get_flags: |
---|
703 | * @msg: a #SoupMessage |
---|
704 | * |
---|
705 | * Gets the flags on @msg |
---|
706 | * |
---|
707 | * Return value: the flags |
---|
708 | **/ |
---|
709 | guint |
---|
710 | soup_message_get_flags (SoupMessage *msg) |
---|
711 | { |
---|
712 | g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0); |
---|
713 | |
---|
714 | return msg->priv->msg_flags; |
---|
715 | } |
---|
716 | |
---|
717 | /** |
---|
718 | * soup_message_set_http_version: |
---|
719 | * @msg: a #SoupMessage |
---|
720 | * @version: the HTTP version |
---|
721 | * |
---|
722 | * Sets the HTTP version on @msg. The default version is |
---|
723 | * %SOUP_HTTP_1_1. Setting it to %SOUP_HTTP_1_0 will prevent certain |
---|
724 | * functionality from being used. |
---|
725 | **/ |
---|
726 | void |
---|
727 | soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version) |
---|
728 | { |
---|
729 | g_return_if_fail (SOUP_IS_MESSAGE (msg)); |
---|
730 | |
---|
731 | msg->priv->http_version = version; |
---|
732 | } |
---|
733 | |
---|
734 | /** |
---|
735 | * soup_message_get_http_version: |
---|
736 | * @msg: a #SoupMessage |
---|
737 | * |
---|
738 | * Gets the HTTP version of @msg. This is the minimum of the |
---|
739 | * version from the request and the version from the response. |
---|
740 | * |
---|
741 | * Return value: the HTTP version |
---|
742 | **/ |
---|
743 | SoupHttpVersion |
---|
744 | soup_message_get_http_version (SoupMessage *msg) |
---|
745 | { |
---|
746 | g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0); |
---|
747 | |
---|
748 | return msg->priv->http_version; |
---|
749 | } |
---|
750 | |
---|
751 | /** |
---|
752 | * soup_message_is_keepalive: |
---|
753 | * @msg: a #SoupMessage |
---|
754 | * |
---|
755 | * Determines whether or not @msg's connection can be kept alive for |
---|
756 | * further requests after processing @msg. |
---|
757 | * |
---|
758 | * Return value: %TRUE or %FALSE. |
---|
759 | **/ |
---|
760 | gboolean |
---|
761 | soup_message_is_keepalive (SoupMessage *msg) |
---|
762 | { |
---|
763 | const char *c_conn, *s_conn; |
---|
764 | |
---|
765 | c_conn = soup_message_get_header (msg->request_headers, "Connection"); |
---|
766 | s_conn = soup_message_get_header (msg->response_headers, "Connection"); |
---|
767 | |
---|
768 | if (msg->status_code == SOUP_STATUS_OK && |
---|
769 | soup_method_get_id (msg->method) == SOUP_METHOD_ID_CONNECT) |
---|
770 | return TRUE; |
---|
771 | |
---|
772 | if (msg->priv->http_version == SOUP_HTTP_1_0) { |
---|
773 | /* Only persistent if the client requested keepalive |
---|
774 | * and the server agreed. |
---|
775 | */ |
---|
776 | |
---|
777 | if (!c_conn || !s_conn) |
---|
778 | return FALSE; |
---|
779 | if (g_strcasecmp (c_conn, "Keep-Alive") != 0 || |
---|
780 | g_strcasecmp (s_conn, "Keep-Alive") != 0) |
---|
781 | return FALSE; |
---|
782 | |
---|
783 | return TRUE; |
---|
784 | } else { |
---|
785 | /* Persistent unless either side requested otherwise */ |
---|
786 | |
---|
787 | if (c_conn && g_strcasecmp (c_conn, "close") == 0) |
---|
788 | return FALSE; |
---|
789 | if (s_conn && g_strcasecmp (s_conn, "close") == 0) |
---|
790 | return FALSE; |
---|
791 | |
---|
792 | return TRUE; |
---|
793 | } |
---|
794 | } |
---|
795 | |
---|
796 | /** |
---|
797 | * soup_message_set_uri: |
---|
798 | * @msg: a #SoupMessage |
---|
799 | * @new_uri: the new #SoupUri |
---|
800 | * |
---|
801 | * Changes the URI that @msg is directed to (generally as a result |
---|
802 | * of a redirect). |
---|
803 | **/ |
---|
804 | void |
---|
805 | soup_message_set_uri (SoupMessage *msg, const SoupUri *new_uri) |
---|
806 | { |
---|
807 | g_return_if_fail (SOUP_IS_MESSAGE (msg)); |
---|
808 | |
---|
809 | if (msg->priv->uri && new_uri) { |
---|
810 | if (strcmp (msg->priv->uri->host, new_uri->host) != 0) |
---|
811 | soup_message_io_stop (msg); |
---|
812 | } else if (!new_uri) |
---|
813 | soup_message_io_stop (msg); |
---|
814 | |
---|
815 | if (msg->priv->uri) |
---|
816 | soup_uri_free (msg->priv->uri); |
---|
817 | msg->priv->uri = soup_uri_copy (new_uri); |
---|
818 | } |
---|
819 | |
---|
820 | /** |
---|
821 | * soup_message_get_uri: |
---|
822 | * @msg: a #SoupMessage |
---|
823 | * |
---|
824 | * Gets @msg's URI |
---|
825 | * |
---|
826 | * Return value: the URI @msg is targeted for. |
---|
827 | **/ |
---|
828 | const SoupUri * |
---|
829 | soup_message_get_uri (SoupMessage *msg) |
---|
830 | { |
---|
831 | g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL); |
---|
832 | |
---|
833 | return msg->priv->uri; |
---|
834 | } |
---|
835 | |
---|
836 | /** |
---|
837 | * soup_message_set_status: |
---|
838 | * @msg: a #SoupMessage |
---|
839 | * @status_code: an HTTP status code |
---|
840 | * |
---|
841 | * Sets @msg's status code to @status_code. If @status_code is a |
---|
842 | * known value, it will also set @msg's reason_phrase. |
---|
843 | **/ |
---|
844 | void |
---|
845 | soup_message_set_status (SoupMessage *msg, guint status_code) |
---|
846 | { |
---|
847 | g_return_if_fail (SOUP_IS_MESSAGE (msg)); |
---|
848 | g_return_if_fail (status_code != 0); |
---|
849 | |
---|
850 | g_free ((char *) msg->reason_phrase); |
---|
851 | |
---|
852 | msg->status_code = status_code; |
---|
853 | msg->reason_phrase = g_strdup (soup_status_get_phrase (status_code)); |
---|
854 | } |
---|
855 | |
---|
856 | /** |
---|
857 | * soup_message_set_status_full: |
---|
858 | * @msg: a #SoupMessage |
---|
859 | * @status_code: an HTTP status code |
---|
860 | * @reason_phrase: a description of the status |
---|
861 | * |
---|
862 | * Sets @msg's status code and reason phrase. |
---|
863 | **/ |
---|
864 | void |
---|
865 | soup_message_set_status_full (SoupMessage *msg, |
---|
866 | guint status_code, |
---|
867 | const char *reason_phrase) |
---|
868 | { |
---|
869 | g_return_if_fail (SOUP_IS_MESSAGE (msg)); |
---|
870 | g_return_if_fail (status_code != 0); |
---|
871 | g_return_if_fail (reason_phrase != NULL); |
---|
872 | |
---|
873 | g_free ((char *) msg->reason_phrase); |
---|
874 | |
---|
875 | msg->status_code = status_code; |
---|
876 | msg->reason_phrase = g_strdup (reason_phrase); |
---|
877 | } |
---|
878 | |
---|
879 | |
---|
880 | /** |
---|
881 | * soup_message_add_chunk: |
---|
882 | * @msg: a #SoupMessage |
---|
883 | * @owner: the ownership of @body |
---|
884 | * @body: body data |
---|
885 | * @length: length of @body |
---|
886 | * |
---|
887 | * Adds a chunk of response data to @body. (Note that currently |
---|
888 | * there is no way to send a request using chunked encoding.) |
---|
889 | **/ |
---|
890 | void |
---|
891 | soup_message_add_chunk (SoupMessage *msg, |
---|
892 | SoupOwnership owner, |
---|
893 | const char *body, |
---|
894 | guint length) |
---|
895 | { |
---|
896 | SoupDataBuffer *chunk; |
---|
897 | |
---|
898 | g_return_if_fail (SOUP_IS_MESSAGE (msg)); |
---|
899 | g_return_if_fail (body != NULL || length == 0); |
---|
900 | |
---|
901 | chunk = g_new0 (SoupDataBuffer, 1); |
---|
902 | if (owner == SOUP_BUFFER_USER_OWNED) { |
---|
903 | chunk->owner = SOUP_BUFFER_SYSTEM_OWNED; |
---|
904 | chunk->body = g_memdup (body, length); |
---|
905 | } else { |
---|
906 | chunk->owner = owner; |
---|
907 | chunk->body = (char *)body; |
---|
908 | } |
---|
909 | chunk->length = length; |
---|
910 | |
---|
911 | if (msg->priv->chunks) { |
---|
912 | g_slist_append (msg->priv->last_chunk, chunk); |
---|
913 | msg->priv->last_chunk = msg->priv->last_chunk->next; |
---|
914 | } else { |
---|
915 | msg->priv->chunks = msg->priv->last_chunk = |
---|
916 | g_slist_append (NULL, chunk); |
---|
917 | } |
---|
918 | } |
---|
919 | |
---|
920 | /** |
---|
921 | * soup_message_add_final_chunk: |
---|
922 | * @msg: a #SoupMessage |
---|
923 | * |
---|
924 | * Adds a final, empty chunk of response data to @body. This must |
---|
925 | * be called after adding the last real chunk, to indicate that |
---|
926 | * there is no more data. |
---|
927 | **/ |
---|
928 | void |
---|
929 | soup_message_add_final_chunk (SoupMessage *msg) |
---|
930 | { |
---|
931 | soup_message_add_chunk (msg, SOUP_BUFFER_STATIC, NULL, 0); |
---|
932 | } |
---|
933 | |
---|
934 | /** |
---|
935 | * soup_message_pop_chunk: |
---|
936 | * @msg: a #SoupMessage |
---|
937 | * |
---|
938 | * Pops a chunk of response data from @msg's chunk list. The caller |
---|
939 | * must free @chunk itself, and must handle the data in @chunk |
---|
940 | * according to its %ownership. |
---|
941 | * |
---|
942 | * Return value: the chunk, or %NULL if there are no chunks left. |
---|
943 | **/ |
---|
944 | SoupDataBuffer * |
---|
945 | soup_message_pop_chunk (SoupMessage *msg) |
---|
946 | { |
---|
947 | SoupDataBuffer *chunk; |
---|
948 | |
---|
949 | g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL); |
---|
950 | |
---|
951 | if (!msg->priv->chunks) |
---|
952 | return NULL; |
---|
953 | |
---|
954 | chunk = msg->priv->chunks->data; |
---|
955 | msg->priv->chunks = g_slist_remove (msg->priv->chunks, chunk); |
---|
956 | if (!msg->priv->chunks) |
---|
957 | msg->priv->last_chunk = NULL; |
---|
958 | |
---|
959 | return chunk; |
---|
960 | } |
---|
961 | |
---|
962 | static void |
---|
963 | free_chunks (SoupMessage *msg) |
---|
964 | { |
---|
965 | SoupDataBuffer *chunk; |
---|
966 | GSList *ch; |
---|
967 | |
---|
968 | for (ch = msg->priv->chunks; ch; ch = ch->next) { |
---|
969 | chunk = ch->data; |
---|
970 | |
---|
971 | if (chunk->owner == SOUP_BUFFER_SYSTEM_OWNED) |
---|
972 | g_free (chunk->body); |
---|
973 | g_free (chunk); |
---|
974 | } |
---|
975 | |
---|
976 | g_slist_free (msg->priv->chunks); |
---|
977 | msg->priv->chunks = msg->priv->last_chunk = NULL; |
---|
978 | } |
---|