Version 0.0.10
[sipe-libnice.git] / socket / http.c
blob81e18fb7bf41a2e0d79f783c8535dd73ded6efbe
1 /*
2 * This file is part of the Nice GLib ICE library.
4 * (C) 2008 Collabora Ltd.
5 * Contact: Youness Alaoui
6 * (C) 2008 Nokia Corporation. All rights reserved.
8 * The contents of this file are subject to the Mozilla Public License Version
9 * 1.1 (the "License"); you may not use this file except in compliance with
10 * the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
13 * Software distributed under the License is distributed on an "AS IS" basis,
14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
15 * for the specific language governing rights and limitations under the
16 * License.
18 * The Original Code is the Nice GLib ICE library.
20 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
21 * Corporation. All Rights Reserved.
23 * Contributors:
24 * Youness Alaoui, Collabora Ltd.
26 * Alternatively, the contents of this file may be used under the terms of the
27 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
28 * case the provisions of LGPL are applicable instead of those above. If you
29 * wish to allow use of your version of this file only under the terms of the
30 * LGPL and not to allow others to use your version of this file under the
31 * MPL, indicate your decision by deleting the provisions above and replace
32 * them with the notice and other provisions required by the LGPL. If you do
33 * not delete the provisions above, a recipient may use your version of this
34 * file under either the MPL or the LGPL.
38 * Implementation of TCP relay socket interface using TCP Berkeley sockets. (See
39 * http://en.wikipedia.org/wiki/Berkeley_sockets.)
41 #ifdef HAVE_CONFIG_H
42 # include "config.h"
43 #endif
45 #include "http.h"
47 #include <string.h>
48 #include <stdlib.h>
51 #ifndef G_OS_WIN32
52 #include <unistd.h>
53 #endif
56 #define HTTP_USER_AGENT "libnice"
58 typedef enum {
59 HTTP_STATE_INIT,
60 HTTP_STATE_HEADERS,
61 HTTP_STATE_BODY,
62 HTTP_STATE_CONNECTED,
63 HTTP_STATE_ERROR
64 } HttpState;
66 typedef struct {
67 HttpState state;
68 NiceSocket *base_socket;
69 NiceAddress addr;
70 gchar *username;
71 gchar *password;
72 GQueue send_queue;
73 gchar *recv_buf;
74 gint recv_len;
75 gint content_length;
76 } HttpPriv;
79 struct to_be_sent {
80 guint length;
81 gchar *buf;
82 NiceAddress to;
86 static void socket_close (NiceSocket *sock);
87 static gint socket_recv (NiceSocket *sock, NiceAddress *from,
88 guint len, gchar *buf);
89 static gboolean socket_send (NiceSocket *sock, const NiceAddress *to,
90 guint len, const gchar *buf);
91 static gboolean socket_is_reliable (NiceSocket *sock);
93 static void add_to_be_sent (NiceSocket *sock, const NiceAddress *to,
94 const gchar *buf, guint len);
95 static void free_to_be_sent (struct to_be_sent *tbs);
98 NiceSocket *
99 nice_http_socket_new (NiceSocket *base_socket,
100 NiceAddress *addr, gchar *username, gchar *password)
102 HttpPriv *priv;
103 NiceSocket *sock = NULL;
105 if (addr) {
106 sock = g_slice_new0 (NiceSocket);
107 sock->priv = priv = g_slice_new0 (HttpPriv);
109 priv->base_socket = base_socket;
110 priv->addr = *addr;
111 priv->username = g_strdup (username);
112 priv->password = g_strdup (password);
113 priv->recv_buf = NULL;
114 priv->recv_len = 0;
115 priv->content_length = 0;
118 sock->fileno = priv->base_socket->fileno;
119 sock->addr = priv->base_socket->addr;
120 sock->send = socket_send;
121 sock->recv = socket_recv;
122 sock->is_reliable = socket_is_reliable;
123 sock->close = socket_close;
125 /* Send HTTP CONNECT */
127 gchar *msg = NULL;
128 gchar *credential = NULL;
129 gchar host[INET6_ADDRSTRLEN];
130 gint port = nice_address_get_port (&priv->addr);
131 nice_address_to_string (&priv->addr, host);
133 if (username) {
134 gchar * userpass = g_strdup_printf ("%s:%s", username,
135 password ? password : "");
136 gchar * auth = g_base64_encode ((guchar *)userpass, strlen (userpass));
137 credential = g_strdup_printf ("Proxy-Authorization: Basic %s\r\n", auth);
138 g_free (auth);
139 g_free (userpass);
141 msg = g_strdup_printf ("CONNECT %s:%d HTTP/1.0\r\n"
142 "Host: %s\r\n"
143 "User-Agent: %s\r\n"
144 "Content-Length: 0\r\n"
145 "Proxy-Connection: Keep-Alive\r\n"
146 "Connection: Keep-Alive\r\n"
147 "Cache-Control: no-cache\r\n"
148 "Pragma: no-cache\r\n"
149 "%s\r\n", host, port, host, HTTP_USER_AGENT,
150 credential? credential : "" );
151 g_free (credential);
153 nice_socket_send (priv->base_socket, NULL, strlen (msg), msg);
154 priv->state = HTTP_STATE_INIT;
155 g_free (msg);
159 return sock;
163 static void
164 socket_close (NiceSocket *sock)
166 HttpPriv *priv = sock->priv;
168 if (priv->base_socket)
169 nice_socket_free (priv->base_socket);
171 if (priv->username)
172 g_free (priv->username);
174 if (priv->password)
175 g_free (priv->password);
177 if (priv->recv_buf)
178 g_free (priv->recv_buf);
180 g_queue_foreach (&priv->send_queue, (GFunc) free_to_be_sent, NULL);
181 g_queue_clear (&priv->send_queue);
183 g_slice_free(HttpPriv, sock->priv);
187 static gint
188 socket_recv (NiceSocket *sock, NiceAddress *from, guint len, gchar *buf)
190 HttpPriv *priv = sock->priv;
191 gint read = -1;
193 if (priv->base_socket)
194 read = nice_socket_recv (priv->base_socket, from, len, buf);
196 if (read <= 0 || priv->state == HTTP_STATE_CONNECTED) {
197 return read;
198 } else {
199 priv->recv_buf = g_realloc (priv->recv_buf, priv->recv_len + read);
200 memcpy (priv->recv_buf + priv->recv_len, buf, read);
201 priv->recv_len += read;
204 retry:
205 nice_debug ("Receiving from HTTP proxy (state %d) : %d \n'%s'", priv->state, priv->recv_len, priv->recv_buf);
206 switch (priv->state) {
207 case HTTP_STATE_INIT:
209 gint pos = 0;
211 /* Remove any leading spaces (could happen!) */
212 while (pos < priv->recv_len && priv->recv_buf[pos] == ' ')
213 pos++;
215 /* Make sure we have enough data */
216 if (pos >= priv->recv_len)
217 goto not_enough_data;
219 if (pos + 7 > priv->recv_len)
220 goto not_enough_data;
221 if (strncmp (priv->recv_buf + pos, "HTTP/1.", 7) != 0)
222 goto error;
223 pos += 7;
225 if (pos >= priv->recv_len)
226 goto not_enough_data;
227 if(priv->recv_buf[pos] != '0' && priv->recv_buf[pos] != '1')
228 goto error;
229 pos++;
231 /* Make sure we have a space after the HTTP version */
232 if (pos >= priv->recv_len)
233 goto not_enough_data;
234 if (priv->recv_buf[pos] != ' ')
235 goto error;
237 /* Skip all spaces (could be more than one!) */
238 while (pos < priv->recv_len && priv->recv_buf[pos] == ' ')
239 pos++;
240 if (pos >= priv->recv_len)
241 goto not_enough_data;
243 /* Check for a successfull 2xx code */
244 if (pos + 3 > priv->recv_len)
245 goto not_enough_data;
246 if (priv->recv_buf[pos] != '2' ||
247 priv->recv_buf[pos+1] < '0' || priv->recv_buf[pos+1] > '9' ||
248 priv->recv_buf[pos+2] < '0' || priv->recv_buf[pos+2] > '9')
249 goto error;
251 /* Clear any trailing chars */
252 while (pos + 1 < priv->recv_len &&
253 priv->recv_buf[pos] != '\r' && priv->recv_buf[pos+1] != '\n')
254 pos++;
255 if (pos + 1 >= priv->recv_len)
256 goto not_enough_data;
257 pos += 2;
259 /* consume the data we just parsed */
260 priv->recv_len -= pos;
261 memmove (priv->recv_buf, priv->recv_buf + pos, priv->recv_len);
262 priv->recv_buf = g_realloc (priv->recv_buf, priv->recv_len);
264 priv->content_length = 0;
265 priv->state = HTTP_STATE_HEADERS;
266 goto retry;
268 break;
269 case HTTP_STATE_HEADERS:
271 gint pos = 0;
273 if (pos + 15 < priv->recv_len &&
274 g_ascii_strncasecmp (priv->recv_buf, "Content-Length:", 15) == 0) {
275 priv->content_length = atoi(priv->recv_buf + 15);
277 while (pos + 1 < priv->recv_len &&
278 priv->recv_buf[pos] != '\r' && priv->recv_buf[pos+1] != '\n')
279 pos++;
280 nice_debug ("pos = %d, len = %d", pos, priv->recv_len);
281 if (pos + 1 >= priv->recv_len)
282 goto not_enough_data;
283 pos += 2;
285 /* consume the data we just parsed */
286 priv->recv_len -= pos;
287 memmove (priv->recv_buf, priv->recv_buf + pos, priv->recv_len);
288 priv->recv_buf = g_realloc (priv->recv_buf, priv->recv_len);
290 if (pos == 2)
291 priv->state = HTTP_STATE_BODY;
292 goto retry;
294 break;
295 case HTTP_STATE_BODY:
297 gint consumed = priv->content_length;
298 if (priv->content_length == 0) {
299 priv->state = HTTP_STATE_CONNECTED;
300 goto retry;
302 if (priv->recv_len == 0)
303 goto not_enough_data;
305 if (priv->content_length > priv->recv_len)
306 consumed = priv->recv_len;
308 priv->recv_len -= consumed;
309 priv->content_length -= consumed;
310 memmove (priv->recv_buf, priv->recv_buf + consumed, priv->recv_len);
311 priv->recv_buf = g_realloc (priv->recv_buf, priv->recv_len);
312 goto retry;
314 break;
315 case HTTP_STATE_CONNECTED:
317 guint read = priv->recv_len;
318 struct to_be_sent *tbs = NULL;
320 if (read > len)
321 read = len;
323 memcpy (buf, priv->recv_buf, read);
325 /* consume the data we returned */
326 priv->recv_len -= read;
327 memmove (priv->recv_buf, priv->recv_buf + read, priv->recv_len);
328 priv->recv_buf = g_realloc (priv->recv_buf, priv->recv_len);
330 /* Send the pending data */
331 while ((tbs = g_queue_pop_head (&priv->send_queue))) {
332 nice_socket_send (priv->base_socket, &tbs->to,
333 tbs->length, tbs->buf);
334 g_free (tbs->buf);
335 g_slice_free (struct to_be_sent, tbs);
338 return read;
340 break;
341 default:
342 /* Unknown status */
343 goto error;
346 not_enough_data:
347 return 0;
349 error:
350 nice_debug ("http error");
351 if (priv->base_socket)
352 nice_socket_free (priv->base_socket);
353 priv->base_socket = NULL;
354 priv->state = HTTP_STATE_ERROR;
356 return -1;
359 static gboolean
360 socket_send (NiceSocket *sock, const NiceAddress *to,
361 guint len, const gchar *buf)
363 HttpPriv *priv = sock->priv;
365 if (priv->state == HTTP_STATE_CONNECTED) {
366 if (priv->base_socket)
367 return nice_socket_send (priv->base_socket, to, len, buf);
368 else
369 return FALSE;
370 } else if (priv->state == HTTP_STATE_ERROR) {
371 return FALSE;
372 } else {
373 add_to_be_sent (sock, to, buf, len);
375 return TRUE;
379 static gboolean
380 socket_is_reliable (NiceSocket *sock)
382 return TRUE;
386 static void
387 add_to_be_sent (NiceSocket *sock, const NiceAddress *to,
388 const gchar *buf, guint len)
390 HttpPriv *priv = sock->priv;
391 struct to_be_sent *tbs = NULL;
393 if (len <= 0)
394 return;
396 tbs = g_slice_new0 (struct to_be_sent);
397 tbs->buf = g_memdup (buf, len);
398 tbs->length = len;
399 if (to)
400 tbs->to = *to;
401 g_queue_push_tail (&priv->send_queue, tbs);
406 static void
407 free_to_be_sent (struct to_be_sent *tbs)
409 g_free (tbs->buf);
410 g_slice_free (struct to_be_sent, tbs);