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