1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-message-server-io.c: server-side request/response
5 * Copyright (C) 2000-2003, Ximian, Inc.
14 #include <glib/gi18n-lib.h>
17 #include "soup-message-private.h"
18 #include "soup-misc-private.h"
21 parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
22 SoupEncoding *encoding, gpointer sock, GError **error)
24 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
25 char *req_method, *req_path, *url;
26 SoupHTTPVersion version;
31 status = soup_headers_parse_request (headers, headers_len,
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"));
45 g_object_set (G_OBJECT (msg),
46 SOUP_MESSAGE_METHOD, req_method,
47 SOUP_MESSAGE_HTTP_VERSION, version,
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;
57 return SOUP_STATUS_BAD_REQUEST;
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, '/')) {
64 return SOUP_STATUS_BAD_REQUEST;
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",
72 uri = soup_uri_new (url);
74 soup_uri_set_path (uri, "*");
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",
83 uri = soup_uri_new (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);
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);
101 if (!uri || !uri->host) {
104 return SOUP_STATUS_BAD_REQUEST;
107 soup_message_set_uri (msg, uri);
110 return SOUP_STATUS_OK;
114 handle_partial_get (SoupMessage *msg)
118 SoupBuffer *full_response;
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
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))
135 /* Oh, and there has to have been a valid Range header on the
136 * request, of course.
138 status = soup_message_headers_get_ranges_internal (msg->request_headers,
139 msg->response_body->length,
142 if (status == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) {
143 soup_message_set_status (msg, status);
144 soup_message_body_truncate (msg->response_body);
146 } else if (status != SOUP_STATUS_PARTIAL_CONTENT)
149 full_response = soup_message_body_flatten (msg->response_body);
150 if (!full_response) {
151 soup_message_headers_free_ranges (msg->request_headers, ranges);
155 soup_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
156 soup_message_body_truncate (msg->response_body);
159 SoupBuffer *range_buf;
161 /* Single range, so just set Content-Range and fix the body. */
163 soup_message_headers_set_content_range (msg->response_headers,
166 full_response->length);
167 range_buf = soup_buffer_new_subbuffer (full_response,
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);
173 SoupMultipart *multipart;
174 SoupMessageHeaders *part_headers;
175 SoupBuffer *part_body;
176 const char *content_type;
179 /* Multiple ranges, so build a multipart/byteranges response
180 * to replace msg->response_body with.
183 multipart = soup_multipart_new ("multipart/byteranges");
184 content_type = soup_message_headers_get_one (msg->response_headers,
186 for (i = 0; i < nranges; i++) {
187 part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
189 soup_message_headers_append (part_headers,
193 soup_message_headers_set_content_range (part_headers,
196 full_response->length);
197 part_body = soup_buffer_new_subbuffer (full_response,
199 ranges[i].end - ranges[i].start + 1);
200 soup_multipart_append_part (multipart, part_headers,
202 soup_message_headers_free (part_headers);
203 soup_buffer_free (part_body);
206 soup_multipart_to_message (multipart, msg->response_headers,
208 soup_multipart_free (multipart);
211 soup_buffer_free (full_response);
212 soup_message_headers_free_ranges (msg->request_headers, ranges);
216 get_response_headers (SoupMessage *msg, GString *headers,
217 SoupEncoding *encoding, gpointer user_data)
219 SoupEncoding claimed_encoding;
220 SoupMessageHeadersIter iter;
221 const char *name, *value;
223 handle_partial_get (msg);
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);
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;
238 *encoding = claimed_encoding;
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);
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");
253 soup_message_read_request (SoupMessage *msg,
255 SoupMessageCompletionFn completion_cb,
258 GMainContext *async_context;
262 SOUP_SOCKET_ASYNC_CONTEXT, &async_context,
265 async_context = g_main_context_ref (g_main_context_default ());
267 iostream = soup_socket_get_iostream (sock);
269 soup_message_io_server (msg, iostream, async_context,
270 get_response_headers,
271 parse_request_headers,
273 completion_cb, user_data);
275 g_main_context_unref (async_context);