Remove build warning
[platform/upstream/libsoup.git] / libsoup / soup-message-server-io.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-message-server-io.c: server-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 <string.h>
13
14 #include <glib/gi18n-lib.h>
15
16 #include "soup.h"
17 #include "soup-message-private.h"
18 #include "soup-misc-private.h"
19
20 static guint
21 parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
22                        SoupEncoding *encoding, gpointer sock, GError **error)
23 {
24         SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
25         char *req_method, *req_path, *url;
26         SoupHTTPVersion version;
27         const char *req_host;
28         guint status;
29         SoupURI *uri;
30
31         status = soup_headers_parse_request (headers, headers_len,
32                                              msg->request_headers,
33                                              &req_method,
34                                              &req_path,
35                                              &version);
36         if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
37                 if (status == SOUP_STATUS_MALFORMED) {
38                         g_set_error_literal (error, SOUP_REQUEST_ERROR,
39                                              SOUP_REQUEST_ERROR_PARSING,
40                                              _("Could not parse HTTP request"));
41                 }
42                 return status;
43         }
44
45         g_object_set (G_OBJECT (msg),
46                       SOUP_MESSAGE_METHOD, req_method,
47                       SOUP_MESSAGE_HTTP_VERSION, version,
48                       NULL);
49         g_free (req_method);
50
51         /* Handle request body encoding */
52         *encoding = soup_message_headers_get_encoding (msg->request_headers);
53         if (*encoding == SOUP_ENCODING_UNRECOGNIZED) {
54                 if (soup_message_headers_get_list (msg->request_headers, "Transfer-Encoding"))
55                         return SOUP_STATUS_NOT_IMPLEMENTED;
56                 else
57                         return SOUP_STATUS_BAD_REQUEST;
58         }
59
60         /* Generate correct context for request */
61         req_host = soup_message_headers_get_one (msg->request_headers, "Host");
62         if (req_host && strchr (req_host, '/')) {
63                 g_free (req_path);
64                 return SOUP_STATUS_BAD_REQUEST;
65         }
66
67         if (!strcmp (req_path, "*") && req_host) {
68                 /* Eg, "OPTIONS * HTTP/1.1" */
69                 url = g_strdup_printf ("%s://%s",
70                                        soup_socket_is_ssl (sock) ? "https" : "http",
71                                        req_host);
72                 uri = soup_uri_new (url);
73                 if (uri)
74                         soup_uri_set_path (uri, "*");
75                 g_free (url);
76         } else if (*req_path != '/') {
77                 /* Must be an absolute URI */
78                 uri = soup_uri_new (req_path);
79         } else if (req_host) {
80                 url = g_strdup_printf ("%s://%s%s",
81                                        soup_socket_is_ssl (sock) ? "https" : "http",
82                                        req_host, req_path);
83                 uri = soup_uri_new (url);
84                 g_free (url);
85         } else if (priv->http_version == SOUP_HTTP_1_0) {
86                 /* No Host header, no AbsoluteUri */
87                 SoupAddress *addr = soup_socket_get_local_address (sock);
88
89                 uri = soup_uri_new (NULL);
90                 soup_uri_set_scheme (uri, soup_socket_is_ssl (sock) ?
91                                      SOUP_URI_SCHEME_HTTPS :
92                                      SOUP_URI_SCHEME_HTTP);
93                 soup_uri_set_host (uri, soup_address_get_physical (addr));
94                 soup_uri_set_port (uri, soup_address_get_port (addr));
95                 soup_uri_set_path (uri, req_path);
96         } else
97                 uri = NULL;
98
99         g_free (req_path);
100
101         if (!uri || !uri->host) {
102                 if (uri)
103                         soup_uri_free (uri);
104                 return SOUP_STATUS_BAD_REQUEST;
105         }
106
107         soup_message_set_uri (msg, uri);
108         soup_uri_free (uri);
109
110         return SOUP_STATUS_OK;
111 }
112
113 static void
114 handle_partial_get (SoupMessage *msg)
115 {
116         SoupRange *ranges;
117         int nranges;
118         SoupBuffer *full_response;
119         guint status;
120
121         /* Make sure the message is set up right for us to return a
122          * partial response; it has to be a GET, the status must be
123          * 200 OK (and in particular, NOT already 206 Partial
124          * Content), and the SoupServer must have already filled in
125          * the response body
126          */
127         if (msg->method != SOUP_METHOD_GET ||
128             msg->status_code != SOUP_STATUS_OK ||
129             soup_message_headers_get_encoding (msg->response_headers) !=
130             SOUP_ENCODING_CONTENT_LENGTH ||
131             msg->response_body->length == 0 ||
132             !soup_message_body_get_accumulate (msg->response_body))
133                 return;
134
135         /* Oh, and there has to have been a valid Range header on the
136          * request, of course.
137          */
138         status = soup_message_headers_get_ranges_internal (msg->request_headers,
139                                                            msg->response_body->length,
140                                                            TRUE,
141                                                            &ranges, &nranges);
142         if (status == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) {
143                 soup_message_set_status (msg, status);
144                 soup_message_body_truncate (msg->response_body);
145                 return;
146         } else if (status != SOUP_STATUS_PARTIAL_CONTENT)
147                 return;
148
149         full_response = soup_message_body_flatten (msg->response_body);
150         if (!full_response) {
151                 soup_message_headers_free_ranges (msg->request_headers, ranges);
152                 return;
153         }
154
155         soup_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
156         soup_message_body_truncate (msg->response_body);
157
158         if (nranges == 1) {
159                 SoupBuffer *range_buf;
160
161                 /* Single range, so just set Content-Range and fix the body. */
162
163                 soup_message_headers_set_content_range (msg->response_headers,
164                                                         ranges[0].start,
165                                                         ranges[0].end,
166                                                         full_response->length);
167                 range_buf = soup_buffer_new_subbuffer (full_response,
168                                                        ranges[0].start,
169                                                        ranges[0].end - ranges[0].start + 1);
170                 soup_message_body_append_buffer (msg->response_body, range_buf);
171                 soup_buffer_free (range_buf);
172         } else {
173                 SoupMultipart *multipart;
174                 SoupMessageHeaders *part_headers;
175                 SoupBuffer *part_body;
176                 const char *content_type;
177                 int i;
178
179                 /* Multiple ranges, so build a multipart/byteranges response
180                  * to replace msg->response_body with.
181                  */
182
183                 multipart = soup_multipart_new ("multipart/byteranges");
184                 content_type = soup_message_headers_get_one (msg->response_headers,
185                                                              "Content-Type");
186                 for (i = 0; i < nranges; i++) {
187                         part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
188                         if (content_type) {
189                                 soup_message_headers_append (part_headers,
190                                                              "Content-Type",
191                                                              content_type);
192                         }
193                         soup_message_headers_set_content_range (part_headers,
194                                                                 ranges[i].start,
195                                                                 ranges[i].end,
196                                                                 full_response->length);
197                         part_body = soup_buffer_new_subbuffer (full_response,
198                                                                ranges[i].start,
199                                                                ranges[i].end - ranges[i].start + 1);
200                         soup_multipart_append_part (multipart, part_headers,
201                                                     part_body);
202                         soup_message_headers_free (part_headers);
203                         soup_buffer_free (part_body);
204                 }
205
206                 soup_multipart_to_message (multipart, msg->response_headers,
207                                            msg->response_body);
208                 soup_multipart_free (multipart);
209         }
210
211         soup_buffer_free (full_response);
212         soup_message_headers_free_ranges (msg->request_headers, ranges);
213 }
214
215 static void
216 get_response_headers (SoupMessage *msg, GString *headers,
217                       SoupEncoding *encoding, gpointer user_data)
218 {
219         SoupEncoding claimed_encoding;
220         SoupMessageHeadersIter iter;
221         const char *name, *value;
222
223         handle_partial_get (msg);
224
225         g_string_append_printf (headers, "HTTP/1.%c %d %s\r\n",
226                                 soup_message_get_http_version (msg) == SOUP_HTTP_1_0 ? '0' : '1',
227                                 msg->status_code, msg->reason_phrase);
228
229         claimed_encoding = soup_message_headers_get_encoding (msg->response_headers);
230         if ((msg->method == SOUP_METHOD_HEAD ||
231              msg->status_code  == SOUP_STATUS_NO_CONTENT ||
232              msg->status_code  == SOUP_STATUS_NOT_MODIFIED ||
233              SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) ||
234             (msg->method == SOUP_METHOD_CONNECT &&
235              SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)))
236                 *encoding = SOUP_ENCODING_NONE;
237         else
238                 *encoding = claimed_encoding;
239
240         if (claimed_encoding == SOUP_ENCODING_CONTENT_LENGTH &&
241             !soup_message_headers_get_content_length (msg->response_headers)) {
242                 soup_message_headers_set_content_length (msg->response_headers,
243                                                          msg->response_body->length);
244         }
245
246         soup_message_headers_iter_init (&iter, msg->response_headers);
247         while (soup_message_headers_iter_next (&iter, &name, &value))
248                 g_string_append_printf (headers, "%s: %s\r\n", name, value);
249         g_string_append (headers, "\r\n");
250 }
251
252 void
253 soup_message_read_request (SoupMessage               *msg,
254                            SoupSocket                *sock,
255                            SoupMessageCompletionFn    completion_cb,
256                            gpointer                   user_data)
257 {
258         GMainContext *async_context;
259         GIOStream *iostream;
260
261         g_object_get (sock,
262                       SOUP_SOCKET_ASYNC_CONTEXT, &async_context,
263                       NULL);
264         if (!async_context)
265                 async_context = g_main_context_ref (g_main_context_default ());
266
267         iostream = soup_socket_get_iostream (sock);
268
269         soup_message_io_server (msg, iostream, async_context,
270                                 get_response_headers,
271                                 parse_request_headers,
272                                 sock,
273                                 completion_cb, user_data);
274         if (async_context)
275                 g_main_context_unref (async_context);
276 }