
One use case for this is adding an ALPN header which is a MUST requirement when a HTTP proxy is used in WebRTC (see RFC8835, section 3.4).
667 lines
20 KiB
C
667 lines
20 KiB
C
/*
|
||
* This file is part of the Nice GLib ICE library.
|
||
*
|
||
* (C) 2008 Collabora Ltd.
|
||
* Contact: Youness Alaoui
|
||
* (C) 2008 Nokia Corporation. All rights reserved.
|
||
*
|
||
* The contents of this file are subject to the Mozilla Public License Version
|
||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
* the License. You may obtain a copy of the License at
|
||
* http://www.mozilla.org/MPL/
|
||
*
|
||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
* for the specific language governing rights and limitations under the
|
||
* License.
|
||
*
|
||
* The Original Code is the Nice GLib ICE library.
|
||
*
|
||
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
|
||
* Corporation. All Rights Reserved.
|
||
*
|
||
* Contributors:
|
||
* Youness Alaoui, Collabora Ltd.
|
||
*
|
||
* Alternatively, the contents of this file may be used under the terms of the
|
||
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
|
||
* case the provisions of LGPL are applicable instead of those above. If you
|
||
* wish to allow use of your version of this file only under the terms of the
|
||
* LGPL and not to allow others to use your version of this file under the
|
||
* MPL, indicate your decision by deleting the provisions above and replace
|
||
* them with the notice and other provisions required by the LGPL. If you do
|
||
* not delete the provisions above, a recipient may use your version of this
|
||
* file under either the MPL or the LGPL.
|
||
*/
|
||
|
||
/*
|
||
* Implementation of TCP relay socket interface using TCP Berkeley sockets. (See
|
||
* http://en.wikipedia.org/wiki/Berkeley_sockets.)
|
||
*/
|
||
#ifdef HAVE_CONFIG_H
|
||
# include "config.h"
|
||
#endif
|
||
|
||
#include "http.h"
|
||
#include "agent-priv.h"
|
||
#include "socket-priv.h"
|
||
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
|
||
|
||
#ifndef G_OS_WIN32
|
||
#include <unistd.h>
|
||
#endif
|
||
|
||
|
||
#define HTTP_USER_AGENT "libnice"
|
||
|
||
typedef enum {
|
||
HTTP_STATE_INIT,
|
||
HTTP_STATE_HEADERS,
|
||
HTTP_STATE_BODY,
|
||
HTTP_STATE_CONNECTED,
|
||
HTTP_STATE_ERROR
|
||
} HttpState;
|
||
|
||
typedef struct {
|
||
HttpState state;
|
||
NiceSocket *base_socket;
|
||
NiceAddress addr;
|
||
gchar *username;
|
||
gchar *password;
|
||
GQueue send_queue;
|
||
|
||
/* Ring buffer for receiving HTTP headers into before they’re parsed. */
|
||
guint8 *recv_buf;
|
||
gsize recv_buf_length; /* allocation size of @recv_buf */
|
||
gsize recv_buf_pos; /* offset from @recv_buf of the 0th byte in the buffer */
|
||
gsize recv_buf_fill; /* number of bytes occupied in the buffer */
|
||
|
||
/* Parsed from the Content-Length header provided by the other endpoint. */
|
||
gsize content_length;
|
||
} HttpPriv;
|
||
|
||
|
||
static void socket_close (NiceSocket *sock);
|
||
static gint socket_recv_messages (NiceSocket *sock,
|
||
NiceInputMessage *recv_messages, guint n_recv_messages);
|
||
static gint socket_send_messages (NiceSocket *sock, const NiceAddress *to,
|
||
const NiceOutputMessage *messages, guint n_messages);
|
||
static gint socket_send_messages_reliable (NiceSocket *sock,
|
||
const NiceAddress *to, const NiceOutputMessage *messages, guint n_messages);
|
||
static gboolean socket_is_reliable (NiceSocket *sock);
|
||
static gboolean socket_can_send (NiceSocket *sock, NiceAddress *addr);
|
||
static void socket_set_writable_callback (NiceSocket *sock,
|
||
NiceSocketWritableCb callback, gpointer user_data);
|
||
static gboolean socket_is_based_on (NiceSocket *sock, NiceSocket *other);
|
||
|
||
static void
|
||
_append_extra_header (gpointer key, gpointer value, gpointer user_data)
|
||
{
|
||
GString *str = user_data;
|
||
g_string_append_printf (str, "%s: %s\r\n", (gchar *)key, (gchar *)value);
|
||
}
|
||
|
||
NiceSocket *
|
||
nice_http_socket_new (NiceSocket *base_socket,
|
||
NiceAddress *addr, gchar *username, gchar *password,
|
||
GHashTable *extra_headers)
|
||
{
|
||
HttpPriv *priv;
|
||
NiceSocket *sock = NULL;
|
||
|
||
if (addr) {
|
||
sock = g_slice_new0 (NiceSocket);
|
||
sock->priv = priv = g_slice_new0 (HttpPriv);
|
||
|
||
priv->base_socket = base_socket;
|
||
priv->addr = *addr;
|
||
priv->username = g_strdup (username);
|
||
priv->password = g_strdup (password);
|
||
priv->recv_buf = NULL;
|
||
priv->recv_buf_length = 0;
|
||
priv->recv_buf_pos = 0;
|
||
priv->recv_buf_fill = 0;
|
||
priv->content_length = 0;
|
||
|
||
sock->type = NICE_SOCKET_TYPE_HTTP;
|
||
sock->fileno = priv->base_socket->fileno;
|
||
sock->addr = priv->base_socket->addr;
|
||
sock->send_messages = socket_send_messages;
|
||
sock->send_messages_reliable = socket_send_messages_reliable;
|
||
sock->recv_messages = socket_recv_messages;
|
||
sock->is_reliable = socket_is_reliable;
|
||
sock->can_send = socket_can_send;
|
||
sock->set_writable_callback = socket_set_writable_callback;
|
||
sock->is_based_on = socket_is_based_on;
|
||
sock->close = socket_close;
|
||
|
||
/* Send HTTP CONNECT */
|
||
{
|
||
gchar *msg = NULL;
|
||
GString *str = NULL;
|
||
gchar host[INET6_ADDRSTRLEN];
|
||
gint port = nice_address_get_port (&priv->addr);
|
||
GOutputVector local_bufs;
|
||
NiceOutputMessage local_messages;
|
||
|
||
nice_address_to_string (&priv->addr, host);
|
||
|
||
str = g_string_new (NULL);
|
||
g_string_printf (str, "CONNECT %s:%d HTTP/1.0\r\n"
|
||
"Host: %s\r\n"
|
||
"User-Agent: %s\r\n"
|
||
"Content-Length: 0\r\n"
|
||
"Proxy-Connection: Keep-Alive\r\n"
|
||
"Connection: Keep-Alive\r\n"
|
||
"Cache-Control: no-cache\r\n"
|
||
"Pragma: no-cache\r\n",
|
||
host, port, host, HTTP_USER_AGENT);
|
||
|
||
if (extra_headers) {
|
||
g_hash_table_foreach (extra_headers, _append_extra_header, str);
|
||
}
|
||
|
||
if (username) {
|
||
gchar * userpass = g_strdup_printf ("%s:%s", username,
|
||
password ? password : "");
|
||
gchar * auth = g_base64_encode ((guchar *)userpass, strlen (userpass));
|
||
g_string_append_printf (str, "Proxy-Authorization: Basic %s\r\n", auth);
|
||
g_free (auth);
|
||
g_free (userpass);
|
||
}
|
||
g_string_append_printf (str, "\r\n");
|
||
msg = g_string_free (str, FALSE);
|
||
|
||
local_bufs.buffer = msg;
|
||
local_bufs.size = strlen (msg);
|
||
local_messages.buffers = &local_bufs;
|
||
local_messages.n_buffers = 1;
|
||
|
||
nice_socket_send_messages_reliable (priv->base_socket, NULL,
|
||
&local_messages, 1);
|
||
priv->state = HTTP_STATE_INIT;
|
||
g_free (msg);
|
||
}
|
||
}
|
||
|
||
return sock;
|
||
}
|
||
|
||
|
||
static void
|
||
socket_close (NiceSocket *sock)
|
||
{
|
||
HttpPriv *priv = sock->priv;
|
||
|
||
if (priv->base_socket)
|
||
nice_socket_free (priv->base_socket);
|
||
|
||
if (priv->username)
|
||
g_free (priv->username);
|
||
|
||
if (priv->password)
|
||
g_free (priv->password);
|
||
|
||
if (priv->recv_buf)
|
||
g_free (priv->recv_buf);
|
||
|
||
nice_socket_free_send_queue (&priv->send_queue);
|
||
|
||
g_slice_free(HttpPriv, sock->priv);
|
||
sock->priv = NULL;
|
||
}
|
||
|
||
static void
|
||
assert_ring_buffer_valid (HttpPriv *priv)
|
||
{
|
||
g_assert (priv->recv_buf_fill <= priv->recv_buf_length);
|
||
g_assert (priv->recv_buf_pos == 0 ||
|
||
priv->recv_buf_pos < priv->recv_buf_length);
|
||
g_assert (priv->recv_buf_length == 0 || priv->recv_buf != NULL);
|
||
}
|
||
|
||
/* Pops up to @buffer_length bytes off the ring buffer and copies them into
|
||
* @buffer. Returns the number of bytes copied. */
|
||
static gsize
|
||
memcpy_ring_buffer_to_buffer (HttpPriv *priv,
|
||
guint8 *buffer, gsize buffer_length)
|
||
{
|
||
gsize len, consumed = 0;
|
||
gboolean has_wrapped;
|
||
|
||
has_wrapped =
|
||
(priv->recv_buf_pos + priv->recv_buf_fill) > priv->recv_buf_length;
|
||
|
||
if (has_wrapped) {
|
||
len = MIN (priv->recv_buf_length - priv->recv_buf_pos, buffer_length);
|
||
memcpy (buffer, priv->recv_buf + priv->recv_buf_pos, len);
|
||
consumed += len;
|
||
|
||
buffer += len;
|
||
buffer_length -= len;
|
||
|
||
len = MIN (priv->recv_buf_fill - len, buffer_length);
|
||
memcpy (buffer, priv->recv_buf, len);
|
||
consumed += len;
|
||
} else {
|
||
len = MIN (priv->recv_buf_fill, buffer_length);
|
||
memcpy (buffer, priv->recv_buf + priv->recv_buf_pos, len);
|
||
consumed += len;
|
||
}
|
||
|
||
priv->recv_buf_pos =
|
||
(priv->recv_buf_pos + consumed) % priv->recv_buf_length;
|
||
priv->recv_buf_fill -= consumed;
|
||
|
||
return consumed;
|
||
}
|
||
|
||
/* Returns the number of messages touched. Silently drops any data from @buffer
|
||
* which doesn’t fit in @messages. Updates the ring buffer to pop the copied
|
||
* data off it. Treats all #GInputVectors in @messages the same; there is no
|
||
* differentiation between different #NiceInputMessages. */
|
||
static gint
|
||
memcpy_ring_buffer_to_input_messages (HttpPriv *priv,
|
||
NiceInputMessage *messages, guint n_messages)
|
||
{
|
||
guint i, j;
|
||
|
||
for (i = 0; priv->recv_buf_fill > 0 && i < n_messages; i++) {
|
||
NiceInputMessage *message = &messages[i];
|
||
|
||
for (j = 0;
|
||
priv->recv_buf_fill > 0 &&
|
||
((message->n_buffers >= 0 && j < (guint) message->n_buffers) ||
|
||
(message->n_buffers < 0 && message->buffers[j].buffer != NULL));
|
||
j++) {
|
||
message->buffers[j].size =
|
||
memcpy_ring_buffer_to_buffer (priv,
|
||
message->buffers[j].buffer, message->buffers[j].size);
|
||
}
|
||
}
|
||
|
||
return i;
|
||
}
|
||
|
||
/* FIXME: The current implementation of socket_recv_message() is a fast
|
||
* pass-through to nice_socket_recv_message() if the HTTP socket is connected,
|
||
* but is a slow state machine otherwise, using multiple memcpy()s. Spruce it up
|
||
* to better to use the recv_messages to avoid the memcpy()s. */
|
||
static gint
|
||
socket_recv_messages (NiceSocket *sock,
|
||
NiceInputMessage *recv_messages, guint n_recv_messages)
|
||
{
|
||
HttpPriv *priv = sock->priv;
|
||
gint ret = -1;
|
||
|
||
/* Make sure socket has not been freed: */
|
||
g_assert (sock->priv != NULL);
|
||
|
||
if (priv->state == HTTP_STATE_CONNECTED) {
|
||
guint i;
|
||
|
||
/* Fast path: pass through to the base socket once we’re connected. */
|
||
if (priv->base_socket) {
|
||
ret = nice_socket_recv_messages (priv->base_socket,
|
||
recv_messages, n_recv_messages);
|
||
}
|
||
|
||
if (ret <= 0)
|
||
return ret;
|
||
|
||
/* After successfully receiving into at least one NiceInputMessage,
|
||
* update the from address in each valid NiceInputMessage. */
|
||
for (i = 0; i < (guint) ret; i++) {
|
||
if (recv_messages[i].from != NULL)
|
||
*recv_messages[i].from = priv->addr;
|
||
}
|
||
|
||
return ret;
|
||
} else {
|
||
/* Slow path: read into a local ring buffer until we’re parsed enough of the
|
||
* headers. Double the buffer in size every time it fills up. */
|
||
gboolean has_wrapped;
|
||
GInputVector local_recv_bufs[2];
|
||
NiceInputMessage local_recv_message = { local_recv_bufs, 2, NULL, 0 };
|
||
|
||
/* Has the buffer filled up? Start with an initial buffer of 1KB, which
|
||
* should cover the average size of HTTP response headers. Source:
|
||
* http://dev.chromium.org/spdy/spdy-whitepaper */
|
||
if (priv->recv_buf_fill == priv->recv_buf_length) {
|
||
priv->recv_buf_length = MAX (priv->recv_buf_length * 2, 1024);
|
||
priv->recv_buf = g_realloc (priv->recv_buf, priv->recv_buf_length);
|
||
}
|
||
|
||
assert_ring_buffer_valid (priv);
|
||
|
||
/* Read some data into the buffer. Use two GInputVectors: one for the tail
|
||
* of the buffer and one for the head. */
|
||
has_wrapped =
|
||
(priv->recv_buf_pos + priv->recv_buf_fill) > priv->recv_buf_length;
|
||
|
||
if (has_wrapped) {
|
||
local_recv_bufs[0].buffer =
|
||
priv->recv_buf + (priv->recv_buf_pos + priv->recv_buf_fill) %
|
||
priv->recv_buf_length;
|
||
local_recv_bufs[0].size = priv->recv_buf_length - priv->recv_buf_fill;
|
||
local_recv_bufs[1].buffer = NULL;
|
||
local_recv_bufs[1].size = 0;
|
||
} else {
|
||
local_recv_bufs[0].buffer =
|
||
priv->recv_buf + priv->recv_buf_pos + priv->recv_buf_fill;
|
||
local_recv_bufs[0].size =
|
||
priv->recv_buf_length - (priv->recv_buf_pos + priv->recv_buf_fill);
|
||
local_recv_bufs[1].buffer = priv->recv_buf;
|
||
local_recv_bufs[1].size = priv->recv_buf_pos;
|
||
}
|
||
|
||
if (priv->base_socket) {
|
||
ret = nice_socket_recv_messages (priv->base_socket,
|
||
&local_recv_message, 1);
|
||
}
|
||
|
||
if (ret <= 0)
|
||
return ret;
|
||
|
||
/* Update the buffer’s metadata. */
|
||
priv->recv_buf_fill += local_recv_message.length;
|
||
assert_ring_buffer_valid (priv);
|
||
|
||
/* Fall through and try parsing the newly received data. */
|
||
}
|
||
|
||
#define GET_BYTE(pos) \
|
||
priv->recv_buf[(pos + priv->recv_buf_pos) % priv->recv_buf_length]
|
||
#define EAT_WHITESPACE(pos) \
|
||
while (pos < priv->recv_buf_fill && GET_BYTE(pos) == ' ') \
|
||
pos++; \
|
||
if (pos >= priv->recv_buf_fill) \
|
||
goto not_enough_data;
|
||
|
||
retry:
|
||
nice_debug ("Receiving from HTTP proxy (state %d) : %" G_GSSIZE_FORMAT " \n"
|
||
"'%s'", priv->state, priv->recv_buf_fill,
|
||
priv->recv_buf + priv->recv_buf_pos);
|
||
|
||
switch (priv->state) {
|
||
case HTTP_STATE_INIT:
|
||
{
|
||
/* This is a logical position in the recv_buf; add
|
||
* (priv->recv_buf + priv->recv_buf_pos) to get the actual byte in
|
||
* memory. */
|
||
guint pos = 0;
|
||
|
||
/* Eat leading whitespace and check we have enough data. */
|
||
EAT_WHITESPACE (pos);
|
||
|
||
if (pos + 7 > priv->recv_buf_fill)
|
||
goto not_enough_data;
|
||
if (GET_BYTE (pos + 0) != 'H' ||
|
||
GET_BYTE (pos + 1) != 'T' ||
|
||
GET_BYTE (pos + 2) != 'T' ||
|
||
GET_BYTE (pos + 3) != 'P' ||
|
||
GET_BYTE (pos + 4) != '/' ||
|
||
GET_BYTE (pos + 5) != '1' ||
|
||
GET_BYTE (pos + 6) != '.')
|
||
goto error;
|
||
pos += 7;
|
||
|
||
if (pos >= priv->recv_buf_fill)
|
||
goto not_enough_data;
|
||
if (GET_BYTE (pos) != '0' && GET_BYTE (pos) != '1')
|
||
goto error;
|
||
pos++;
|
||
|
||
/* Make sure we have a space after the HTTP version */
|
||
if (pos >= priv->recv_buf_fill)
|
||
goto not_enough_data;
|
||
if (GET_BYTE (pos) != ' ')
|
||
goto error;
|
||
|
||
EAT_WHITESPACE (pos);
|
||
|
||
/* Check for a successful 2xx code */
|
||
if (pos + 3 > priv->recv_buf_fill)
|
||
goto not_enough_data;
|
||
if (GET_BYTE (pos) != '2' ||
|
||
GET_BYTE (pos + 1) < '0' || GET_BYTE (pos + 1) > '9' ||
|
||
GET_BYTE (pos + 2) < '0' || GET_BYTE (pos + 2) > '9')
|
||
goto error;
|
||
|
||
/* Clear any trailing chars */
|
||
while (pos + 1 < priv->recv_buf_fill &&
|
||
GET_BYTE (pos) != '\r' && GET_BYTE (pos + 1) != '\n')
|
||
pos++;
|
||
if (pos + 1 >= priv->recv_buf_fill)
|
||
goto not_enough_data;
|
||
pos += 2;
|
||
|
||
/* Consume the data we just parsed. */
|
||
priv->recv_buf_pos = (priv->recv_buf_pos + pos) % priv->recv_buf_length;
|
||
priv->recv_buf_fill -= pos;
|
||
|
||
priv->content_length = 0;
|
||
priv->state = HTTP_STATE_HEADERS;
|
||
|
||
goto retry;
|
||
}
|
||
break;
|
||
case HTTP_STATE_HEADERS:
|
||
{
|
||
guint pos = 0;
|
||
|
||
if (pos + 15 < priv->recv_buf_fill &&
|
||
(GET_BYTE (pos + 0) == 'C' || GET_BYTE (pos + 0) == 'c') &&
|
||
(GET_BYTE (pos + 1) == 'o' || GET_BYTE (pos + 1) == 'O') &&
|
||
(GET_BYTE (pos + 2) == 'n' || GET_BYTE (pos + 2) == 'N') &&
|
||
(GET_BYTE (pos + 3) == 't' || GET_BYTE (pos + 3) == 'T') &&
|
||
(GET_BYTE (pos + 4) == 'e' || GET_BYTE (pos + 4) == 'E') &&
|
||
(GET_BYTE (pos + 5) == 'n' || GET_BYTE (pos + 5) == 'N') &&
|
||
(GET_BYTE (pos + 6) == 't' || GET_BYTE (pos + 6) == 'T') &&
|
||
GET_BYTE (pos + 7) == '-' &&
|
||
(GET_BYTE (pos + 8) == 'L' || GET_BYTE (pos + 8) == 'l') &&
|
||
(GET_BYTE (pos + 9) == 'e' || GET_BYTE (pos + 9) == 'E') &&
|
||
(GET_BYTE (pos + 10) == 'n' || GET_BYTE (pos + 10) == 'N') &&
|
||
(GET_BYTE (pos + 11) == 'g' || GET_BYTE (pos + 11) == 'G') &&
|
||
(GET_BYTE (pos + 12) == 't' || GET_BYTE (pos + 12) == 'T') &&
|
||
(GET_BYTE (pos + 13) == 'h' || GET_BYTE (pos + 13) == 'H') &&
|
||
GET_BYTE (pos + 14) == ':') {
|
||
/* Found a Content-Length header. Parse and store the value. Note that
|
||
* the HTTP standard allows for arbitrarily-big content lengths. We
|
||
* limit it to G_MAXSIZE for sanity’s sake.
|
||
*
|
||
* The code below is equivalent to strtoul(input, NULL, 10), but
|
||
* operates on a ring buffer. */
|
||
pos += 15;
|
||
EAT_WHITESPACE (pos);
|
||
priv->content_length = 0;
|
||
|
||
while (TRUE) {
|
||
guint8 byte = GET_BYTE (pos);
|
||
gint val = g_ascii_digit_value (byte);
|
||
|
||
if (byte == '\r') {
|
||
/* Reached the end of the value; fall out to the code below which
|
||
* will grab the \n. */
|
||
break;
|
||
} else if (val == -1) {
|
||
priv->content_length = 0;
|
||
goto error;
|
||
}
|
||
|
||
/* Check for overflow. Don’t flag it as an error; just fall through
|
||
* to the code below which will skip to the \r\n. */
|
||
if (priv->content_length > G_MAXSIZE / 10 ||
|
||
priv->content_length * 10 > G_MAXSIZE - val) {
|
||
priv->content_length = 0;
|
||
break;
|
||
}
|
||
|
||
priv->content_length = (priv->content_length * 10) + val;
|
||
|
||
if (pos + 1 > priv->recv_buf_fill)
|
||
goto not_enough_data;
|
||
pos++;
|
||
}
|
||
}
|
||
|
||
/* Skip over the header. */
|
||
while (pos + 1 < priv->recv_buf_fill &&
|
||
GET_BYTE (pos) != '\r' && GET_BYTE (pos + 1) != '\n')
|
||
pos++;
|
||
|
||
nice_debug ("pos = %u, fill = %" G_GSSIZE_FORMAT,
|
||
pos, priv->recv_buf_fill);
|
||
|
||
if (pos + 1 >= priv->recv_buf_fill)
|
||
goto not_enough_data;
|
||
pos += 2;
|
||
|
||
/* Consume the data we just parsed. */
|
||
priv->recv_buf_pos = (priv->recv_buf_pos + pos) % priv->recv_buf_length;
|
||
priv->recv_buf_fill -= pos;
|
||
|
||
if (pos == 2)
|
||
priv->state = HTTP_STATE_BODY;
|
||
|
||
goto retry;
|
||
}
|
||
break;
|
||
case HTTP_STATE_BODY:
|
||
{
|
||
gsize consumed;
|
||
|
||
if (priv->content_length == 0) {
|
||
priv->state = HTTP_STATE_CONNECTED;
|
||
goto retry;
|
||
}
|
||
|
||
if (priv->recv_buf_fill == 0)
|
||
goto not_enough_data;
|
||
|
||
consumed = MIN (priv->content_length, priv->recv_buf_fill);
|
||
|
||
priv->recv_buf_pos =
|
||
(priv->recv_buf_pos + consumed) % priv->recv_buf_length;
|
||
priv->recv_buf_fill -= consumed;
|
||
priv->content_length -= consumed;
|
||
|
||
goto retry;
|
||
}
|
||
break;
|
||
case HTTP_STATE_CONNECTED:
|
||
{
|
||
gsize len;
|
||
|
||
len = memcpy_ring_buffer_to_input_messages (priv,
|
||
recv_messages, n_recv_messages);
|
||
|
||
/* Send the pending data */
|
||
nice_socket_flush_send_queue (priv->base_socket,
|
||
&priv->send_queue);
|
||
|
||
return len;
|
||
}
|
||
break;
|
||
case HTTP_STATE_ERROR:
|
||
default:
|
||
/* Unknown status */
|
||
goto error;
|
||
}
|
||
|
||
not_enough_data:
|
||
return 0;
|
||
|
||
error:
|
||
nice_debug ("http error");
|
||
if (priv->base_socket)
|
||
nice_socket_free (priv->base_socket);
|
||
priv->base_socket = NULL;
|
||
priv->state = HTTP_STATE_ERROR;
|
||
|
||
return -1;
|
||
}
|
||
|
||
static gint
|
||
socket_send_messages (NiceSocket *sock, const NiceAddress *to,
|
||
const NiceOutputMessage *messages, guint n_messages)
|
||
{
|
||
HttpPriv *priv = sock->priv;
|
||
|
||
/* Make sure socket has not been freed: */
|
||
g_assert (sock->priv != NULL);
|
||
|
||
if (priv->state == HTTP_STATE_CONNECTED) {
|
||
/* Fast path. */
|
||
if (!priv->base_socket)
|
||
return -1;
|
||
|
||
return nice_socket_send_messages (priv->base_socket, to, messages,
|
||
n_messages);
|
||
} else if (priv->state == HTTP_STATE_ERROR) {
|
||
return -1;
|
||
} else {
|
||
return 0;
|
||
}
|
||
|
||
return n_messages;
|
||
}
|
||
|
||
static gint
|
||
socket_send_messages_reliable (NiceSocket *sock, const NiceAddress *to,
|
||
const NiceOutputMessage *messages, guint n_messages)
|
||
{
|
||
HttpPriv *priv = sock->priv;
|
||
|
||
if (priv->state == HTTP_STATE_CONNECTED) {
|
||
/* Fast path. */
|
||
if (!priv->base_socket)
|
||
return -1;
|
||
|
||
return nice_socket_send_messages_reliable (priv->base_socket, to, messages,
|
||
n_messages);
|
||
} else if (priv->state == HTTP_STATE_ERROR) {
|
||
return -1;
|
||
} else {
|
||
nice_socket_queue_send (&priv->send_queue, to, messages, n_messages);
|
||
}
|
||
|
||
return n_messages;
|
||
}
|
||
|
||
static gboolean
|
||
socket_is_reliable (NiceSocket *sock)
|
||
{
|
||
HttpPriv *priv = sock->priv;
|
||
|
||
return nice_socket_is_reliable (priv->base_socket);
|
||
}
|
||
|
||
static gboolean
|
||
socket_can_send (NiceSocket *sock, NiceAddress *addr)
|
||
{
|
||
HttpPriv *priv = sock->priv;
|
||
|
||
return nice_socket_can_send (priv->base_socket, addr);
|
||
}
|
||
|
||
static void
|
||
socket_set_writable_callback (NiceSocket *sock,
|
||
NiceSocketWritableCb callback, gpointer user_data)
|
||
{
|
||
HttpPriv *priv = sock->priv;
|
||
|
||
nice_socket_set_writable_callback (priv->base_socket, callback, user_data);
|
||
}
|
||
|
||
static gboolean
|
||
socket_is_based_on (NiceSocket *sock, NiceSocket *other)
|
||
{
|
||
HttpPriv *priv = sock->priv;
|
||
|
||
return (sock == other) ||
|
||
(priv && nice_socket_is_based_on (priv->base_socket, other));
|
||
}
|