soup-message-client-io: ASCIIfy unicode hostnames for Host header
[libsoup-meego:libsoup-meego.git] / libsoup / soup-message-client-io.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-message-client-io.c: client-side request/response
4  *
5  * Copyright (C) 2000-2003, Ximian, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "soup-connection.h"
16 #include "soup-message-private.h"
17 #include "soup-auth.h"
18 #include "soup-connection.h"
19 #include "soup-headers.h"
20 #include "soup-message-queue.h"
21 #include "soup-uri.h"
22
23 static guint
24 parse_response_headers (SoupMessage *req,
25                         char *headers, guint headers_len,
26                         SoupEncoding *encoding,
27                         gpointer user_data)
28 {
29         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
30         SoupHTTPVersion version;
31
32         g_free(req->reason_phrase);
33         req->reason_phrase = NULL;
34         if (!soup_headers_parse_response (headers, headers_len,
35                                           req->response_headers,
36                                           &version,
37                                           &req->status_code,
38                                           &req->reason_phrase))
39                 return SOUP_STATUS_MALFORMED;
40
41         g_object_notify (G_OBJECT (req), SOUP_MESSAGE_STATUS_CODE);
42         g_object_notify (G_OBJECT (req), SOUP_MESSAGE_REASON_PHRASE);
43
44         if (version < priv->http_version) {
45                 priv->http_version = version;
46                 g_object_notify (G_OBJECT (req), SOUP_MESSAGE_HTTP_VERSION);
47         }
48
49         if ((req->method == SOUP_METHOD_HEAD ||
50              req->status_code  == SOUP_STATUS_NO_CONTENT ||
51              req->status_code  == SOUP_STATUS_NOT_MODIFIED ||
52              SOUP_STATUS_IS_INFORMATIONAL (req->status_code)) ||
53             (req->method == SOUP_METHOD_CONNECT &&
54              SOUP_STATUS_IS_SUCCESSFUL (req->status_code)))
55                 *encoding = SOUP_ENCODING_NONE;
56         else
57                 *encoding = soup_message_headers_get_encoding (req->response_headers);
58
59         if (*encoding == SOUP_ENCODING_UNRECOGNIZED)
60                 return SOUP_STATUS_MALFORMED;
61
62         return SOUP_STATUS_OK;
63 }
64
65 static void
66 get_request_headers (SoupMessage *req, GString *header,
67                      SoupEncoding *encoding, gpointer user_data)
68 {
69         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
70         SoupMessageQueueItem *item = user_data;
71         SoupURI *uri = soup_message_get_uri (req);
72         char *uri_host;
73         char *uri_string;
74         SoupMessageHeadersIter iter;
75         const char *name, *value;
76
77         if (strchr (uri->host, ':'))
78                 uri_host = g_strdup_printf ("[%s]", uri->host);
79         else if (g_hostname_is_non_ascii (uri->host))
80                 uri_host = g_hostname_to_ascii (uri->host);
81         else
82                 uri_host = uri->host;
83
84         if (req->method == SOUP_METHOD_CONNECT) {
85                 /* CONNECT URI is hostname:port for tunnel destination */
86                 uri_string = g_strdup_printf ("%s:%d", uri_host, uri->port);
87         } else {
88                 gboolean proxy = soup_connection_is_via_proxy (item->conn);
89
90                 /* Proxy expects full URI to destination. Otherwise
91                  * just the path.
92                  */
93                 uri_string = soup_uri_to_string (uri, !proxy);
94
95                 if (proxy && uri->fragment) {
96                         /* Strip fragment */
97                         char *fragment = strchr (uri_string, '#');
98                         if (fragment)
99                                 *fragment = '\0';
100                 }
101         }
102
103         if (priv->http_version == SOUP_HTTP_1_0) {
104                 g_string_append_printf (header, "%s %s HTTP/1.0\r\n",
105                                         req->method, uri_string);
106         } else {
107                 g_string_append_printf (header, "%s %s HTTP/1.1\r\n",
108                                         req->method, uri_string);
109                 if (!soup_message_headers_get_one (req->request_headers, "Host")) {
110                         if (soup_uri_uses_default_port (uri)) {
111                                 g_string_append_printf (header, "Host: %s\r\n",
112                                                         uri_host);
113                         } else {
114                                 g_string_append_printf (header, "Host: %s:%d\r\n",
115                                                         uri_host, uri->port);
116                         }
117                 }
118         }
119         g_free (uri_string);
120         if (uri_host != uri->host)
121                 g_free (uri_host);
122
123         *encoding = soup_message_headers_get_encoding (req->request_headers);
124         if ((*encoding == SOUP_ENCODING_CONTENT_LENGTH ||
125              *encoding == SOUP_ENCODING_NONE) &&
126             (req->request_body->length > 0 ||
127              soup_message_headers_get_one (req->request_headers, "Content-Type")) &&
128             !soup_message_headers_get_content_length (req->request_headers)) {
129                 *encoding = SOUP_ENCODING_CONTENT_LENGTH;
130                 soup_message_headers_set_content_length (req->request_headers,
131                                                          req->request_body->length);
132         }
133
134         soup_message_headers_iter_init (&iter, req->request_headers);
135         while (soup_message_headers_iter_next (&iter, &name, &value))
136                 g_string_append_printf (header, "%s: %s\r\n", name, value);
137         g_string_append (header, "\r\n");
138 }
139
140 void
141 soup_message_send_request (SoupMessageQueueItem      *item,
142                            SoupMessageCompletionFn    completion_cb,
143                            gpointer                   user_data)
144 {
145         soup_message_cleanup_response (item->msg);
146         soup_message_io_client (item,
147                                 get_request_headers,
148                                 parse_response_headers,
149                                 item,
150                                 completion_cb, user_data);
151 }