1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-message.c: Asyncronous Callback-based SOAP Request Queue.
6 * Alex Graveley (alex@helixcode.com)
8 * Copyright (C) 2000, Helix Code, Inc.
11 #include "soup-auth.h"
12 #include "soup-error.h"
13 #include "soup-message.h"
14 #include "soup-misc.h"
15 #include "soup-context.h"
16 #include "soup-private.h"
17 #include "soup-queue.h"
18 #include "soup-transfer.h"
21 SoupHandlerEvent type;
23 SoupHandlerWhen order;
24 SoupHandlerFilter filter;
25 SoupHandlerFn handler_cb;
34 * @context: a %SoupContext for the destination endpoint.
35 * @method: a string which will be used as the HTTP method for the created
36 * request, if NULL a GET request will be made.
38 * Creates a new empty %SoupMessage, which will connect to the URL represented
39 * by @context. A reference will be added to @context.
41 * The new message has a status of @SOUP_STATUS_IDLE.
43 * Return value: the new %SoupMessage.
46 soup_message_new (SoupContext *context, const gchar *method)
50 g_return_val_if_fail (context, NULL);
52 ret = g_new0 (SoupMessage, 1);
53 ret->priv = g_new0 (SoupMessagePrivate, 1);
54 ret->status = SOUP_STATUS_IDLE;
55 ret->context = context;
56 ret->method = method ? method : SOUP_METHOD_GET;
58 ret->request_headers = g_hash_table_new (soup_str_case_hash,
61 ret->response_headers = g_hash_table_new (soup_str_case_hash,
64 ret->priv->http_version = SOUP_HTTP_1_1;
66 soup_context_ref (context);
72 * soup_message_new_full:
73 * @context: a %SoupContext for the destination endpoint.
74 * @method: a string which will be used as the HTTP method for the created
75 * request, if NULL a GET request will be made..
76 * @req_owner: the %SoupOwnership of the passed data buffer.
77 * @req_body: a data buffer containing the body of the message request.
78 * @req_length: the byte length of @req_body.
80 * Creates a new %SoupMessage, which will connect to the URL represented by
81 * @context. A reference is added to @context. The request data
82 * buffer will be filled from @req_owner, @req_body, and @req_length
85 * The new message has a status of @SOUP_STATUS_IDLE.
87 * Return value: the new %SoupMessage.
90 soup_message_new_full (SoupContext *context,
92 SoupOwnership req_owner,
96 SoupMessage *ret = soup_message_new (context, method);
98 ret->request.owner = req_owner;
99 ret->request.body = req_body;
100 ret->request.length = req_length;
106 * soup_message_cleanup:
107 * @req: a %SoupMessage.
109 * Frees any temporary resources created in the processing of @req. Also
110 * releases the active connection, if one exists. Request and response data
111 * buffers are left intact.
114 soup_message_cleanup (SoupMessage *req)
116 g_return_if_fail (req != NULL);
118 if (req->priv->read_tag) {
119 soup_transfer_read_cancel (req->priv->read_tag);
120 req->priv->read_tag = 0;
123 if (req->priv->write_tag) {
124 soup_transfer_write_cancel (req->priv->write_tag);
125 req->priv->write_tag = 0;
128 if (req->priv->connect_tag) {
129 soup_context_cancel_connect (req->priv->connect_tag);
130 req->priv->connect_tag = NULL;
132 if (req->connection) {
133 soup_connection_release (req->connection);
134 req->connection = NULL;
137 soup_active_requests = g_slist_remove (soup_active_requests, req);
141 handler_free (SoupHandlerData *data)
143 if (data->filter.type == SOUP_FILTER_HEADER)
144 g_free ((gchar *) data->filter.data.header);
146 if (data->timeout_tag)
147 g_source_remove (data->timeout_tag);
149 data->msg->priv->content_handlers =
150 g_slist_remove (data->msg->priv->content_handlers, data);
157 finalize_message (SoupMessage *req)
159 soup_context_unref (req->context);
161 if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
162 g_free (req->request.body);
163 if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
164 g_free (req->response.body);
166 if (req->priv->req_header)
167 g_string_free (req->priv->req_header, TRUE);
169 soup_message_clear_headers (req->request_headers);
170 g_hash_table_destroy (req->request_headers);
172 soup_message_clear_headers (req->response_headers);
173 g_hash_table_destroy (req->response_headers);
175 while (req->priv->content_handlers) {
176 SoupHandlerData *data = req->priv->content_handlers->data;
180 g_free ((gchar *) req->errorphrase);
187 * @req: a %SoupMessage to destroy.
189 * Destroys the %SoupMessage pointed to by @req. Request and response headers
190 * are freed. Request and response data buffers are also freed if their
191 * ownership is %SOUP_BUFFER_SYSTEM_OWNED. The message's destination context
192 * will be de-referenced.
195 soup_message_free (SoupMessage *req)
197 g_return_if_fail (req != NULL);
199 soup_message_cleanup (req);
201 finalize_message (req);
205 * soup_message_issue_callback:
206 * @req: a %SoupMessage currently being processed.
207 * @error: a %SoupErrorCode to be passed to %req's completion callback.
209 * Finalizes the message request, by first freeing any temporary resources, then
210 * issuing the callback function pointer passed in %soup_message_new or
211 * %soup_message_new_full. If, after returning from the callback, the message
212 * has not been requeued, @msg is destroyed using %soup_message_free.
215 soup_message_issue_callback (SoupMessage *req)
217 g_return_if_fail (req != NULL);
220 * Make sure we don't have some icky recursion if the callback
221 * runs the main loop, and the connection has some data or error
222 * which causes the callback to be run again.
224 soup_message_cleanup (req);
226 if (req->priv->callback) {
227 (*req->priv->callback) (req, req->priv->user_data);
229 if (req->status != SOUP_STATUS_QUEUED)
230 finalize_message (req);
235 * soup_message_cancel:
236 * @req: a %SoupMessage currently being processed.
238 * Cancel a running message, and issue completion callback with a
239 * %SoupTransferStatus of %SOUP_ERROR_CANCELLED. If not requeued by the
240 * completion callback, the @msg will be destroyed.
243 soup_message_cancel (SoupMessage *msg)
245 soup_message_set_error (msg, SOUP_ERROR_CANCELLED);
246 soup_message_issue_callback (msg);
250 foreach_free_header_list (gchar *name, GSList *vals, gpointer notused)
253 g_slist_foreach (vals, (GFunc) g_free, NULL);
260 soup_message_clear_headers (GHashTable *hash)
262 g_return_if_fail (hash != NULL);
264 g_hash_table_foreach_remove (hash,
265 (GHRFunc) foreach_free_header_list,
270 soup_message_remove_header (GHashTable *hash,
276 g_return_if_fail (hash != NULL);
277 g_return_if_fail (name != NULL || name [0] != '\0');
279 if (g_hash_table_lookup_extended (hash,
281 (gpointer *) &stored_key,
282 (gpointer *) &vals)) {
283 g_hash_table_remove (hash, name);
284 foreach_free_header_list (stored_key, vals, NULL);
289 soup_message_add_header (GHashTable *hash,
295 g_return_if_fail (hash != NULL);
296 g_return_if_fail (name != NULL || name [0] != '\0');
297 g_return_if_fail (value != NULL);
299 old_value = g_hash_table_lookup (hash, name);
302 g_slist_append (old_value, g_strdup (value));
304 g_hash_table_insert (hash,
306 g_slist_append (NULL,
311 * soup_message_get_header:
312 * @req: a %SoupMessage.
313 * @name: header name.
315 * Lookup the first transport header with a key equal to @name.
317 * Return value: the header's value or NULL if not found.
320 soup_message_get_header (GHashTable *hash,
325 g_return_val_if_fail (hash != NULL, NULL);
326 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
328 vals = g_hash_table_lookup (hash, name);
336 * soup_message_get_header_list:
337 * @req: a %SoupMessage.
338 * @name: header name.
340 * Lookup the all transport request headers with a key equal to @name.
342 * Return value: a const pointer to a GSList of header values or NULL if not
346 soup_message_get_header_list (GHashTable *hash,
349 g_return_val_if_fail (hash != NULL, NULL);
350 g_return_val_if_fail (name != NULL || name [0] != '\0', NULL);
352 return g_hash_table_lookup (hash, name);
361 foreach_value_in_list (gchar *name, GSList *vals, ForeachData *data)
364 gchar *v = vals->data;
366 (*data->func) (name, v, data->user_data);
373 soup_message_foreach_header (GHashTable *hash,
377 ForeachData data = { func, user_data };
379 g_return_if_fail (hash != NULL);
380 g_return_if_fail (func != NULL);
382 g_hash_table_foreach (hash, (GHFunc) foreach_value_in_list, &data);
391 foreach_remove_value_in_list (gchar *name,
393 ForeachRemoveData *data)
398 gchar *v = iter->data;
399 gboolean ret = FALSE;
401 ret = (*data->func) (name, v, data->user_data);
403 GSList *next = iter->next;
405 vals = g_slist_remove (vals, v);
422 soup_message_foreach_remove_header (GHashTable *hash,
426 ForeachRemoveData data = { func, user_data };
428 g_return_if_fail (hash != NULL);
429 g_return_if_fail (func != NULL);
431 g_hash_table_foreach_remove (hash,
432 (GHRFunc) foreach_remove_value_in_list,
437 * soup_message_queue:
438 * @req: a %SoupMessage.
439 * @callback: a %SoupCallbackFn which will be called after the message completes
440 * or when an unrecoverable error occurs.
441 * @user_data: a pointer passed to @callback.
443 * Queues the message @req for sending. All messages are processed while the
444 * glib main loop runs. If this %SoupMessage has been processed before, any
445 * resources related to the time it was last sent are freed.
447 * If the response %SoupDataBuffer has an owner of %SOUP_BUFFER_USER_OWNED, the
448 * message will not be queued, and @callback will be called with a
449 * %SoupErrorCode of %SOUP_ERROR_CANCELLED.
451 * Upon message completetion, the callback specified in @callback will be
452 * invoked. If after returning from this callback the message has not been
453 * requeued using %soup_message_queue, %soup_message_free will be called on
457 soup_message_queue (SoupMessage *req,
458 SoupCallbackFn callback,
461 soup_queue_message (req, callback, user_data);
466 * @msg: a %SoupMessage.
468 * Syncronously send @msg. This call will not return until the transfer is
469 * finished successfully or there is an unrecoverable error.
471 * @msg is not free'd upon return.
473 * Return value: the %SoupErrorClass of the error encountered while sending or
474 * reading the response.
477 soup_message_send (SoupMessage *msg)
479 soup_message_queue (msg, NULL, NULL);
482 g_main_iteration (TRUE);
483 if (msg->status == SOUP_STATUS_FINISHED ||
484 SOUP_ERROR_IS_TRANSPORT (msg->errorcode))
488 return msg->errorclass;
491 static SoupHandlerResult
492 authorize_handler (SoupMessage *msg, gpointer user_data)
494 gboolean proxy = GPOINTER_TO_INT (user_data);
496 SoupAuth *auth, *old_auth;
500 ctx = proxy ? soup_get_proxy () : msg->context;
501 uri = soup_context_get_uri (ctx);
503 vals = soup_message_get_header_list (msg->response_headers,
505 "Proxy-Authenticate" :
507 if (!vals) goto THROW_CANT_AUTHENTICATE;
509 auth = soup_auth_new_from_header_list (vals);
511 soup_message_set_error_full (
514 SOUP_ERROR_CANT_AUTHENTICATE_PROXY :
515 SOUP_ERROR_CANT_AUTHENTICATE,
517 "Unknown authentication scheme required by "
519 "Unknown authentication scheme required");
520 return SOUP_HANDLER_RESTART;
524 * Call registered authenticate handler
526 if (!uri->user && soup_auth_fn)
527 (*soup_auth_fn) (auth->type,
530 soup_auth_fn_user_data);
533 soup_auth_free (auth);
534 goto THROW_CANT_AUTHENTICATE;
538 * Initialize with auth data (possibly returned from auth callback).
540 soup_auth_initialize (auth, uri);
542 old_auth = soup_auth_lookup (ctx);
544 if (!soup_auth_invalidates_prior (auth, old_auth)) {
545 soup_auth_free (auth);
546 goto THROW_CANT_AUTHENTICATE;
550 soup_auth_set_context (auth, ctx);
552 return SOUP_HANDLER_RESEND;
554 THROW_CANT_AUTHENTICATE:
555 soup_message_set_error (msg,
557 SOUP_ERROR_CANT_AUTHENTICATE_PROXY :
558 SOUP_ERROR_CANT_AUTHENTICATE);
559 return SOUP_HANDLER_RESTART;
562 static SoupHandlerResult
563 redirect_handler (SoupMessage *msg, gpointer user_data)
565 const gchar *new_loc;
567 if (msg->errorclass != SOUP_ERROR_CLASS_REDIRECT ||
568 msg->priv->msg_flags & SOUP_MESSAGE_NO_REDIRECT)
569 return SOUP_HANDLER_CONTINUE;
571 new_loc = soup_message_get_header (msg->response_headers, "Location");
574 const SoupUri *old_uri;
576 SoupContext *new_ctx;
578 old_uri = soup_context_get_uri (msg->context);
580 new_uri = soup_uri_new (new_loc);
582 goto INVALID_REDIRECT;
585 * Copy auth info from original URI.
587 if (old_uri->user && !new_uri->user)
588 soup_uri_set_auth (new_uri,
593 new_ctx = soup_context_from_uri (new_uri);
595 soup_uri_free (new_uri);
598 goto INVALID_REDIRECT;
600 soup_message_set_context (msg, new_ctx);
601 soup_context_unref (new_ctx);
603 return SOUP_HANDLER_RESEND;
606 return SOUP_HANDLER_CONTINUE;
609 soup_message_set_error_full (msg,
610 SOUP_ERROR_MALFORMED,
611 "Invalid Redirect URL");
612 return SOUP_HANDLER_RESTART;
615 static SoupHandlerData global_handlers [] = {
617 * Handle redirect response codes 300, 301, 302, 303, and 305.
620 SOUP_HANDLER_HEADERS,
633 * Handle authorization.
636 SOUP_HANDLER_HEADERS,
640 SOUP_FILTER_ERROR_CODE,
646 GINT_TO_POINTER (FALSE),
649 * Handle proxy authorization.
652 SOUP_HANDLER_HEADERS,
653 "proxy-authenticate",
656 SOUP_FILTER_ERROR_CODE,
662 GINT_TO_POINTER (TRUE),
667 static inline SoupHandlerResult
668 run_handler (SoupMessage *msg,
669 SoupHandlerEvent invoke_type,
670 SoupHandlerEvent when,
671 SoupHandlerData *data)
673 SoupHandlerResult result;
675 if (data->type != invoke_type || data->order != when)
676 return SOUP_HANDLER_CONTINUE;
678 switch (data->filter.type) {
679 case SOUP_FILTER_HEADER:
680 if (!soup_message_get_header (msg->response_headers,
681 data->filter.data.header))
682 return SOUP_HANDLER_CONTINUE;
684 case SOUP_FILTER_ERROR_CODE:
685 if (msg->errorcode != data->filter.data.errorcode)
686 return SOUP_HANDLER_CONTINUE;
688 case SOUP_FILTER_ERROR_CLASS:
689 if (msg->errorclass != data->filter.data.errorclass)
690 return SOUP_HANDLER_CONTINUE;
692 case SOUP_FILTER_TIMEOUT:
693 return SOUP_HANDLER_CONTINUE;
698 result = (*data->handler_cb) (msg, data->user_data);
701 case SOUP_HANDLER_STOP:
702 if (invoke_type == SOUP_HANDLER_FINISHED &&
703 msg->errorclass != SOUP_ERROR_CLASS_INFORMATIONAL)
704 soup_message_issue_callback (msg);
706 case SOUP_HANDLER_KILL:
707 soup_message_issue_callback (msg);
709 case SOUP_HANDLER_RESEND:
710 if (msg->status != SOUP_STATUS_QUEUED)
711 soup_message_queue (msg,
713 msg->priv->user_data);
716 if (msg->status == SOUP_STATUS_QUEUED)
717 result = SOUP_HANDLER_RESEND;
724 #define PROCESS_HANDLER_RESULT(result) ({ \
726 case SOUP_HANDLER_STOP: \
728 case SOUP_HANDLER_KILL: \
729 case SOUP_HANDLER_RESEND: \
731 case SOUP_HANDLER_RESTART: \
739 * Run each handler with matching criteria (first per-message then global
740 * handlers). If a handler requeues a message, we stop processing and terminate
741 * the current request.
743 * After running all handlers, if there is an error set or the invoke type was
744 * post_body, issue the final callback.
746 * FIXME: If the errorcode is changed by a handler, we should restart the
750 soup_message_run_handlers (SoupMessage *msg, SoupHandlerEvent invoke_type)
753 SoupHandlerData *data;
754 SoupHandlerResult result;
756 g_return_val_if_fail (msg != NULL, FALSE);
760 * Pre-Global handlers
762 for (list = msg->priv->content_handlers; list; list = list->next) {
764 result = run_handler (msg,
768 PROCESS_HANDLER_RESULT (result);
774 for (data = global_handlers; data->type; data++) {
775 result = run_handler (msg,
779 PROCESS_HANDLER_RESULT (result);
783 * Post-Global handlers
785 for (list = msg->priv->content_handlers; list; list = list->next) {
787 result = run_handler (msg,
791 PROCESS_HANDLER_RESULT (result);
795 * Issue final callback if the invoke_type is FINISHED and the error
796 * class is not INFORMATIONAL.
798 if (invoke_type == SOUP_HANDLER_FINISHED &&
799 msg->errorclass != SOUP_ERROR_CLASS_INFORMATIONAL) {
800 soup_message_issue_callback (msg);
808 timeout_handler (gpointer user_data)
810 SoupHandlerData *data = user_data;
811 SoupMessage *msg = data->msg;
812 SoupHandlerResult result;
815 switch (data->type) {
816 case SOUP_HANDLER_PREPARE:
817 if (msg->status >= SOUP_STATUS_SENDING_REQUEST)
819 case SOUP_HANDLER_HEADERS:
820 case SOUP_HANDLER_DATA:
821 if (msg->status >= SOUP_STATUS_READING_RESPONSE &&
822 g_hash_table_size (msg->response_headers) > 0)
824 case SOUP_HANDLER_FINISHED:
825 if (msg->status == SOUP_STATUS_FINISHED)
827 case SOUP_HANDLER_DATA_SENT:
828 iter = msg->priv->content_handlers;
830 SoupHandlerData *hd = iter->data;
831 if (!g_strcasecmp (hd->name, "server-message"))
836 result = (*data->handler_cb) (msg, data->user_data);
839 case SOUP_HANDLER_KILL:
840 soup_message_cancel (msg);
842 case SOUP_HANDLER_RESEND:
843 soup_message_queue (msg,
845 msg->priv->user_data);
852 data->timeout_tag = 0;
857 soup_message_add_handler_full (SoupMessage *msg,
859 SoupHandlerEvent type,
860 SoupHandlerWhen order,
861 SoupHandlerFilter *filter,
862 SoupHandlerFn handler_cb,
865 SoupHandlerData *data;
867 g_return_if_fail (msg != NULL);
868 g_return_if_fail (type != 0);
869 g_return_if_fail (order != 0);
870 g_return_if_fail (handler_cb != NULL);
872 data = g_new0 (SoupHandlerData, 1);
874 data->handler_cb = handler_cb;
875 data->user_data = user_data;
876 data->name = g_strdup (name);
881 data->filter.type = filter->type;
883 switch (filter->type) {
884 case SOUP_FILTER_HEADER:
885 data->filter.data.header =
886 g_strdup (filter->data.header);
888 case SOUP_FILTER_ERROR_CODE:
889 data->filter.data.errorcode = filter->data.errorcode;
891 case SOUP_FILTER_ERROR_CLASS:
892 data->filter.data.errorclass = filter->data.errorclass;
894 case SOUP_FILTER_TIMEOUT:
895 data->filter.data.timeout = filter->data.timeout;
897 g_timeout_add (filter->data.timeout * 1000,
906 msg->priv->content_handlers =
907 g_slist_append (msg->priv->content_handlers, data);
911 soup_message_add_handler (SoupMessage *msg,
912 SoupHandlerEvent type,
913 SoupHandlerFilter *filter,
914 SoupHandlerFn handler_cb,
917 g_return_if_fail (msg != NULL);
918 g_return_if_fail (type != 0);
919 g_return_if_fail (handler_cb != NULL);
921 soup_message_add_handler_full (msg,
931 soup_message_list_handlers (SoupMessage *msg)
933 GSList *ret = NULL, *list;
935 g_return_val_if_fail (msg != NULL, NULL);
937 for (list = msg->priv->content_handlers; list; list = list->next) {
938 SoupHandlerData *data = list->data;
940 ret = g_slist_append (ret, data->name);
947 soup_message_remove_handler (SoupMessage *msg,
952 g_return_if_fail (msg != NULL);
953 g_return_if_fail (name != NULL);
955 iter = msg->priv->content_handlers;
957 SoupHandlerData *data = iter->data;
959 if (data->name && !g_strcasecmp (data->name, name)) {
969 soup_message_remove_handler_by_func (SoupMessage *msg,
970 SoupHandlerFn handler_cb)
974 g_return_if_fail (msg != NULL);
975 g_return_if_fail (handler_cb != NULL);
977 iter = msg->priv->content_handlers;
979 SoupHandlerData *data = iter->data;
981 if (data->handler_cb == handler_cb) {
991 soup_message_remove_handler_by_func_and_data (SoupMessage *msg,
992 SoupHandlerFn handler_cb,
997 g_return_if_fail (msg != NULL);
998 g_return_if_fail (handler_cb != NULL);
1000 iter = msg->priv->content_handlers;
1002 SoupHandlerData *data = iter->data;
1004 if (data->handler_cb == handler_cb &&
1005 data->user_data == user_data) {
1006 handler_free (data);
1014 static inline gboolean
1015 ADDED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
1017 return ((newflags & find) && !(msg->priv->msg_flags & find));
1020 static inline gboolean
1021 REMOVED_FLAG (SoupMessage *msg, guint newflags, SoupMessageFlags find)
1023 return (!(newflags & find) && (msg->priv->msg_flags & find));
1027 soup_message_set_flags (SoupMessage *msg, guint flags)
1029 g_return_if_fail (msg != NULL);
1031 msg->priv->msg_flags = flags;
1035 soup_message_get_flags (SoupMessage *msg)
1037 g_return_val_if_fail (msg != NULL, 0);
1039 return msg->priv->msg_flags;
1043 soup_message_set_http_version (SoupMessage *msg, SoupHttpVersion version)
1045 g_return_if_fail (msg != NULL);
1047 msg->priv->http_version = version;
1051 soup_message_get_http_version (SoupMessage *msg)
1053 g_return_val_if_fail (msg != NULL, SOUP_HTTP_1_0);
1055 return msg->priv->http_version;
1059 soup_message_set_context (SoupMessage *msg,
1060 SoupContext *new_ctx)
1062 g_return_if_fail (msg != NULL);
1063 g_return_if_fail (new_ctx != NULL);
1065 soup_context_unref (msg->context);
1066 soup_context_ref (new_ctx);
1068 msg->context = new_ctx;
1072 soup_message_get_context (SoupMessage *msg)
1074 g_return_val_if_fail (msg != NULL, NULL);
1076 soup_context_ref (msg->context);
1077 return msg->context;
1081 soup_message_set_error (SoupMessage *msg, SoupKnownErrorCode errcode)
1083 g_return_if_fail (msg != NULL);
1084 g_return_if_fail (errcode != 0);
1086 g_free ((gchar *) msg->errorphrase);
1088 msg->errorcode = errcode;
1089 msg->errorclass = soup_error_get_class (errcode);
1090 msg->errorphrase = g_strdup (soup_error_get_phrase (errcode));
1094 soup_message_set_error_full (SoupMessage *msg,
1096 const gchar *errphrase)
1098 g_return_if_fail (msg != NULL);
1099 g_return_if_fail (errcode != 0);
1100 g_return_if_fail (errphrase != NULL);
1102 g_free ((gchar *) msg->errorphrase);
1104 msg->errorcode = errcode;
1105 msg->errorclass = soup_error_get_class (errcode);
1106 msg->errorphrase = g_strdup (errphrase);
1110 soup_message_set_handler_error (SoupMessage *msg,
1112 const gchar *errphrase)
1114 g_return_if_fail (msg != NULL);
1115 g_return_if_fail (errcode != 0);
1116 g_return_if_fail (errphrase != NULL);
1118 g_free ((gchar *) msg->errorphrase);
1120 msg->errorcode = errcode;
1121 msg->errorclass = SOUP_ERROR_CLASS_HANDLER;
1122 msg->errorphrase = g_strdup (errphrase);