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