New header with typedefs, to avoid #include loops among other headers.
[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: 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-connection.h"
12 #include "soup-error.h"
13 #include "soup-marshal.h"
14 #include "soup-message.h"
15 #include "soup-message-private.h"
16 #include "soup-misc.h"
17 #include "soup-context.h"
18 #include "soup-private.h"
19 #include "soup-queue.h"
20
21 #define PARENT_TYPE G_TYPE_OBJECT
22 static GObjectClass *parent_class;
23
24 guint soup_message_signals[LAST_SIGNAL] = { 0 };
25
26 static void cleanup_message (SoupMessage *req);
27
28 static void
29 init (GObject *object)
30 {
31         SoupMessage *msg = SOUP_MESSAGE (object);
32
33         msg->priv = g_new0 (SoupMessagePrivate, 1);
34
35         msg->priv->status  = SOUP_MESSAGE_STATUS_IDLE;
36
37         msg->request_headers = g_hash_table_new (soup_str_case_hash,
38                                                  soup_str_case_equal);
39
40         msg->response_headers = g_hash_table_new (soup_str_case_hash,
41                                                   soup_str_case_equal);
42
43         msg->priv->http_version = SOUP_HTTP_1_1;
44 }
45
46 static void
47 finalize (GObject *object)
48 {
49         SoupMessage *msg = SOUP_MESSAGE (object);
50
51         cleanup_message (msg);
52
53         if (msg->priv->context)
54                 g_object_unref (msg->priv->context);
55
56         if (msg->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
57                 g_free (msg->request.body);
58         if (msg->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
59                 g_free (msg->response.body);
60
61         soup_message_clear_headers (msg->request_headers);
62         g_hash_table_destroy (msg->request_headers);
63
64         soup_message_clear_headers (msg->response_headers);
65         g_hash_table_destroy (msg->response_headers);
66
67         g_slist_foreach (msg->priv->content_handlers, (GFunc) g_free, NULL);
68         g_slist_free (msg->priv->content_handlers);
69
70         g_free ((char *) msg->errorphrase);
71
72         g_free (msg->priv);
73
74         G_OBJECT_CLASS (parent_class)->finalize (object);
75 }
76
77 static void
78 class_init (GObjectClass *object_class)
79 {
80         parent_class = g_type_class_ref (PARENT_TYPE);
81
82         /* virtual method override */
83         object_class->finalize = finalize;
84
85         /* signals */
86         soup_message_signals[WROTE_HEADERS] =
87                 g_signal_new ("wrote_headers",
88                               G_OBJECT_CLASS_TYPE (object_class),
89                               G_SIGNAL_RUN_FIRST,
90                               G_STRUCT_OFFSET (SoupMessageClass, wrote_headers),
91                               NULL, NULL,
92                               soup_marshal_NONE__NONE,
93                               G_TYPE_NONE, 0);
94         soup_message_signals[WROTE_CHUNK] =
95                 g_signal_new ("wrote_chunk",
96                               G_OBJECT_CLASS_TYPE (object_class),
97                               G_SIGNAL_RUN_FIRST,
98                               G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
99                               NULL, NULL,
100                               soup_marshal_NONE__NONE,
101                               G_TYPE_NONE, 0);
102         soup_message_signals[WROTE_BODY] =
103                 g_signal_new ("wrote_body",
104                               G_OBJECT_CLASS_TYPE (object_class),
105                               G_SIGNAL_RUN_FIRST,
106                               G_STRUCT_OFFSET (SoupMessageClass, wrote_body),
107                               NULL, NULL,
108                               soup_marshal_NONE__NONE,
109                               G_TYPE_NONE, 0);
110         soup_message_signals[WRITE_ERROR] =
111                 g_signal_new ("write_error",
112                               G_OBJECT_CLASS_TYPE (object_class),
113                               G_SIGNAL_RUN_FIRST,
114                               G_STRUCT_OFFSET (SoupMessageClass, write_error),
115                               NULL, NULL,
116                               soup_marshal_NONE__NONE,
117                               G_TYPE_NONE, 0);
118
119         soup_message_signals[READ_HEADERS] =
120                 g_signal_new ("read_headers",
121                               G_OBJECT_CLASS_TYPE (object_class),
122                               G_SIGNAL_RUN_FIRST,
123                               G_STRUCT_OFFSET (SoupMessageClass, read_headers),
124                               NULL, NULL,
125                               soup_marshal_NONE__NONE,
126                               G_TYPE_NONE, 0);
127         soup_message_signals[READ_CHUNK] =
128                 g_signal_new ("read_chunk",
129                               G_OBJECT_CLASS_TYPE (object_class),
130                               G_SIGNAL_RUN_FIRST,
131                               G_STRUCT_OFFSET (SoupMessageClass, read_chunk),
132                               NULL, NULL,
133                               soup_marshal_NONE__POINTER,
134                               G_TYPE_NONE, 1,
135                               G_TYPE_POINTER);
136         soup_message_signals[READ_BODY] =
137                 g_signal_new ("read_body",
138                               G_OBJECT_CLASS_TYPE (object_class),
139                               G_SIGNAL_RUN_FIRST,
140                               G_STRUCT_OFFSET (SoupMessageClass, read_body),
141                               NULL, NULL,
142                               soup_marshal_NONE__NONE,
143                               G_TYPE_NONE, 0);
144         soup_message_signals[READ_ERROR] =
145                 g_signal_new ("read_error",
146                               G_OBJECT_CLASS_TYPE (object_class),
147                               G_SIGNAL_RUN_FIRST,
148                               G_STRUCT_OFFSET (SoupMessageClass, read_error),
149                               NULL, NULL,
150                               soup_marshal_NONE__NONE,
151                               G_TYPE_NONE, 0);
152 }
153
154 SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE)
155
156
157 /**
158  * soup_message_new:
159  * @method: the HTTP method for the created request
160  * @uri: the destination endpoint (as a string)
161  * 
162  * Creates a new empty #SoupMessage, which will connect to @uri
163  *
164  * Return value: the new #SoupMessage (or %NULL if @uri could not
165  * be parsed).
166  */
167 SoupMessage *
168 soup_message_new (const char *method, const char *uri)
169 {
170         SoupMessage *msg;
171         SoupContext *ctx;
172
173         ctx = soup_context_get (uri);
174         if (!ctx)
175                 return NULL;
176
177         msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
178         msg->method = method ? method : SOUP_METHOD_GET;
179         msg->priv->context = ctx;
180
181         return msg;
182 }
183
184 /**
185  * soup_message_new_from_uri:
186  * @method: the HTTP method for the created request
187  * @uri: the destination endpoint (as a #SoupUri)
188  * 
189  * Creates a new empty #SoupMessage, which will connect to @uri
190  *
191  * Return value: the new #SoupMessage (or %NULL if @uri is invalid)
192  */
193 SoupMessage *
194 soup_message_new_from_uri (const char *method, const SoupUri *uri)
195 {
196         SoupMessage *msg;
197         SoupContext *ctx;
198
199         ctx = soup_context_from_uri (uri);
200         if (!ctx)
201                 return NULL;
202
203         msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
204         msg->method = method ? method : SOUP_METHOD_GET;
205         msg->priv->context = ctx;
206
207         return msg;
208 }
209
210 /**
211  * soup_message_set_request:
212  * @msg: the message
213  * @content_type: MIME Content-Type of the body
214  * @req_owner: the #SoupOwnership of the passed data buffer.
215  * @req_body: a data buffer containing the body of the message request.
216  * @req_length: the byte length of @req_body.
217  * 
218  * Convenience function to set the request body of a #SoupMessage
219  */
220 void
221 soup_message_set_request (SoupMessage   *msg,
222                           const char    *content_type,
223                           SoupOwnership  req_owner,
224                           char          *req_body,
225                           gulong         req_length)
226 {
227         g_return_if_fail (SOUP_IS_MESSAGE (msg));
228         g_return_if_fail (content_type != NULL);
229         g_return_if_fail (req_body != NULL || req_length == 0);
230
231         soup_message_add_header (msg->request_headers,
232                                  "Content-Type", content_type);
233         msg->request.owner = req_owner;
234         msg->request.body = req_body;
235         msg->request.length = req_length;
236 }
237
238 /**
239  * soup_message_set_response:
240  * @msg: the message
241  * @content_type: MIME Content-Type of the body
242  * @req_owner: the #SoupOwnership of the passed data buffer.
243  * @req_body: a data buffer containing the body of the message response.
244  * @req_length: the byte length of @req_body.
245  * 
246  * Convenience function to set the response body of a #SoupMessage
247  */
248 void
249 soup_message_set_response (SoupMessage   *msg,
250                            const char    *content_type,
251                            SoupOwnership  resp_owner,
252                            char          *resp_body,
253                            gulong         resp_length)
254 {
255         g_return_if_fail (SOUP_IS_MESSAGE (msg));
256         g_return_if_fail (content_type != NULL);
257         g_return_if_fail (resp_body != NULL || resp_length == 0);
258
259         soup_message_add_header (msg->response_headers,
260                                  "Content-Type", content_type);
261         msg->response.owner = resp_owner;
262         msg->response.body = resp_body;
263         msg->response.length = resp_length;
264 }
265
266 static void
267 cleanup_message (SoupMessage *req)
268 {
269         if (req->priv->read_state)
270                 soup_message_read_cancel (req);
271
272         if (req->priv->write_state)
273                 soup_message_write_cancel (req);
274
275         if (req->priv->connect_tag) {
276                 soup_context_cancel_connect (req->priv->connect_tag);
277                 req->priv->connect_tag = NULL;
278         }
279
280         soup_message_set_connection (req, NULL);
281
282         soup_queue_remove_request (req);
283 }
284
285 /**
286  * soup_message_issue_callback:
287  * @req: a #SoupMessage currently being processed.
288  * @error: a #SoupErrorCode to be passed to @req's completion callback.
289  * 
290  * Finalizes the message request, by first freeing any temporary
291  * resources, then issuing the callback function pointer passed in
292  * soup_message_new() or soup_message_new_full(). If, after returning
293  * from the callback, the message has not been requeued, @req will be
294  * unreffed.
295  */
296 void
297 soup_message_issue_callback (SoupMessage *req)
298 {
299         g_return_if_fail (SOUP_IS_MESSAGE (req));
300
301         /*
302          * Make sure we don't have some icky recursion if the callback
303          * runs the main loop, and the connection has some data or error
304          * which causes the callback to be run again.
305          */
306         cleanup_message (req);
307
308         if (req->priv->callback) {
309                 (*req->priv->callback) (req, req->priv->user_data);
310
311                 if (!SOUP_MESSAGE_IS_STARTING (req))
312                         g_object_unref (req);
313         }
314 }
315
316 /**
317  * soup_message_disconnect:
318  * @msg: a #SoupMessage
319  *
320  * Utility function to close and unref the connection associated with
321  * @msg if there was an error.
322  **/
323 void
324 soup_message_disconnect (SoupMessage *msg)
325 {
326         if (msg->priv->connection) {
327                 soup_connection_disconnect (msg->priv->connection);
328                 soup_message_set_connection (msg, NULL);
329         }
330 }
331
332 /**
333  * soup_message_cancel:
334  * @msg: a #SoupMessage currently being processed.
335  * 
336  * Cancel a running message, and issue completion callback with an
337  * error code of %SOUP_ERROR_CANCELLED. If not requeued by the
338  * completion callback, the @msg will be destroyed.
339  */
340 void
341 soup_message_cancel (SoupMessage *msg)
342 {
343         soup_message_set_error (msg, SOUP_ERROR_CANCELLED);
344         soup_message_disconnect (msg);
345         soup_message_issue_callback (msg);
346 }
347
348 static gboolean
349 free_header_list (gpointer name, gpointer vals, gpointer user_data)
350 {
351         g_free (name);
352         g_slist_foreach (vals, (GFunc) g_free, NULL);
353         g_slist_free (vals);
354
355         return TRUE;
356 }
357
358 void
359 soup_message_clear_headers (GHashTable *hash)
360 {
361         g_return_if_fail (hash != NULL);
362
363         g_hash_table_foreach_remove (hash, free_header_list, NULL);
364 }
365
366 void
367 soup_message_remove_header (GHashTable *hash, const char *name)
368 {
369         gpointer old_key, old_vals;
370
371         g_return_if_fail (hash != NULL);
372         g_return_if_fail (name != NULL || name[0] != '\0');
373
374         if (g_hash_table_lookup_extended (hash, name, &old_key, &old_vals)) {
375                 g_hash_table_remove (hash, name);
376                 free_header_list (old_key, old_vals, NULL);
377         }
378 }
379
380 void
381 soup_message_add_header (GHashTable *hash, const char *name, const char *value)
382 {
383         GSList *old_value;
384
385         g_return_if_fail (hash != NULL);
386         g_return_if_fail (name != NULL || name [0] != '\0');
387         g_return_if_fail (value != NULL);
388
389         old_value = g_hash_table_lookup (hash, name);
390
391         if (old_value)
392                 g_slist_append (old_value, g_strdup (value));
393         else {
394                 g_hash_table_insert (hash, g_strdup (name),
395                                      g_slist_append (NULL, g_strdup (value)));
396         }
397 }
398
399 /**
400  * soup_message_get_header:
401  * @hash: a header hash table
402  * @name: header name.
403  * 
404  * Lookup the first transport header in @hash with a key equal to
405  * @name.
406  * 
407  * Return value: the header's value or %NULL if not found.
408  */
409 const char *
410 soup_message_get_header (GHashTable *hash, const char *name)
411 {
412         GSList *vals;
413
414         g_return_val_if_fail (hash != NULL, NULL);
415         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
416
417         vals = g_hash_table_lookup (hash, name);
418         if (vals)
419                 return vals->data;
420
421         return NULL;
422 }
423
424 /**
425  * soup_message_get_header_list:
426  * @hash: a header hash table
427  * @name: header name.
428  * 
429  * Lookup the all transport request headers in @hash with a key equal
430  * to @name.
431  * 
432  * Return value: a const pointer to a #GSList of header values or
433  * %NULL if not found.
434  */
435 const GSList *
436 soup_message_get_header_list (GHashTable *hash, const char *name)
437 {
438         g_return_val_if_fail (hash != NULL, NULL);
439         g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
440
441         return g_hash_table_lookup (hash, name);
442 }
443
444 typedef struct {
445         GHFunc   func;
446         gpointer user_data;
447 } SoupMessageForeachHeaderData;
448
449 static void
450 foreach_value_in_list (gpointer name, gpointer value, gpointer user_data)
451 {
452         GSList *vals = value;
453         SoupMessageForeachHeaderData *data = user_data;
454
455         while (vals) {
456                 (*data->func) (name, vals->data, data->user_data);
457                 vals = vals->next;
458         }
459 }
460
461 void
462 soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
463 {
464         SoupMessageForeachHeaderData data;
465
466         g_return_if_fail (hash != NULL);
467         g_return_if_fail (func != NULL);
468
469         data.func = func;
470         data.user_data = user_data;
471         g_hash_table_foreach (hash, foreach_value_in_list, &data);
472 }
473
474 static void
475 queue_message (SoupMessage *req)
476 {
477         if (!req->priv->context) {
478                 soup_message_set_error_full (req, 
479                                              SOUP_ERROR_CANCELLED,
480                                              "Attempted to queue a message "
481                                              "with no destination context");
482                 soup_message_issue_callback (req);
483                 return;
484         }
485
486         if (req->priv->status != SOUP_MESSAGE_STATUS_IDLE)
487                 cleanup_message (req);
488
489         switch (req->response.owner) {
490         case SOUP_BUFFER_USER_OWNED:
491                 soup_message_set_error_full (req, 
492                                              SOUP_ERROR_CANCELLED,
493                                              "Attempted to queue a message "
494                                              "with a user owned response "
495                                              "buffer.");
496                 soup_message_issue_callback (req);
497                 return;
498         case SOUP_BUFFER_SYSTEM_OWNED:
499                 g_free (req->response.body);
500                 break;
501         case SOUP_BUFFER_STATIC:
502                 break;
503         }
504
505         req->response.owner = 0;
506         req->response.body = NULL;
507         req->response.length = 0;
508
509         soup_message_clear_headers (req->response_headers);
510
511         req->errorcode = 0;
512         req->errorclass = 0;
513
514         if (req->errorphrase) {
515                 g_free ((char *) req->errorphrase);
516                 req->errorphrase = NULL;
517         }
518
519         soup_queue_message (req);
520 }
521
522 /**
523  * soup_message_queue:
524  * @req: a #SoupMessage.
525  * @callback: a #SoupCallbackFn which will be called after the message
526  * completes or when an unrecoverable error occurs.
527  * @user_data: a pointer passed to @callback.
528  * 
529  * Queues the message @req for sending. All messages are processed
530  * while the glib main loop runs. If this #SoupMessage has been
531  * processed before, any resources related to the time it was last
532  * sent are freed.
533  *
534  * If the response #SoupDataBuffer has an owner of
535  * %SOUP_BUFFER_USER_OWNED, the message will not be queued, and
536  * @callback will be called with a #SoupErrorCode of
537  * %SOUP_ERROR_CANCELLED.
538  *
539  * Upon message completetion, the callback specified in @callback will
540  * be invoked. If after returning from this callback the message has
541  * not been requeued using soup_message_queue(), @req will be unreffed.
542  */
543 void
544 soup_message_queue (SoupMessage    *req,
545                     SoupCallbackFn  callback,
546                     gpointer        user_data)
547 {
548         g_return_if_fail (SOUP_IS_MESSAGE (req));
549
550         req->priv->callback = callback;
551         req->priv->user_data = user_data;
552
553         queue_message (req);
554 }
555
556 static void
557 requeue_read_error (SoupMessage *msg, gpointer user_data)
558 {
559         soup_message_disconnect (msg);
560         queue_message (msg);
561 }
562
563 static void
564 requeue_read_finished (SoupMessage *msg, gpointer user_data)
565 {
566         SoupConnection *conn = msg->priv->connection;
567
568         g_object_ref (conn);
569         soup_message_set_connection (msg, NULL);
570
571         if (soup_connection_is_connected (conn)) {
572                 soup_connection_mark_old (conn);
573         } else {
574                 g_object_unref (conn);
575                 conn = NULL;
576         }
577
578         queue_message (msg);
579         soup_message_set_connection (msg, conn);
580 }
581
582 /**
583  * soup_message_requeue:
584  * @req: a #SoupMessage
585  *
586  * This causes @req to be placed back on the queue to be attempted
587  * again.
588  **/
589 void
590 soup_message_requeue (SoupMessage *req)
591 {
592         g_return_if_fail (SOUP_IS_MESSAGE (req));
593
594         if (req->priv->connection && req->priv->read_state) {
595                 soup_message_read_set_callbacks (req, NULL, NULL,
596                                                  requeue_read_finished,
597                                                  requeue_read_error, NULL);
598
599                 if (req->priv->write_state)
600                         soup_message_write_cancel (req);
601         } else
602                 queue_message (req);
603 }
604
605 /**
606  * soup_message_send:
607  * @msg: a #SoupMessage.
608  * 
609  * Synchronously send @msg. This call will not return until the
610  * transfer is finished successfully or there is an unrecoverable
611  * error.
612  *
613  * @msg is not freed upon return.
614  *
615  * Return value: the #SoupErrorClass of the error encountered while
616  * sending or reading the response.
617  */
618 SoupErrorClass
619 soup_message_send (SoupMessage *msg)
620 {
621         soup_message_queue (msg, NULL, NULL);
622
623         while (1) {
624                 g_main_iteration (TRUE);
625
626                 if (msg->priv->status == SOUP_MESSAGE_STATUS_FINISHED ||
627                     SOUP_ERROR_IS_TRANSPORT (msg->errorcode))
628                         break;
629
630                 /* Quit if soup_shutdown has been called */
631                 if (!soup_initialized)
632                         return SOUP_ERROR_CANCELLED;
633         }
634
635         return msg->errorclass;
636 }
637
638 void
639 soup_message_set_flags (SoupMessage *msg, guint flags)
640 {
641         g_return_if_fail (SOUP_IS_MESSAGE (msg));
642
643         msg->priv->msg_flags = flags;
644 }
645
646 guint
647 soup_message_get_flags (SoupMessage *msg)
648 {
649         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
650
651         return msg->priv->msg_flags;
652 }
653
654 void
655 soup_message_set_http_version  (SoupMessage *msg, SoupHttpVersion version)
656 {
657         g_return_if_fail (SOUP_IS_MESSAGE (msg));
658
659         msg->priv->http_version = version;
660 }
661
662 SoupHttpVersion
663 soup_message_get_http_version (SoupMessage *msg)
664 {
665         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), SOUP_HTTP_1_0);
666
667         return msg->priv->http_version;
668 }
669
670 gboolean
671 soup_message_is_keepalive (SoupMessage *msg)
672 {
673         const char *c_conn, *s_conn;
674
675         c_conn = soup_message_get_header (msg->request_headers, "Connection");
676         s_conn = soup_message_get_header (msg->response_headers, "Connection");
677
678         if (msg->priv->http_version == SOUP_HTTP_1_0) {
679                 /* Only persistent if the client requested keepalive
680                  * and the server agreed.
681                  */
682
683                 if (!c_conn || !s_conn)
684                         return FALSE;
685                 if (g_strcasecmp (c_conn, "Keep-Alive") != 0 ||
686                     g_strcasecmp (s_conn, "Keep-Alive") != 0)
687                         return FALSE;
688
689                 return TRUE;
690         } else {
691                 /* Persistent unless either side requested otherwise */
692
693                 if (c_conn && g_strcasecmp (c_conn, "close") == 0)
694                         return FALSE;
695                 if (s_conn && g_strcasecmp (s_conn, "close") == 0)
696                         return FALSE;
697
698                 return TRUE;
699         }
700 }
701
702 void
703 soup_message_set_context (SoupMessage *msg, SoupContext *new_ctx)
704 {
705         g_return_if_fail (SOUP_IS_MESSAGE (msg));
706
707         if (msg->priv->context && new_ctx) {
708                 const SoupUri *old, *new;
709
710                 old = soup_context_get_uri (msg->priv->context);
711                 new = soup_context_get_uri (new_ctx);
712                 if (strcmp (old->host, new->host) != 0)
713                         cleanup_message (msg);
714         } else if (!new_ctx)
715                 cleanup_message (msg);
716
717         if (new_ctx)
718                 g_object_ref (new_ctx);
719         if (msg->priv->context)
720                 g_object_unref (msg->priv->context);
721
722         msg->priv->context = new_ctx;
723 }
724
725 const SoupUri *
726 soup_message_get_uri (SoupMessage *msg)
727 {
728         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
729
730         return soup_context_get_uri (msg->priv->context);
731 }
732
733 void
734 soup_message_set_connection (SoupMessage *msg, SoupConnection *conn)
735 {
736         if (conn) {
737                 soup_connection_set_in_use (conn, TRUE);
738                 g_object_ref (conn);
739         }
740         if (msg->priv->connection) {
741                 soup_connection_set_in_use (msg->priv->connection, FALSE);
742                 g_object_unref (msg->priv->connection);
743         }
744
745         msg->priv->connection = conn;
746
747         if (conn) {
748                 msg->priv->socket = soup_connection_get_socket (conn);
749                 g_object_ref (msg->priv->socket);
750         } else if (msg->priv->socket) {
751                 g_object_unref (msg->priv->socket);
752                 msg->priv->socket = NULL;
753         }
754 }
755
756 SoupConnection *
757 soup_message_get_connection (SoupMessage *msg)
758 {
759         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
760
761         return msg->priv->connection;
762 }
763
764 SoupSocket *
765 soup_message_get_socket (SoupMessage *msg)
766 {
767         g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
768
769         return msg->priv->socket;
770 }
771
772 void
773 soup_message_set_error (SoupMessage *msg, SoupKnownErrorCode errcode)
774 {
775         g_return_if_fail (SOUP_IS_MESSAGE (msg));
776         g_return_if_fail (errcode != 0);
777
778         g_free ((char *) msg->errorphrase);
779
780         msg->errorcode = errcode;
781         msg->errorclass = soup_error_get_class (errcode);
782         msg->errorphrase = g_strdup (soup_error_get_phrase (errcode));
783 }
784
785 void
786 soup_message_set_error_full (SoupMessage *msg,
787                              guint        errcode,
788                              const char  *errphrase)
789 {
790         g_return_if_fail (SOUP_IS_MESSAGE (msg));
791         g_return_if_fail (errcode != 0);
792         g_return_if_fail (errphrase != NULL);
793
794         g_free ((char *) msg->errorphrase);
795
796         msg->errorcode = errcode;
797         msg->errorclass = soup_error_get_class (errcode);
798         msg->errorphrase = g_strdup (errphrase);
799 }