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