Allow override of http version. Only include Content-Length if method is
[platform/upstream/libsoup.git] / libsoup / soup-message.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-message.c: Asyncronous Callback-based SOAP Request Queue.
4  *
5  * Authors:
6  *      Alex Graveley (alex@helixcode.com)
7  *
8  * Copyright (C) 2000, Helix Code, Inc.
9  */
10
11 #include "soup-message.h"
12 #include "soup-misc.h"
13 #include "soup-context.h"
14 #include "soup-private.h"
15 #include "soup-transfer.h"
16
17 static SoupErrorCode 
18 authorize_handler (SoupMessage *msg, gboolean proxy)
19 {
20         const char *auth_header;
21         SoupAuth *auth;
22         SoupContext *ctx;
23
24         ctx = proxy ? soup_get_proxy () : msg->context;
25
26         auth_header = 
27                 soup_message_get_response_header (
28                         msg, 
29                         proxy ? "Proxy-Authenticate" : "WWW-Authenticate");
30         if (!auth_header) return SOUP_ERROR_CANT_AUTHENTICATE;
31
32         auth = soup_auth_new_from_header (ctx, auth_header);
33         if (!auth) return SOUP_ERROR_MALFORMED_HEADER;
34
35         if (ctx->auth) {
36                 if (soup_auth_invalidates_prior (auth))
37                         soup_auth_free (ctx->auth);
38                 else {
39                         soup_auth_free (auth);
40                         return SOUP_ERROR_CANT_AUTHENTICATE;
41                 }
42         }
43
44         ctx->auth = auth;
45
46         if (msg->priv->req_header) {
47                 g_string_free (msg->priv->req_header, TRUE);
48                 msg->priv->req_header = NULL;
49         }
50
51         soup_message_queue (msg, msg->priv->callback, msg->priv->user_data);
52
53         return SOUP_ERROR_NONE;
54 }
55
56 static SoupErrorCode 
57 redirect_handler (SoupMessage *msg, gpointer user_data)
58 {
59         const gchar *new_url;
60
61         switch (msg->response_code) {
62         case 300: /* Multiple Choices */
63         case 301: /* Moved Permanently */
64         case 302: /* Moved Temporarily */
65         case 303: /* See Other */
66         case 305: /* Use Proxy */
67                 break;
68         default:
69                 return SOUP_ERROR_NONE;
70         }
71
72         if (msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT) 
73                 return SOUP_ERROR_NONE;
74
75         new_url = soup_message_get_response_header (msg, "Location");
76
77         if (new_url) {
78                 SoupContext *new_ctx = soup_context_get (new_url);
79                 if (!new_ctx) return SOUP_ERROR_MALFORMED_HEADER;
80
81                 soup_context_unref (msg->context);
82                 msg->context = new_ctx;
83
84                 soup_message_queue (msg,
85                                     msg->priv->callback, 
86                                     msg->priv->user_data);
87         }
88
89         return SOUP_ERROR_NONE;
90 }
91
92 /**
93  * soup_message_new:
94  * @context: a %SoupContext for the destination endpoint.
95  * @action: a string which will be used as the SOAPAction header for the created
96  * request.
97  * 
98  * Creates a new empty %SoupMessage, which will connect to the URL represented
99  * by @context. The new message has a status of @SOUP_STATUS_IDLE.
100  *
101  * Return value: the new %SoupMessage.
102  */
103 SoupMessage *
104 soup_message_new (SoupContext *context, SoupAction action) 
105 {
106         SoupMessage *ret;
107
108         g_return_val_if_fail (context, NULL);
109
110         ret          = g_new0 (SoupMessage, 1);
111         ret->priv    = g_new0 (SoupMessagePrivate, 1);
112         ret->status  = SOUP_STATUS_IDLE;
113         ret->action  = g_strdup (action);
114         ret->context = context;
115         ret->method  = SOUP_METHOD_POST;
116
117         ret->priv->http_version = SOUP_HTTP_1_1;
118
119         soup_context_ref (context);
120
121         /*
122          * Add a 401 (Authorization Required) response code handler if the
123          * context URI has a login user name.
124          */
125         if (soup_context_get_uri (context)->user)
126                 soup_message_add_response_code_handler (
127                         ret, 
128                         401, 
129                         SOUP_HANDLER_POST_BODY, 
130                         (SoupHandlerFn) authorize_handler, 
131                         GINT_TO_POINTER (FALSE));
132
133         /*
134          * Always add a 407 (Proxy-Authorization Required) handler, in case the
135          * proxy is reset after message creation.
136          */
137         soup_message_add_response_code_handler (
138                         ret, 
139                         407, 
140                         SOUP_HANDLER_POST_BODY, 
141                         (SoupHandlerFn) authorize_handler, 
142                         GINT_TO_POINTER (TRUE));
143
144         /* 
145          * Handle redirect response codes 300, 301, 302, 303, and 305.
146          */
147         soup_message_add_header_handler (ret,
148                                          "Location",
149                                          SOUP_HANDLER_PRE_BODY,
150                                          redirect_handler,
151                                          NULL);
152
153         return ret;
154 }
155
156 /**
157  * soup_message_new_full:
158  * @context: a %SoupContext for the destination endpoint.
159  * @action: a string which will be used as the SOAPAction header for the created
160  * request.
161  * @req_owner: the %SoupOwnership of the passed data buffer.
162  * @req_body: a data buffer containing the body of the message request.
163  * @req_length: the byte length of @req_body.
164  * 
165  * Creates a new %SoupMessage, which will connect to the URL represented by
166  * @context. The new message has a status of @SOUP_STATUS_IDLE. The request data
167  * buffer will be filled from @req_owner, @req_body, and @req_length
168  * respectively.
169  *
170  * Return value: the new %SoupMessage.
171  */
172 SoupMessage *
173 soup_message_new_full (SoupContext   *context,
174                        SoupAction     action,
175                        SoupOwnership  req_owner,
176                        gchar         *req_body,
177                        gulong         req_length)
178 {
179         SoupMessage *ret = soup_message_new (context, action);
180
181         ret->request.owner = req_owner;
182         ret->request.body = req_body;
183         ret->request.length = req_length;
184
185         return ret;
186 }
187
188 /**
189  * soup_message_cleanup:
190  * @req: a %SoupMessage.
191  * @action: a string which will be used as the SOAPAction header for the created
192  * request.
193  * 
194  * Frees any temporary resources created in the processing of @req. Request and
195  * response data buffers are left intact.
196  */
197 void 
198 soup_message_cleanup (SoupMessage *req)
199 {
200         g_return_if_fail (req != NULL);
201
202         if (req->priv->read_tag) {
203                 soup_transfer_read_cancel (req->priv->read_tag);
204                 req->priv->read_tag = 0;
205         }
206
207         if (req->priv->write_tag) {
208                 soup_transfer_write_cancel (req->priv->write_tag);
209                 req->priv->write_tag = 0;
210         }
211
212         if (req->priv->connect_tag) {
213                 soup_context_cancel_connect (req->priv->connect_tag);
214                 req->priv->connect_tag = NULL;
215         }
216         if (req->priv->conn) {
217                 soup_connection_release (req->priv->conn);
218                 req->priv->conn = NULL;
219         }
220
221         soup_active_requests = g_slist_remove (soup_active_requests, req);
222 }
223
224 static void
225 soup_message_remove_header (gchar *name, gchar *value, gpointer unused)
226 {
227         g_free (name);
228         g_free (value);
229 }
230
231 /**
232  * soup_message_free:
233  * @req: a %SoupMessage to destroy.
234  * 
235  * Destroys the %SoupMessage pointed to by @req. Request and response headers
236  * are freed. Request and response data buffers are also freed if their
237  * ownership is %SOUP_BUFFER_SYSTEM_OWNED. The message's destination context
238  * will be de-referenced.
239  */
240 void 
241 soup_message_free (SoupMessage *req)
242 {
243         g_return_if_fail (req != NULL);
244
245         soup_message_cleanup (req);
246
247         soup_context_unref (req->context);
248
249         if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
250                 g_free (req->request.body);
251         if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
252                 g_free (req->response.body);
253
254         if (req->priv->req_header) 
255                 g_string_free (req->priv->req_header, TRUE);
256
257         if (req->request_headers) {
258                 g_hash_table_foreach (req->request_headers,
259                                       (GHFunc) soup_message_remove_header,
260                                       NULL);
261                 g_hash_table_destroy (req->request_headers);
262         }
263
264         if (req->response_headers) {
265                 g_hash_table_foreach (req->response_headers,
266                                       (GHFunc) soup_message_remove_header,
267                                       NULL);
268                 g_hash_table_destroy (req->response_headers);
269         }
270
271         g_slist_foreach (req->priv->content_handlers, (GFunc) g_free, NULL);
272         g_slist_free (req->priv->content_handlers);
273
274         g_free (req->priv);
275         g_free (req->action);
276         g_free (req);
277 }
278
279 /**
280  * soup_message_issue_callback:
281  * @req: a %SoupMessage currently being processed.
282  * @error: a %SoupErrorCode to be passed to %req's completion callback.
283  * 
284  * Finalizes the message request, by first freeing any temporary resources, then
285  * issuing the callback function pointer passed in %soup_message_new or
286  * %soup_message_new_full. If, after returning from the callback, the message
287  * has not been requeued, @msg is destroyed using %soup_message_free.
288  */
289 void
290 soup_message_issue_callback (SoupMessage *req, SoupErrorCode error)
291 {
292         g_return_if_fail (req != NULL);
293
294         /* 
295          * Make sure we don't have some icky recursion if the callback 
296          * runs the main loop, and the connection has some data or error 
297          * which causes the callback to be run again.
298          */
299         soup_message_cleanup (req);
300
301         req->priv->errorcode = error;
302
303         if (req->priv->callback) {
304                 (*req->priv->callback) (req, error, req->priv->user_data);
305
306                 if (req->status != SOUP_STATUS_QUEUED)
307                         soup_message_free (req);
308         }
309 }
310
311 /**
312  * soup_message_cancel:
313  * @req: a %SoupMessage currently being processed.
314  * 
315  * Cancel a running message, and issue completion callback with a
316  * %SoupTransferStatus of %SOUP_ERROR_CANCELLED. If not requeued by the
317  * completion callback, the @msg will be destroyed.
318  */
319 void 
320 soup_message_cancel (SoupMessage *req) 
321 {
322         soup_message_issue_callback (req, SOUP_ERROR_CANCELLED);
323 }
324
325 static void 
326 soup_message_set_header (GHashTable  **hash,
327                          const gchar  *name,
328                          const gchar  *value) 
329 {
330         gpointer old_name, old_value;
331
332         if (!*hash) 
333                 *hash = g_hash_table_new (soup_str_case_hash, 
334                                           soup_str_case_equal);
335         else if (g_hash_table_lookup_extended (*hash, 
336                                                name, 
337                                                &old_name, 
338                                                &old_value)) {
339                 g_hash_table_remove (*hash, name);
340                 g_free (old_name);
341                 g_free (old_value);
342         }
343
344         if (value)
345                 g_hash_table_insert (*hash, g_strdup (name), g_strdup (value));
346 }
347
348 /**
349  * soup_message_set_request_header:
350  * @req: a %SoupMessage.
351  * @name: header name.
352  * @value: header value.
353  * 
354  * Adds a new transport header to be sent on an outgoing request. Passing a NULL
355  * @value will remove the header name supplied.
356  */
357 void
358 soup_message_set_request_header (SoupMessage *req,
359                                  const gchar *name,
360                                  const gchar *value) 
361 {
362         g_return_if_fail (req != NULL);
363         g_return_if_fail (name != NULL || name [0] != '\0');
364
365         if (req->priv->req_header) {
366                 g_string_free (req->priv->req_header, TRUE);
367                 req->priv->req_header = NULL;
368         }
369
370         soup_message_set_header (&req->request_headers, name, value);
371 }
372
373 /**
374  * soup_message_get_request_header:
375  * @req: a %SoupMessage.
376  * @name: header name.
377  * 
378  * Lookup the transport request header with a key equal to @name.
379  *
380  * Return value: the header's value or NULL if not found.
381  */
382 const gchar *
383 soup_message_get_request_header (SoupMessage *req,
384                                  const gchar *name) 
385 {
386         g_return_val_if_fail (req != NULL, NULL);
387         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
388
389         return req->request_headers ? 
390                 g_hash_table_lookup (req->request_headers, name) : NULL;
391 }
392
393 /**
394  * soup_message_set_response_header:
395  * @req: a %SoupMessage.
396  * @name: header name.
397  * @value: header value.
398  * 
399  * Adds a new transport header to be sent on an outgoing response. Passing a
400  * NULL @value will remove the header name supplied.
401  */
402 void
403 soup_message_set_response_header (SoupMessage *req,
404                                   const gchar *name,
405                                   const gchar *value) 
406 {
407         g_return_if_fail (req != NULL);
408         g_return_if_fail (name != NULL || name [0] != '\0');
409
410         soup_message_set_header (&req->response_headers, name, value);
411 }
412
413 /**
414  * soup_message_get_response_header:
415  * @req: a %SoupMessage.
416  * @name: header name.
417  * 
418  * Lookup the transport response header with a key equal to @name.
419  *
420  * Return value: the header's value or NULL if not found.
421  */
422 const gchar *
423 soup_message_get_response_header (SoupMessage *req,
424                                   const gchar *name) 
425 {
426         g_return_val_if_fail (req != NULL, NULL);
427         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
428
429         return req->response_headers ? 
430                 g_hash_table_lookup (req->response_headers, name) : NULL;
431 }
432
433 /**
434  * soup_message_send:
435  * @msg: a %SoupMessage.
436  * 
437  * Syncronously send @msg. This call will not return until the transfer is
438  * finished successfully or there is an unrecoverable error. 
439  *
440  * @msg is not free'd upon return.
441  *
442  * Return value: the %SoupErrorCode of the error encountered while sending, or
443  * SOUP_ERROR_NONE.
444  */
445 SoupErrorCode 
446 soup_message_send (SoupMessage *msg)
447 {
448         soup_message_queue (msg, NULL, NULL);
449
450         while (1) {
451                 g_main_iteration (TRUE); 
452                 if (msg->status == SOUP_STATUS_FINISHED ||
453                     msg->priv->errorcode != SOUP_ERROR_NONE)
454                         return msg->priv->errorcode;
455         }
456
457         return SOUP_ERROR_NONE;
458 }
459
460 void
461 soup_message_set_method (SoupMessage *msg, const gchar *method)
462 {
463         g_return_if_fail (msg != NULL);
464         g_return_if_fail (method != NULL);
465
466         msg->method = method;
467 }
468
469 const gchar *
470 soup_message_get_method (SoupMessage *msg)
471 {
472         g_return_val_if_fail (msg != NULL, NULL);
473
474         return msg->method;
475 }
476
477 typedef enum {
478         RESPONSE_HEADER_HANDLER,
479         RESPONSE_CODE_HANDLER,
480         RESPONSE_BODY_HANDLER
481 } SoupHandlerKind;
482
483 typedef struct {
484         SoupHandlerType   type;
485         SoupHandlerFn     handler_cb;
486         gpointer          user_data;
487
488         SoupHandlerKind   kind;
489         const gchar      *header;
490         guint             code;
491 } SoupHandlerData;
492
493 static void 
494 soup_message_add_handler (SoupMessage      *msg,
495                           SoupHandlerType   type,
496                           SoupHandlerFn     handler_cb,
497                           gpointer          user_data,
498                           SoupHandlerKind   kind,
499                           const gchar      *header,
500                           guint             code)
501 {
502         SoupHandlerData *data;
503
504         data = g_new0 (SoupHandlerData, 1);
505         data->type = type;
506         data->handler_cb = handler_cb;
507         data->user_data = user_data;
508         data->kind = kind;
509         data->header = header;
510         data->code = code;
511
512         msg->priv->content_handlers = 
513                 g_slist_append (msg->priv->content_handlers, data);
514 }
515
516 void 
517 soup_message_add_header_handler (SoupMessage      *msg,
518                                  const gchar      *header,
519                                  SoupHandlerType   type,
520                                  SoupHandlerFn     handler_cb,
521                                  gpointer          user_data)
522 {
523         g_return_if_fail (msg != NULL);
524         g_return_if_fail (header != NULL);
525         g_return_if_fail (handler_cb != NULL);
526
527         soup_message_add_handler (msg, 
528                                   type, 
529                                   handler_cb, 
530                                   user_data, 
531                                   RESPONSE_HEADER_HANDLER, 
532                                   header, 
533                                   0);
534 }
535
536 void 
537 soup_message_add_response_code_handler (SoupMessage      *msg,
538                                         guint             code,
539                                         SoupHandlerType   type,
540                                         SoupHandlerFn     handler_cb,
541                                         gpointer          user_data)
542 {
543         g_return_if_fail (msg != NULL);
544         g_return_if_fail (code != 0);
545         g_return_if_fail (handler_cb != NULL);
546
547         soup_message_add_handler (msg, 
548                                   type, 
549                                   handler_cb, 
550                                   user_data, 
551                                   RESPONSE_CODE_HANDLER, 
552                                   NULL, 
553                                   code);
554 }
555
556 void 
557 soup_message_add_body_handler (SoupMessage      *msg,
558                                SoupHandlerType   type,
559                                SoupHandlerFn     handler_cb,
560                                gpointer          user_data)
561 {
562         g_return_if_fail (msg != NULL);
563         g_return_if_fail (handler_cb != NULL);
564
565         soup_message_add_handler (msg, 
566                                   type, 
567                                   handler_cb, 
568                                   user_data, 
569                                   RESPONSE_BODY_HANDLER, 
570                                   NULL, 
571                                   0);
572 }
573
574 SoupErrorCode 
575 soup_message_run_handlers (SoupMessage *msg, SoupHandlerType invoke_type)
576 {
577         GSList *list;
578         SoupErrorCode retval = SOUP_ERROR_NONE;
579
580         g_return_val_if_fail (msg != NULL, retval);
581         
582         for (list = msg->priv->content_handlers; list; list = list->next) {
583                 SoupHandlerData *data = list->data;
584                 
585                 if (data->type != invoke_type) continue;
586
587                 switch (data->kind) {
588                 case RESPONSE_HEADER_HANDLER:
589                         if (!soup_message_get_response_header (msg,
590                                                                data->header))
591                                 continue;
592                         break;
593                 case RESPONSE_CODE_HANDLER:
594                         if (msg->response_code != data->code) continue;
595                         break;
596                 case RESPONSE_BODY_HANDLER:
597                         break;
598                 }
599
600                 retval = (*data->handler_cb) (msg, data->user_data);
601
602                 if (retval != SOUP_ERROR_NONE) break;
603                 if (msg->status == SOUP_STATUS_QUEUED) break;
604         }
605
606         return retval;
607 }
608
609 static void
610 soup_message_remove_handler (SoupMessage   *msg, 
611                              SoupHandlerFn  handler_cb,
612                              gpointer       user_data)
613 {
614         GSList *iter = msg->priv->content_handlers;
615
616         while (iter) {
617                 SoupHandlerData *data = iter->data;
618
619                 if (data->handler_cb == handler_cb &&
620                     data->user_data == user_data) {
621                         msg->priv->content_handlers = 
622                                 g_slist_remove_link (
623                                         msg->priv->content_handlers,
624                                         iter);
625                         g_free (data);
626                         break;
627                 }
628                 
629                 iter = iter->next;
630         }
631 }
632
633 static inline gboolean
634 ADDED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
635 {
636         return ((newflags & find) && !(msg->priv->msg_flags & find));
637 }
638
639 static inline gboolean
640 REMOVED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
641 {
642         return (!(newflags & find) && (msg->priv->msg_flags & find));
643 }
644
645 void
646 soup_message_set_flags (SoupMessage *msg, guint flags)
647 {
648         g_return_if_fail (msg != NULL);
649
650         if (ADDED_FLAG (msg, flags, SOUP_MESSAGE_NO_REDIRECT))
651                 soup_message_remove_handler (msg, 
652                                              redirect_handler,
653                                              NULL);
654         else if (REMOVED_FLAG (msg, flags, SOUP_MESSAGE_NO_REDIRECT))
655                 soup_message_add_header_handler (msg,
656                                                  "Location",
657                                                  SOUP_HANDLER_PRE_BODY,
658                                                  redirect_handler,
659                                                  NULL);
660
661         msg->priv->msg_flags = flags;
662 }
663
664 guint
665 soup_message_get_flags (SoupMessage *msg)
666 {
667         g_return_val_if_fail (msg != NULL, 0);
668
669         return msg->priv->msg_flags;
670 }
671
672 void 
673 soup_message_set_http_version  (SoupMessage *msg, SoupHttpVersion version)
674 {
675         g_return_if_fail (msg != NULL);
676
677         msg->priv->http_version = version;
678 }