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