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.
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"
24 parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
25 SoupEncoding *encoding, gpointer sock)
27 SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
28 char *req_method, *req_path, *url;
29 SoupHTTPVersion version;
34 status = soup_headers_parse_request (headers, headers_len,
39 if (!SOUP_STATUS_IS_SUCCESSFUL (status))
42 g_object_set (G_OBJECT (msg),
43 SOUP_MESSAGE_METHOD, req_method,
44 SOUP_MESSAGE_HTTP_VERSION, version,
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;
54 return SOUP_STATUS_BAD_REQUEST;
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, '/')) {
61 return SOUP_STATUS_BAD_REQUEST;
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",
69 uri = soup_uri_new (url);
71 soup_uri_set_path (uri, "*");
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",
80 uri = soup_uri_new (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);
87 url = g_strdup_printf ("%s://%s:%d%s",
88 soup_socket_is_ssl (sock) ? "https" : "http",
89 host, soup_address_get_port (addr),
91 uri = soup_uri_new (url);
98 return SOUP_STATUS_BAD_REQUEST;
99 soup_message_set_uri (msg, uri);
102 return SOUP_STATUS_OK;
106 handle_partial_get (SoupMessage *msg)
110 SoupBuffer *full_response;
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
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))
126 /* Oh, and there has to have been a valid Range header on the
127 * request, of course.
129 if (!soup_message_headers_get_ranges (msg->request_headers,
130 msg->response_body->length,
134 full_response = soup_message_body_flatten (msg->response_body);
135 if (!full_response) {
136 soup_message_headers_free_ranges (msg->request_headers, ranges);
140 soup_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
141 soup_message_body_truncate (msg->response_body);
144 SoupBuffer *range_buf;
146 /* Single range, so just set Content-Range and fix the body. */
148 soup_message_headers_set_content_range (msg->response_headers,
151 full_response->length);
152 range_buf = soup_buffer_new_subbuffer (full_response,
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);
158 SoupMultipart *multipart;
159 SoupMessageHeaders *part_headers;
160 SoupBuffer *part_body;
161 const char *content_type;
164 /* Multiple ranges, so build a multipart/byteranges response
165 * to replace msg->response_body with.
168 multipart = soup_multipart_new ("multipart/byteranges");
169 content_type = soup_message_headers_get_one (msg->response_headers,
171 for (i = 0; i < nranges; i++) {
172 part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
174 soup_message_headers_append (part_headers,
178 soup_message_headers_set_content_range (part_headers,
181 full_response->length);
182 part_body = soup_buffer_new_subbuffer (full_response,
184 ranges[i].end - ranges[i].start + 1);
185 soup_multipart_append_part (multipart, part_headers,
187 soup_message_headers_free (part_headers);
188 soup_buffer_free (part_body);
191 soup_multipart_to_message (multipart, msg->response_headers,
193 soup_multipart_free (multipart);
196 soup_buffer_free (full_response);
197 soup_message_headers_free_ranges (msg->request_headers, ranges);
201 get_response_headers (SoupMessage *msg, GString *headers,
202 SoupEncoding *encoding, gpointer user_data)
204 SoupEncoding claimed_encoding;
205 SoupMessageHeadersIter iter;
206 const char *name, *value;
208 handle_partial_get (msg);
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);
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;
223 *encoding = claimed_encoding;
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);
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");
238 soup_message_read_request (SoupMessage *msg,
240 SoupMessageCompletionFn completion_cb,
243 soup_message_io_server (msg, sock,
244 get_response_headers,
245 parse_request_headers,
247 completion_cb, user_data);