Commit a13ac437 authored by Ole André Vadla Ravnås's avatar Ole André Vadla Ravnås Committed by Olivier Crête

agent: Add support for bytestream TCP

parent 7bd11cf0
......@@ -178,10 +178,10 @@ struct _NiceAgent
#endif
gchar *software_attribute; /* SOFTWARE attribute */
gboolean reliable; /* property: reliable */
gboolean bytestream_tcp; /* property: bytestream-tcp */
gboolean keepalive_conncheck; /* property: keepalive_conncheck */
GQueue pending_signals;
guint16 rfc4571_expecting_length;
gboolean use_ice_udp;
gboolean use_ice_tcp;
gboolean use_ice_trickle;
......
This diff is collapsed.
......@@ -409,6 +409,7 @@ typedef enum
* @NICE_AGENT_OPTION_SUPPORT_RENOMINATION: Enable renomination triggered by NOMINATION STUN attribute
* proposed here: https://tools.ietf.org/html/draft-thatcher-ice-renomination-00
* @NICE_AGENT_OPTION_CONSENT_FRESHNESS: Enable RFC 7675 consent freshness support. (Since: 0.1.19)
* @NICE_AGENT_OPTION_BYTESTREAM_TCP: Use bytestream mode for reliable TCP connections. (Since: 0.1.20)
*
* These are options that can be passed to nice_agent_new_full(). They set
* various properties on the agent. Not including them sets the property to
......@@ -424,6 +425,7 @@ typedef enum {
NICE_AGENT_OPTION_ICE_TRICKLE = 1 << 3,
NICE_AGENT_OPTION_SUPPORT_RENOMINATION = 1 << 4,
NICE_AGENT_OPTION_CONSENT_FRESHNESS = 1 << 5,
NICE_AGENT_OPTION_BYTESTREAM_TCP = 1 << 6,
} NiceAgentOption;
/**
......
......@@ -388,6 +388,7 @@ nice_component_close (NiceAgent *agent, NiceStream *stream, NiceComponent *cmp)
}
g_free (cmp->recv_buffer);
g_free (cmp->rfc4571_buffer);
}
/*
......@@ -1129,6 +1130,9 @@ nice_component_init (NiceComponent *component)
component->recv_buffer = g_malloc (MAX_BUFFER_SIZE);
component->recv_buffer_size = MAX_BUFFER_SIZE;
component->rfc4571_buffer_size = sizeof (guint16) + G_MAXUINT16;
component->rfc4571_buffer = g_malloc (component->rfc4571_buffer_size);
}
static void
......@@ -1304,6 +1308,9 @@ static gboolean
component_source_prepare (GSource *source, gint *timeout_)
{
ComponentSource *component_source = (ComponentSource *) source;
/* We can’t be sure if the ComponentSource itself needs to be dispatched until
* poll() is called on all the child sources. */
gboolean skip_poll = FALSE;
NiceAgent *agent;
NiceComponent *component;
GSList *parentl, *childl;
......@@ -1320,6 +1327,11 @@ component_source_prepare (GSource *source, gint *timeout_)
&component))
goto done;
if (component->rfc4571_wakeup_needed) {
component->rfc4571_wakeup_needed = FALSE;
skip_poll = TRUE;
goto done;
}
if (component->socket_sources_age ==
component_source->component_socket_sources_age)
......@@ -1393,9 +1405,7 @@ component_source_prepare (GSource *source, gint *timeout_)
agent_unlock_and_emit (agent);
g_object_unref (agent);
/* We can’t be sure if the ComponentSource itself needs to be dispatched until
* poll() is called on all the child sources. */
return FALSE;
return skip_poll;
}
static gboolean
......@@ -1659,3 +1669,9 @@ nice_component_get_sockets (NiceComponent *component)
return array;
}
guint
nice_component_compute_rfc4571_headroom (NiceComponent *component)
{
return component->rfc4571_buffer_offset - component->rfc4571_frame_offset;
}
......@@ -241,6 +241,16 @@ struct _NiceComponent {
*/
guint8 *recv_buffer;
guint recv_buffer_size;
/* ICE-TCP frame state */
guint8 *rfc4571_buffer;
guint rfc4571_buffer_offset;
guint rfc4571_buffer_size;
guint rfc4571_frame_offset;
guint rfc4571_frame_size;
guint rfc4571_consumed_size;
NiceAddress rfc4571_remote_addr;
gboolean rfc4571_wakeup_needed;
};
typedef struct {
......@@ -331,6 +341,9 @@ nice_component_verify_remote_candidate (NiceComponent *component,
GPtrArray *
nice_component_get_sockets (NiceComponent *component);
guint
nice_component_compute_rfc4571_headroom (NiceComponent *component);
G_END_DECLS
#endif /* _NICE_COMPONENT_H */
......
......@@ -23,6 +23,7 @@ nice_tests = [
'test-trickle',
'test-tcp',
'test-icetcp',
'test-bytestream-tcp',
'test-credentials',
'test-turn',
'test-drop-invalid',
......@@ -40,7 +41,7 @@ if cc.has_header('arpa/inet.h')
endif
foreach tname : nice_tests
if tname.startswith('test-io-stream') or tname.startswith('test-send-recv')
if tname.startswith('test-io-stream') or tname.startswith('test-send-recv') or tname == 'test-bytestream-tcp'
extra_src = ['test-io-stream-common.c']
else
extra_src = []
......
/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2013 Collabora Ltd.
* Contact: Philip Withnall
*
* 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:
* Philip Withnall, 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.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "agent.h"
#include "test-io-stream-common.h"
#include <stdlib.h>
#include <string.h>
#ifndef G_OS_WIN32
#include <unistd.h>
#endif
typedef struct {
gsize recv_count;
gsize *other_recv_count;
gsize send_count;
gsize *other_send_count;
} ThreadData;
static const gchar test_sequence[] = { '1', '2', '3', '4' };
static void
read_thread_cb (GInputStream *input_stream, TestIOStreamThreadData *data)
{
ThreadData *user_data = data->user_data;
guint8 buf[2];
GError *error = NULL;
g_input_stream_read_all (input_stream, buf, sizeof (buf), NULL, NULL, &error);
g_assert_no_error (error);
user_data->recv_count++;
g_assert_cmpmem (buf, sizeof (buf), test_sequence, sizeof (buf));
g_input_stream_read_all (input_stream, buf, sizeof (buf), NULL, NULL, &error);
g_assert_no_error (error);
user_data->recv_count++;
g_assert_cmpmem (buf, sizeof (buf), test_sequence + 2, sizeof (buf));
check_for_termination (data, &user_data->recv_count,
user_data->other_recv_count, &user_data->send_count, 2);
}
static void
write_thread_cb (GOutputStream *output_stream, TestIOStreamThreadData *data)
{
ThreadData *user_data = data->user_data;
GError *error = NULL;
g_output_stream_write_all (output_stream, test_sequence,
sizeof (test_sequence), NULL, NULL, &error);
g_assert_no_error (error);
user_data->send_count += 2;
}
int main (void)
{
ThreadData *l_data, *r_data;
const TestIOStreamCallbacks callbacks = {
read_thread_cb,
write_thread_cb,
NULL,
NULL,
};
#ifdef G_OS_WIN32
WSADATA w;
WSAStartup (0x0202, &w);
#endif
l_data = g_malloc0 (sizeof (ThreadData));
r_data = g_malloc0 (sizeof (ThreadData));
l_data->recv_count = 0;
l_data->other_recv_count = &r_data->recv_count;
l_data->send_count = 0;
l_data->other_send_count = &r_data->send_count;
r_data->recv_count = 0;
r_data->other_recv_count = &l_data->recv_count;
r_data->send_count = 0;
r_data->other_send_count = &l_data->send_count;
run_io_stream_test (30, TRUE, &callbacks, l_data, NULL, r_data, NULL,
TEST_IO_STREAM_OPTION_TCP_ONLY | TEST_IO_STREAM_OPTION_BYTESTREAM_TCP);
g_free (r_data);
g_free (l_data);
#ifdef G_OS_WIN32
WSACleanup ();
#endif
return 0;
}
......@@ -128,7 +128,7 @@ int main (void)
r_cancellation_thread = spawn_thread ("libnice R cancel",
cancellation_thread_cb, &r_data);
run_io_stream_test (30, TRUE, &callbacks, &l_data, NULL, &r_data, NULL);
run_io_stream_test (30, TRUE, &callbacks, &l_data, NULL, &r_data, NULL, 0);
g_thread_join (l_cancellation_thread);
g_thread_join (r_cancellation_thread);
......
......@@ -128,7 +128,8 @@ int main (void)
WSAStartup (0x0202, &w);
#endif
run_io_stream_test (30, TRUE, &callbacks, (gpointer) TRUE, NULL, NULL, NULL);
run_io_stream_test (30, TRUE, &callbacks, (gpointer) TRUE, NULL, NULL, NULL,
0);
#ifdef G_OS_WIN32
WSACleanup ();
......
......@@ -128,7 +128,8 @@ int main (void)
WSAStartup (0x0202, &w);
#endif
run_io_stream_test (30, TRUE, &callbacks, (gpointer) TRUE, NULL, NULL, NULL);
run_io_stream_test (30, TRUE, &callbacks, (gpointer) TRUE, NULL, NULL, NULL,
0);
#ifdef G_OS_WIN32
WSACleanup ();
......
......@@ -243,7 +243,8 @@ new_selected_pair_cb (NiceAgent *agent, guint stream_id, guint component_id,
static NiceAgent *
create_agent (gboolean controlling_mode, TestIOStreamThreadData *data,
GMainContext **main_context, GMainLoop **main_loop)
GMainContext **main_context, GMainLoop **main_loop,
TestIOStreamOption flags)
{
NiceAgent *agent;
NiceAddress base_addr;
......@@ -253,17 +254,29 @@ create_agent (gboolean controlling_mode, TestIOStreamThreadData *data,
*main_context = g_main_context_new ();
*main_loop = g_main_loop_new (*main_context, FALSE);
/* Use Google compatibility to ignore credentials. */
if (data->reliable)
agent = nice_agent_new_reliable (*main_context, NICE_COMPATIBILITY_GOOGLE);
agent = nice_agent_new_reliable (*main_context, NICE_COMPATIBILITY_RFC5245);
else
agent = nice_agent_new (*main_context, NICE_COMPATIBILITY_GOOGLE);
agent = nice_agent_new (*main_context, NICE_COMPATIBILITY_RFC5245);
g_object_set (G_OBJECT (agent),
"controlling-mode", controlling_mode,
"upnp", FALSE,
NULL);
if (flags & TEST_IO_STREAM_OPTION_TCP_ONLY) {
g_object_set (G_OBJECT (agent),
"ice-udp", FALSE,
"ice-tcp", TRUE,
NULL);
}
if (flags & TEST_IO_STREAM_OPTION_BYTESTREAM_TCP) {
g_object_set (G_OBJECT (agent),
"bytestream-tcp", TRUE,
NULL);
}
/* Specify which local interface to use. */
g_assert_true (nice_address_set_from_string (&base_addr, "127.0.0.1"));
nice_agent_add_local_address (agent, &base_addr);
......@@ -310,6 +323,28 @@ add_stream (NiceAgent *agent)
GUINT_TO_POINTER (stream_id));
}
static void
swap_credentials (NiceAgent *agent)
{
guint stream_id;
gchar *ufrag, *password;
NiceAgent *other_agent;
guint other_stream_id;
stream_id = GPOINTER_TO_UINT (
g_object_get_data (G_OBJECT (agent), "stream-id"));
nice_agent_get_local_credentials (agent, stream_id, &ufrag, &password);
other_agent = g_object_get_data (G_OBJECT (agent), "other-agent");
other_stream_id = GPOINTER_TO_UINT (
g_object_get_data (G_OBJECT (other_agent), "stream-id"));
nice_agent_set_remote_credentials (other_agent, other_stream_id, ufrag,
password);
g_free (ufrag);
g_free (password);
}
static void
run_agent (TestIOStreamThreadData *data, NiceAgent *agent)
{
......@@ -346,7 +381,8 @@ void
run_io_stream_test (guint deadlock_timeout, gboolean reliable,
const TestIOStreamCallbacks *callbacks,
gpointer l_user_data, GDestroyNotify l_user_data_free,
gpointer r_user_data, GDestroyNotify r_user_data_free)
gpointer r_user_data, GDestroyNotify r_user_data_free,
TestIOStreamOption flags)
{
GMainLoop *error_loop;
GThread *l_main_thread, *r_main_thread;
......@@ -396,9 +432,9 @@ run_io_stream_test (guint deadlock_timeout, gboolean reliable,
/* Create the L and R agents. */
l_data.agent = create_agent (TRUE, &l_data,
&l_data.main_context, &l_data.main_loop);
&l_data.main_context, &l_data.main_loop, flags);
r_data.agent = create_agent (FALSE, &r_data,
&r_data.main_context, &r_data.main_loop);
&r_data.main_context, &r_data.main_loop, flags);
g_object_set_data (G_OBJECT (l_data.agent), "other-agent", r_data.agent);
g_object_set_data (G_OBJECT (r_data.agent), "other-agent", l_data.agent);
......@@ -411,6 +447,8 @@ run_io_stream_test (guint deadlock_timeout, gboolean reliable,
add_stream (l_data.agent);
add_stream (r_data.agent);
swap_credentials (l_data.agent);
swap_credentials (r_data.agent);
run_agent (&l_data, l_data.agent);
run_agent (&r_data, r_data.agent);
......
......@@ -105,12 +105,19 @@ struct _TestIOStreamThreadData {
guint *start_count;
};
typedef enum
{
TEST_IO_STREAM_OPTION_TCP_ONLY = 1 << 0,
TEST_IO_STREAM_OPTION_BYTESTREAM_TCP = 1 << 1,
} TestIOStreamOption;
GThread *spawn_thread (const gchar *thread_name, GThreadFunc thread_func,
gpointer user_data);
void run_io_stream_test (guint deadlock_timeout, gboolean reliable,
const TestIOStreamCallbacks *callbacks,
gpointer l_user_data, GDestroyNotify l_user_data_free,
gpointer r_user_data, GDestroyNotify r_user_data_free);
gpointer r_user_data, GDestroyNotify r_user_data_free,
TestIOStreamOption flags);
void check_for_termination (TestIOStreamThreadData *data, gsize *recv_count,
gsize *other_recv_count, volatile gsize *send_count, gsize expected_recv_count);
......
......@@ -178,7 +178,7 @@ int main (void)
r_data->other_send_count = &l_data->send_count;
r_data->recv_offset = 0;
run_io_stream_test (30, TRUE, &callbacks, l_data, NULL, r_data, NULL);
run_io_stream_test (30, TRUE, &callbacks, l_data, NULL, r_data, NULL, 0);
g_free (r_data);
g_free (l_data);
......
......@@ -143,7 +143,7 @@ int main (void)
r_data->send_count = 0;
r_data->other_send_count = &l_data->send_count;
run_io_stream_test (30, TRUE, &callbacks, l_data, NULL, r_data, NULL);
run_io_stream_test (30, TRUE, &callbacks, l_data, NULL, r_data, NULL, 0);
/* Verify that correct number of local candidates were reported. */
g_assert_cmpuint (l_data->cand_count, ==, 1);
......
......@@ -1146,7 +1146,9 @@ test (gboolean reliable, StreamApi stream_api, gsize n_bytes, guint n_messages,
&l_data.received_bytes, &l_data.received_messages);
run_io_stream_test (deadlock_timeout, reliable, &callbacks[stream_api],
&l_data, NULL, &r_data, NULL);
&l_data, NULL, &r_data, NULL,
/* Ensure TCP has the same behavior as Pseudo-TCP in reliable mode: */
reliable ? TEST_IO_STREAM_OPTION_BYTESTREAM_TCP : 0);
test_data_clear (&r_data);
test_data_clear (&l_data);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment