Various changes for yamux adding a protocol
This commit is contained in:
parent
f2e5af4058
commit
b3c8e77ed9
18 changed files with 691 additions and 162 deletions
|
@ -139,7 +139,7 @@ int libp2p_conn_dialer_join_swarm(const struct Dialer* dialer, struct Libp2pPeer
|
||||||
if (new_stream != NULL) {
|
if (new_stream != NULL) {
|
||||||
peer->sessionContext->default_stream = new_stream;
|
peer->sessionContext->default_stream = new_stream;
|
||||||
// yamux over multistream
|
// yamux over multistream
|
||||||
new_stream = libp2p_yamux_stream_new(new_stream);
|
new_stream = libp2p_yamux_stream_new(new_stream, 0, NULL);
|
||||||
if (new_stream != NULL) {
|
if (new_stream != NULL) {
|
||||||
peer->sessionContext->default_stream = new_stream;
|
peer->sessionContext->default_stream = new_stream;
|
||||||
// we have our swarm connection. Now we ask for some "channels"
|
// we have our swarm connection. Now we ask for some "channels"
|
||||||
|
|
|
@ -157,6 +157,26 @@ void libp2p_stream_message_free(struct StreamMessage* msg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Make a copy of a message
|
||||||
|
* @param original the original message
|
||||||
|
* @returns a StreamMessage that is a copy of the original
|
||||||
|
*/
|
||||||
|
struct StreamMessage* libp2p_stream_message_copy(const struct StreamMessage* original) {
|
||||||
|
struct StreamMessage* copy = libp2p_stream_message_new();
|
||||||
|
if (copy != NULL) {
|
||||||
|
copy->error_number = original->error_number;
|
||||||
|
copy->data_size = original->data_size;
|
||||||
|
copy->data = (uint8_t*) malloc(copy->data_size);
|
||||||
|
if (copy->data == NULL) {
|
||||||
|
libp2p_stream_message_free(copy);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memcpy(copy->data, original->data, copy->data_size);
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
/****
|
/****
|
||||||
* Make a copy of a SessionContext
|
* Make a copy of a SessionContext
|
||||||
* @param original the original
|
* @param original the original
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "varint.h"
|
#include "varint.h"
|
||||||
#include "libp2p/net/protocol.h"
|
#include "libp2p/net/protocol.h"
|
||||||
#include "libp2p/net/protocol.h"
|
#include "libp2p/net/protocol.h"
|
||||||
|
#include "libp2p/net/multistream.h"
|
||||||
#include "libp2p/utils/vector.h"
|
#include "libp2p/utils/vector.h"
|
||||||
#include "libp2p/net/stream.h"
|
#include "libp2p/net/stream.h"
|
||||||
#include "libp2p/conn/session.h"
|
#include "libp2p/conn/session.h"
|
||||||
|
@ -50,7 +51,7 @@ int libp2p_identify_send_protocol(struct Stream *stream) {
|
||||||
/***
|
/***
|
||||||
* Check to see if the reply is the identify header we expect
|
* Check to see if the reply is the identify header we expect
|
||||||
* NOTE: if we initiate the connection, we should expect the same back
|
* NOTE: if we initiate the connection, we should expect the same back
|
||||||
* @param context the SessionContext
|
* @param stream the incoming stream of the underlying protocol
|
||||||
* @returns true(1) on success, false(0) otherwise
|
* @returns true(1) on success, false(0) otherwise
|
||||||
*/
|
*/
|
||||||
int libp2p_identify_receive_protocol(struct Stream* stream) {
|
int libp2p_identify_receive_protocol(struct Stream* stream) {
|
||||||
|
@ -81,12 +82,11 @@ int libp2p_identify_receive_protocol(struct Stream* stream) {
|
||||||
* @returns <0 on error, 0 if loop should not continue, >0 on success
|
* @returns <0 on error, 0 if loop should not continue, >0 on success
|
||||||
*/
|
*/
|
||||||
int libp2p_identify_handle_message(const struct StreamMessage* msg, struct Stream* stream, void* protocol_context) {
|
int libp2p_identify_handle_message(const struct StreamMessage* msg, struct Stream* stream, void* protocol_context) {
|
||||||
|
// attempt to create a new Identify connection with them.
|
||||||
|
// send the protocol id back, and set up the channel
|
||||||
struct Stream* new_stream = libp2p_identify_stream_new(stream);
|
struct Stream* new_stream = libp2p_identify_stream_new(stream);
|
||||||
if (new_stream == NULL)
|
if (new_stream == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
// attempt to create a new Identify connection with them.
|
|
||||||
// send the protocol id back, and set up the channel
|
|
||||||
//TODO: now add this "channel"
|
|
||||||
return stream->handle_upgrade(stream, new_stream);
|
return stream->handle_upgrade(stream, new_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,6 +149,7 @@ struct Stream* libp2p_identify_stream_new(struct Stream* parent_stream) {
|
||||||
ctx->stream = out;
|
ctx->stream = out;
|
||||||
out->stream_context = ctx;
|
out->stream_context = ctx;
|
||||||
out->close = libp2p_identify_close;
|
out->close = libp2p_identify_close;
|
||||||
|
out->negotiate = libp2p_identify_stream_new;
|
||||||
if (!libp2p_identify_send_protocol(parent_stream) || !libp2p_identify_receive_protocol(parent_stream)) {
|
if (!libp2p_identify_send_protocol(parent_stream) || !libp2p_identify_receive_protocol(parent_stream)) {
|
||||||
libp2p_stream_free(out);
|
libp2p_stream_free(out);
|
||||||
free(ctx);
|
free(ctx);
|
||||||
|
@ -158,3 +159,40 @@ struct Stream* libp2p_identify_stream_new(struct Stream* parent_stream) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Create a new stream that negotiates the identify protocol
|
||||||
|
* on top of the multistream protocol
|
||||||
|
*
|
||||||
|
* NOTE: This will be sent by our side (us asking them).
|
||||||
|
* Incoming "Identify" requests should be handled by the
|
||||||
|
* external protocol handler, not this function.
|
||||||
|
*
|
||||||
|
* @param parent_stream the parent stream
|
||||||
|
* @returns a new Stream that is a multistream, but with "identify" already negotiated
|
||||||
|
*/
|
||||||
|
struct Stream* libp2p_identify_stream_new_with_multistream(struct Stream* parent_stream) {
|
||||||
|
if (parent_stream == NULL)
|
||||||
|
return NULL;
|
||||||
|
struct Stream* multistream = libp2p_net_multistream_stream_new(parent_stream);
|
||||||
|
struct Stream* out = libp2p_stream_new();
|
||||||
|
if (out != NULL) {
|
||||||
|
out->stream_type = STREAM_TYPE_IDENTIFY;
|
||||||
|
out->parent_stream = multistream;
|
||||||
|
struct IdentifyContext* ctx = (struct IdentifyContext*) malloc(sizeof(struct IdentifyContext));
|
||||||
|
if (ctx == NULL) {
|
||||||
|
libp2p_stream_free(out);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ctx->parent_stream = multistream;
|
||||||
|
ctx->stream = out;
|
||||||
|
out->stream_context = ctx;
|
||||||
|
out->close = libp2p_identify_close;
|
||||||
|
out->negotiate = libp2p_identify_stream_new_with_multistream;
|
||||||
|
if (!libp2p_identify_send_protocol(parent_stream) || !libp2p_identify_receive_protocol(parent_stream)) {
|
||||||
|
libp2p_stream_free(out);
|
||||||
|
free(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
|
@ -95,6 +95,15 @@ int libp2p_net_multistream_negotiate(struct MultistreamContext* ctx);
|
||||||
*/
|
*/
|
||||||
struct KademliaMessage* libp2p_net_multistream_get_message(struct Stream* stream);
|
struct KademliaMessage* libp2p_net_multistream_get_message(struct Stream* stream);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the transmission size to the front of a StreamMessage.
|
||||||
|
* NOTE: This is used internally by multistream. It is accessible to help
|
||||||
|
* with testing.
|
||||||
|
* @param incoming the incoming message
|
||||||
|
* @returns a new StreamMessage, in the format of a MessageStream buffer
|
||||||
|
*/
|
||||||
|
struct StreamMessage* libp2p_net_multistream_prepare_to_send(struct StreamMessage* incoming);
|
||||||
|
|
||||||
struct Stream* libp2p_net_multistream_stream_new(struct Stream* parent_stream);
|
struct Stream* libp2p_net_multistream_stream_new(struct Stream* parent_stream);
|
||||||
|
|
||||||
void libp2p_net_multistream_stream_free(struct Stream* stream);
|
void libp2p_net_multistream_stream_free(struct Stream* stream);
|
||||||
|
|
|
@ -25,6 +25,12 @@ struct StreamMessage* libp2p_stream_message_new();
|
||||||
*/
|
*/
|
||||||
void libp2p_stream_message_free(struct StreamMessage* msg);
|
void libp2p_stream_message_free(struct StreamMessage* msg);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Make a copy of a message
|
||||||
|
* @param original the original message
|
||||||
|
* @returns a StreamMessage that is a copy of the original
|
||||||
|
*/
|
||||||
|
struct StreamMessage* libp2p_stream_message_copy(const struct StreamMessage* original);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a context struct for a basic IP connection
|
* This is a context struct for a basic IP connection
|
||||||
|
@ -35,6 +41,19 @@ struct ConnectionContext {
|
||||||
struct SessionContext* session_context;
|
struct SessionContext* session_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The different types of protocols
|
||||||
|
*/
|
||||||
|
enum stream_type {
|
||||||
|
STREAM_TYPE_UNKNOWN = 0x0,
|
||||||
|
STREAM_TYPE_MULTISTREAM = 0x1,
|
||||||
|
STREAM_TYPE_SECIO = 0x2,
|
||||||
|
STREAM_TYPE_KADEMLIA = 0x3,
|
||||||
|
STREAM_TYPE_IDENTIFY = 0x4,
|
||||||
|
STREAM_TYPE_YAMUX = 0x5,
|
||||||
|
STREAM_TYPE_JOURNAL = 0x6,
|
||||||
|
STREAM_TYPE_RAW = 0x7
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface in front of various streams
|
* An interface in front of various streams
|
||||||
|
@ -47,6 +66,7 @@ struct Stream {
|
||||||
pthread_mutex_t* socket_mutex; // only 1 transmission at a time
|
pthread_mutex_t* socket_mutex; // only 1 transmission at a time
|
||||||
struct Stream* parent_stream; // what stream wraps this stream
|
struct Stream* parent_stream; // what stream wraps this stream
|
||||||
int channel; // the channel (for multiplexing streams)
|
int channel; // the channel (for multiplexing streams)
|
||||||
|
enum stream_type stream_type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A generic place to store implementation-specific context items
|
* A generic place to store implementation-specific context items
|
||||||
|
@ -103,6 +123,13 @@ struct Stream {
|
||||||
* @param new_stream the newly created stream
|
* @param new_stream the newly created stream
|
||||||
*/
|
*/
|
||||||
int (*handle_upgrade)(struct Stream* stream, struct Stream* new_stream);
|
int (*handle_upgrade)(struct Stream* stream, struct Stream* new_stream);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Negotiate this protocol using the parent stream
|
||||||
|
* @param parent_stream the connection to use
|
||||||
|
* @returns a new Stream, or NULL on error
|
||||||
|
*/
|
||||||
|
struct Stream* (*negotiate)(struct Stream* parent_stream);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Stream* libp2p_stream_new();
|
struct Stream* libp2p_stream_new();
|
||||||
|
|
|
@ -23,14 +23,18 @@ enum yamux_error
|
||||||
yamux_error_intern = 0x02
|
yamux_error_intern = 0x02
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// forward declarations
|
||||||
struct yamux_session;
|
struct yamux_session;
|
||||||
struct yamux_stream;
|
struct yamux_stream;
|
||||||
|
struct YamuxContext;
|
||||||
|
struct Stream;
|
||||||
|
struct StreamMessage;
|
||||||
|
|
||||||
typedef void* (*yamux_session_get_str_ud_fn)(struct yamux_session* session, yamux_streamid newid );
|
typedef void* (*yamux_session_get_str_ud_fn)(struct yamux_session* session, yamux_streamid newid );
|
||||||
typedef void (*yamux_session_ping_fn )(struct yamux_session* session, uint32_t val );
|
typedef void (*yamux_session_ping_fn )(struct yamux_session* session, uint32_t val );
|
||||||
typedef void (*yamux_session_pong_fn )(struct yamux_session* session, uint32_t val, struct timespec dt);
|
typedef void (*yamux_session_pong_fn )(struct yamux_session* session, uint32_t val, struct timespec dt);
|
||||||
typedef void (*yamux_session_go_away_fn )(struct yamux_session* session, enum yamux_error err );
|
typedef void (*yamux_session_go_away_fn )(struct yamux_session* session, enum yamux_error err );
|
||||||
typedef void (*yamux_session_new_stream_fn)(struct yamux_session* session, struct yamux_stream* stream);
|
typedef void (*yamux_session_new_stream_fn)(struct YamuxContext* context, struct Stream* stream, struct StreamMessage* msg);
|
||||||
typedef void (*yamux_session_free_fn )(struct yamux_session* sesssion );
|
typedef void (*yamux_session_free_fn )(struct yamux_session* sesssion );
|
||||||
|
|
||||||
struct yamux_session_stream
|
struct yamux_session_stream
|
||||||
|
@ -38,31 +42,71 @@ struct yamux_session_stream
|
||||||
struct yamux_stream* stream;
|
struct yamux_stream* stream;
|
||||||
int alive;
|
int alive;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A yamux session. This keeps all the streams related to a yamux session
|
||||||
|
*/
|
||||||
struct yamux_session
|
struct yamux_session
|
||||||
{
|
{
|
||||||
struct yamux_config* config;
|
struct yamux_config* config; // configuration of size of windows and max number of streams
|
||||||
|
|
||||||
size_t num_streams;
|
size_t num_streams; // number of streams
|
||||||
size_t cap_streams;
|
size_t cap_streams; // capacity of stream array
|
||||||
struct yamux_session_stream* streams;
|
struct yamux_session_stream* streams; // array of streams
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user data
|
||||||
|
*/
|
||||||
yamux_session_get_str_ud_fn get_str_ud_fn;
|
yamux_session_get_str_ud_fn get_str_ud_fn;
|
||||||
|
/**
|
||||||
|
* Ping
|
||||||
|
*/
|
||||||
yamux_session_ping_fn ping_fn ;
|
yamux_session_ping_fn ping_fn ;
|
||||||
|
/**
|
||||||
|
* Respond to ping
|
||||||
|
*/
|
||||||
yamux_session_pong_fn pong_fn ;
|
yamux_session_pong_fn pong_fn ;
|
||||||
|
/**
|
||||||
|
* Hanging up
|
||||||
|
*/
|
||||||
yamux_session_go_away_fn go_away_fn ;
|
yamux_session_go_away_fn go_away_fn ;
|
||||||
|
/**
|
||||||
|
* A new stream is coming in
|
||||||
|
*/
|
||||||
yamux_session_new_stream_fn new_stream_fn;
|
yamux_session_new_stream_fn new_stream_fn;
|
||||||
|
/**
|
||||||
|
* Free resources
|
||||||
|
*/
|
||||||
yamux_session_free_fn free_fn ;
|
yamux_session_free_fn free_fn ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User data
|
||||||
|
*/
|
||||||
void* userdata;
|
void* userdata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for heartbeat
|
||||||
|
*/
|
||||||
struct timespec since_ping;
|
struct timespec since_ping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Session type (client or server)
|
||||||
|
*/
|
||||||
enum yamux_session_type type;
|
enum yamux_session_type type;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* The parent stream
|
||||||
|
*/
|
||||||
struct Stream* parent_stream;
|
struct Stream* parent_stream;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* The next id to use
|
||||||
|
*/
|
||||||
yamux_streamid nextid;
|
yamux_streamid nextid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this session is closed
|
||||||
|
*/
|
||||||
int closed;
|
int closed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -92,12 +136,12 @@ ssize_t yamux_session_ping(struct yamux_session* session, uint32_t value, int po
|
||||||
// defers to stream read handlers
|
// defers to stream read handlers
|
||||||
ssize_t yamux_session_read(struct yamux_session* session);
|
ssize_t yamux_session_read(struct yamux_session* session);
|
||||||
|
|
||||||
struct YamuxChannelContext;
|
|
||||||
/**
|
/**
|
||||||
* Decode an incoming message
|
* Decode an incoming message
|
||||||
* @param channel the channel
|
* @param context a YamuxChannelContext or YamuxContext
|
||||||
* @param incoming the incoming bytes
|
* @param incoming the incoming bytes
|
||||||
* @param incoming_size the size of the incoming bytes
|
* @param incoming_size the size of the incoming bytes
|
||||||
|
* @param return_message the return message (usually the bytes after the frame)
|
||||||
* @returns true(1) on success, false(0) otherwise
|
* @returns true(1) on success, false(0) otherwise
|
||||||
*/
|
*/
|
||||||
int yamux_decode(struct YamuxChannelContext* channel, const uint8_t* incoming, size_t incoming_size);
|
int yamux_decode(void* context, const uint8_t* incoming, size_t incoming_size, struct StreamMessage** return_message);
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
#include "libp2p/conn/session.h"
|
#include "libp2p/conn/session.h"
|
||||||
#include "libp2p/yamux/yamux.h"
|
#include "libp2p/yamux/yamux.h"
|
||||||
|
|
||||||
|
// forward declarations
|
||||||
|
struct YamuxChannelContext;
|
||||||
|
|
||||||
// NOTE: 'data' is not guaranteed to be preserved when the read_fn
|
// NOTE: 'data' is not guaranteed to be preserved when the read_fn
|
||||||
// handler exists (read: it will be freed).
|
// handler exists (read: it will be freed).
|
||||||
struct yamux_stream;
|
struct yamux_stream;
|
||||||
|
@ -35,7 +38,7 @@ struct yamux_stream
|
||||||
yamux_stream_rst_fn rst_fn ;
|
yamux_stream_rst_fn rst_fn ;
|
||||||
yamux_stream_free_fn free_fn;
|
yamux_stream_free_fn free_fn;
|
||||||
|
|
||||||
void* userdata;
|
struct Stream* stream;
|
||||||
|
|
||||||
enum yamux_stream_state state;
|
enum yamux_stream_state state;
|
||||||
|
|
||||||
|
@ -44,16 +47,26 @@ struct yamux_stream
|
||||||
uint32_t window_size;
|
uint32_t window_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
// does not init the stream
|
/**
|
||||||
struct yamux_stream* yamux_stream_new(struct yamux_session* session, yamux_streamid id, void* userdata);
|
* Create a new stream that has a YamuxChannelContext
|
||||||
|
* @param context the Yamux context
|
||||||
|
* @param id the stream id
|
||||||
|
* @param msg the incoming message
|
||||||
|
* @returns a stream that is a Yamux channel
|
||||||
|
*/
|
||||||
|
struct Stream* yamux_channel_new(struct YamuxContext* context, yamux_streamid id, struct StreamMessage* msg);
|
||||||
|
|
||||||
// not obligatory, SYN is sent by yamux_stream_write when the stream
|
// not obligatory, SYN is sent by yamux_stream_write when the stream
|
||||||
// isn't initialised anyway
|
// isn't initialised anyway
|
||||||
ssize_t yamux_stream_init (struct YamuxChannelContext* channel_ctx);
|
ssize_t yamux_stream_init (struct YamuxChannelContext* channel_ctx);
|
||||||
|
|
||||||
// doesn't free the stream
|
/***
|
||||||
// uses FIN
|
* Closes the stream
|
||||||
ssize_t yamux_stream_close(struct YamuxChannelContext* channel_ctx);
|
* NOTE: doesn't free the stream, uses FIN
|
||||||
|
* @param context the YamuxContext or YamuxChannelContext
|
||||||
|
* @returns number of bytes sent
|
||||||
|
*/
|
||||||
|
ssize_t yamux_stream_close(void* context);
|
||||||
// uses RST
|
// uses RST
|
||||||
ssize_t yamux_stream_reset(struct YamuxChannelContext* stream);
|
ssize_t yamux_stream_reset(struct YamuxChannelContext* stream);
|
||||||
|
|
||||||
|
@ -68,9 +81,14 @@ ssize_t yamux_stream_write(struct YamuxChannelContext* ctx, uint32_t data_length
|
||||||
* @param frame the frame
|
* @param frame the frame
|
||||||
* @param incoming the stream bytes (after the frame)
|
* @param incoming the stream bytes (after the frame)
|
||||||
* @param incoming_size the size of incoming
|
* @param incoming_size the size of incoming
|
||||||
* @param session_context the SessionContext
|
|
||||||
* @returns the number of bytes processed (can be zero) or negative number on error
|
* @returns the number of bytes processed (can be zero) or negative number on error
|
||||||
*/
|
*/
|
||||||
ssize_t yamux_stream_process(struct yamux_stream* stream, struct yamux_frame* frame, const uint8_t* incoming, size_t incoming_size, struct SessionContext* session_context);
|
ssize_t yamux_stream_process(struct yamux_stream* stream, struct yamux_frame* frame, const uint8_t* incoming, size_t incoming_size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the flags for this context
|
||||||
|
* @param context the context
|
||||||
|
* @returns the correct flag
|
||||||
|
*/
|
||||||
|
enum yamux_frame_flags get_flags(void* context);
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@ struct YamuxContext {
|
||||||
struct Stream* stream;
|
struct Stream* stream;
|
||||||
struct yamux_session* session;
|
struct yamux_session* session;
|
||||||
struct Libp2pVector* channels;
|
struct Libp2pVector* channels;
|
||||||
|
int am_server;
|
||||||
|
int state; // the state of the connection
|
||||||
|
struct Libp2pVector* protocol_handlers;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct YamuxChannelContext {
|
struct YamuxChannelContext {
|
||||||
|
@ -60,7 +63,14 @@ int yamux_send_protocol(struct Stream* stream);
|
||||||
*/
|
*/
|
||||||
int yamux_receive_protocol(struct YamuxContext* context);
|
int yamux_receive_protocol(struct YamuxContext* context);
|
||||||
|
|
||||||
struct Stream* libp2p_yamux_stream_new(struct Stream* parent_stream);
|
/***
|
||||||
|
* Negotiate the Yamux protocol
|
||||||
|
* @param parent_stream the parent stream
|
||||||
|
* @param am_server true(1) if we are considered the server, false(0) if we are the client.
|
||||||
|
* @param protocol_handlers the protocol handlers (used when a new protocol is requested)
|
||||||
|
* @returns a Stream initialized and ready for yamux
|
||||||
|
*/
|
||||||
|
struct Stream* libp2p_yamux_stream_new(struct Stream* parent_stream, int am_server, struct Libp2pVector* protocol_handlers);
|
||||||
|
|
||||||
void libp2p_yamux_stream_free(struct Stream* stream);
|
void libp2p_yamux_stream_free(struct Stream* stream);
|
||||||
|
|
||||||
|
@ -74,9 +84,22 @@ int libp2p_yamux_stream_add(struct YamuxContext* ctx, struct Stream* stream);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a stream that has a "YamuxChannelContext" related to this yamux protocol
|
* Create a stream that has a "YamuxChannelContext" related to this yamux protocol
|
||||||
* @param parent_stream the parent yamux stream
|
* NOTE: If incoming_stream is not of the Yamux protocol, this "wraps" the incoming
|
||||||
* @returns a new Stream that is a YamuxChannelContext
|
* stream, so that the returned stream is the parent of the incoming_stream. If the
|
||||||
|
* incoming stream is of the yamux protocol, the YamuxChannelContext.child_stream
|
||||||
|
* will be NULL, awaiting an upgrade to fill it in.
|
||||||
|
* @param incoming_stream the stream of the new protocol
|
||||||
|
* @param channelNumber the channel number (0 if unknown)
|
||||||
|
* @returns a new Stream that has a YamuxChannelContext
|
||||||
*/
|
*/
|
||||||
struct Stream* libp2p_yamux_channel_new(struct Stream* parent_stream);
|
struct Stream* libp2p_yamux_channel_stream_new(struct Stream* incoming_stream, int channelNumber);
|
||||||
|
|
||||||
void libp2p_yamux_channel_free(struct YamuxChannelContext* ctx);
|
void libp2p_yamux_channel_free(struct YamuxChannelContext* ctx);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Prepare a new Yamux StreamMessage based on another StreamMessage
|
||||||
|
* NOTE: This is here for testing. This should normally not be used.
|
||||||
|
* @param incoming the incoming message
|
||||||
|
* @returns a new StreamMessage that has a yamux_frame
|
||||||
|
*/
|
||||||
|
struct StreamMessage* libp2p_yamux_prepare_to_send(struct StreamMessage* incoming);
|
||||||
|
|
|
@ -117,6 +117,7 @@ int libp2p_net_connection_write(void* stream_context, struct StreamMessage* msg)
|
||||||
struct Stream* libp2p_net_connection_new(int fd, char* ip, int port, struct SessionContext* session_context) {
|
struct Stream* libp2p_net_connection_new(int fd, char* ip, int port, struct SessionContext* session_context) {
|
||||||
struct Stream* out = (struct Stream*) malloc(sizeof(struct Stream));
|
struct Stream* out = (struct Stream*) malloc(sizeof(struct Stream));
|
||||||
if (out != NULL) {
|
if (out != NULL) {
|
||||||
|
out->stream_type = STREAM_TYPE_RAW;
|
||||||
out->close = libp2p_net_connection_close;
|
out->close = libp2p_net_connection_close;
|
||||||
out->peek = libp2p_net_connection_peek;
|
out->peek = libp2p_net_connection_peek;
|
||||||
out->read = libp2p_net_connection_read;
|
out->read = libp2p_net_connection_read;
|
||||||
|
|
|
@ -131,6 +131,31 @@ int libp2p_net_multistream_peek(void* stream_context) {
|
||||||
return parent_stream->peek(parent_stream->stream_context);
|
return parent_stream->peek(parent_stream->stream_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the transmission size to the front of a StreamMessage.
|
||||||
|
* NOTE: This is used internally by multistream. It is accessible to help
|
||||||
|
* with testing.
|
||||||
|
* @param incoming the incoming message
|
||||||
|
* @returns a new StreamMessage, in the format of a MessageStream buffer
|
||||||
|
*/
|
||||||
|
struct StreamMessage* libp2p_net_multistream_prepare_to_send(struct StreamMessage* incoming) {
|
||||||
|
struct StreamMessage* out = libp2p_stream_message_new();
|
||||||
|
if (out != NULL) {
|
||||||
|
unsigned char varint[12];
|
||||||
|
size_t varint_size = 0;
|
||||||
|
varint_encode(incoming->data_size, &varint[0], 12, &varint_size);
|
||||||
|
out->data_size = varint_size + incoming->data_size;
|
||||||
|
out->data = malloc(out->data_size);
|
||||||
|
if (out->data == NULL) {
|
||||||
|
libp2p_stream_message_free(out);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memcpy(&out->data[0], varint, varint_size);
|
||||||
|
memcpy(&out->data[varint_size], incoming->data, incoming->data_size);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write to an open multistream host
|
* Write to an open multistream host
|
||||||
* @param stream_context the session context
|
* @param stream_context the session context
|
||||||
|
@ -143,27 +168,14 @@ int libp2p_net_multistream_write(void* stream_context, struct StreamMessage* inc
|
||||||
int num_bytes = 0;
|
int num_bytes = 0;
|
||||||
|
|
||||||
if (incoming->data_size > 0) { // only do this is if there is something to send
|
if (incoming->data_size > 0) { // only do this is if there is something to send
|
||||||
// first get the size as a varint
|
struct StreamMessage* out = libp2p_net_multistream_prepare_to_send(incoming);
|
||||||
unsigned char varint[12];
|
|
||||||
size_t varint_size = 0;
|
|
||||||
varint_encode(incoming->data_size, &varint[0], 12, &varint_size);
|
|
||||||
// now put the size with the data
|
|
||||||
struct StreamMessage outgoing;
|
|
||||||
outgoing.data_size = varint_size + incoming->data_size;
|
|
||||||
outgoing.data = (uint8_t*) malloc(outgoing.data_size);
|
|
||||||
if (outgoing.data == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
memset(outgoing.data, 0, outgoing.data_size);
|
|
||||||
memcpy(outgoing.data, varint, varint_size);
|
|
||||||
memcpy(&outgoing.data[varint_size], incoming->data, incoming->data_size);
|
|
||||||
// now ship it
|
// now ship it
|
||||||
libp2p_logger_debug("multistream", "Attempting write %d bytes.\n", (int)outgoing.data_size);
|
libp2p_logger_debug("multistream", "Attempting write %d bytes.\n", (int)out->data_size);
|
||||||
num_bytes = parent_stream->write(parent_stream->stream_context, &outgoing);
|
num_bytes = parent_stream->write(parent_stream->stream_context, out);
|
||||||
// subtract the varint if all went well
|
// subtract the varint if all went well
|
||||||
if (num_bytes == outgoing.data_size)
|
if (num_bytes == out->data_size)
|
||||||
num_bytes = incoming->data_size;
|
num_bytes = incoming->data_size;
|
||||||
free(outgoing.data);
|
libp2p_stream_message_free(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
return num_bytes;
|
return num_bytes;
|
||||||
|
@ -349,7 +361,7 @@ int libp2p_net_multistream_negotiate(struct MultistreamContext* ctx) {
|
||||||
|
|
||||||
void libp2p_net_multistream_stream_free(struct Stream* stream) {
|
void libp2p_net_multistream_stream_free(struct Stream* stream) {
|
||||||
if (stream != NULL) {
|
if (stream != NULL) {
|
||||||
stream->parent_stream->close(stream->parent_stream->stream_context);
|
stream->parent_stream->close(stream->parent_stream);
|
||||||
// TODO: free memory allocations
|
// TODO: free memory allocations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -370,6 +382,7 @@ int libp2p_net_multistream_read_raw(void* stream_context, uint8_t* buffer, int b
|
||||||
struct Stream* libp2p_net_multistream_stream_new(struct Stream* parent_stream) {
|
struct Stream* libp2p_net_multistream_stream_new(struct Stream* parent_stream) {
|
||||||
struct Stream* out = (struct Stream*)malloc(sizeof(struct Stream));
|
struct Stream* out = (struct Stream*)malloc(sizeof(struct Stream));
|
||||||
if (out != NULL) {
|
if (out != NULL) {
|
||||||
|
out->stream_type = STREAM_TYPE_MULTISTREAM;
|
||||||
out->parent_stream = parent_stream;
|
out->parent_stream = parent_stream;
|
||||||
out->close = libp2p_net_multistream_close;
|
out->close = libp2p_net_multistream_close;
|
||||||
out->read = libp2p_net_multistream_read;
|
out->read = libp2p_net_multistream_read;
|
||||||
|
|
|
@ -42,7 +42,7 @@ struct Libp2pProtocolHandler* libp2p_protocol_handler_new() {
|
||||||
/***
|
/***
|
||||||
* Handle an incoming message
|
* Handle an incoming message
|
||||||
* @param message the incoming message
|
* @param message the incoming message
|
||||||
* @param session the SessionContext of the incoming connection
|
* @param stream the stream the message came in on
|
||||||
* @param handlers a Vector of protocol handlers
|
* @param handlers a Vector of protocol handlers
|
||||||
* @returns -1 on error, 0 if everything was okay, but the daemon should no longer handle this connection, 1 on success
|
* @returns -1 on error, 0 if everything was okay, but the daemon should no longer handle this connection, 1 on success
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1353,6 +1353,7 @@ int libp2p_secio_close(struct Stream* stream) {
|
||||||
struct Stream* libp2p_secio_stream_new(struct Stream* parent_stream, struct Libp2pPeer* remote_peer, struct Peerstore* peerstore, struct RsaPrivateKey* rsa_private_key) {
|
struct Stream* libp2p_secio_stream_new(struct Stream* parent_stream, struct Libp2pPeer* remote_peer, struct Peerstore* peerstore, struct RsaPrivateKey* rsa_private_key) {
|
||||||
struct Stream* new_stream = libp2p_stream_new();
|
struct Stream* new_stream = libp2p_stream_new();
|
||||||
if (new_stream != NULL) {
|
if (new_stream != NULL) {
|
||||||
|
new_stream->stream_type = STREAM_TYPE_SECIO;
|
||||||
struct SecioContext* ctx = (struct SecioContext*) malloc(sizeof(struct SecioContext));
|
struct SecioContext* ctx = (struct SecioContext*) malloc(sizeof(struct SecioContext));
|
||||||
if (ctx == NULL) {
|
if (ctx == NULL) {
|
||||||
libp2p_stream_free(new_stream);
|
libp2p_stream_free(new_stream);
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "libp2p/net/stream.h"
|
#include "libp2p/net/stream.h"
|
||||||
|
|
||||||
|
struct StreamMessage* mock_message = NULL;
|
||||||
|
int mock_message_position = 0;
|
||||||
|
|
||||||
void mock_stream_free(struct Stream* stream);
|
void mock_stream_free(struct Stream* stream);
|
||||||
|
|
||||||
int mock_stream_close(struct Stream* stream) {
|
int mock_stream_close(struct Stream* stream) {
|
||||||
|
@ -14,19 +17,27 @@ int mock_stream_close(struct Stream* stream) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int mock_stream_peek(void* context) {
|
int mock_stream_peek(void* context) {
|
||||||
return 1;
|
if (mock_message == NULL)
|
||||||
|
return 0;
|
||||||
|
return mock_message->data_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mock_stream_read(void* context, struct StreamMessage** msg, int timeout_secs) {
|
int mock_stream_read(void* context, struct StreamMessage** msg, int timeout_secs) {
|
||||||
|
*msg = libp2p_stream_message_copy(mock_message);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mock_stream_read_raw(void* context, uint8_t* buffer, int buffer_size, int timeout_secs) {
|
int mock_stream_read_raw(void* context, uint8_t* buffer, int buffer_size, int timeout_secs) {
|
||||||
return 1;
|
if (mock_message == NULL)
|
||||||
|
return 0;
|
||||||
|
int min = buffer_size > mock_message->data_size - mock_message_position ? mock_message->data_size - mock_message_position : buffer_size;
|
||||||
|
memcpy(buffer, mock_message->data, min);
|
||||||
|
mock_message_position += min;
|
||||||
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mock_stream_write(void* context, struct StreamMessage* msg) {
|
int mock_stream_write(void* context, struct StreamMessage* msg) {
|
||||||
return 1;
|
return msg->data_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Stream* mock_stream_new() {
|
struct Stream* mock_stream_new() {
|
||||||
|
|
|
@ -3,11 +3,22 @@
|
||||||
#include "libp2p/identify/identify.h"
|
#include "libp2p/identify/identify.h"
|
||||||
#include "mock_stream.h"
|
#include "mock_stream.h"
|
||||||
#include "libp2p/utils/logger.h"
|
#include "libp2p/utils/logger.h"
|
||||||
|
#include "libp2p/net/stream.h"
|
||||||
|
#include "libp2p/net/multistream.h"
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Helpers
|
* Helpers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
struct StreamMessage* build_message(const char* data) {
|
||||||
|
struct StreamMessage* out = libp2p_stream_message_new();
|
||||||
|
if (out != NULL) {
|
||||||
|
out->data_size = strlen(data);
|
||||||
|
out->data = (uint8_t*) malloc(out->data_size);
|
||||||
|
memcpy(out->data, data, out->data_size);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
/***
|
/***
|
||||||
* Sends back the yamux protocol to fake negotiation
|
* Sends back the yamux protocol to fake negotiation
|
||||||
*/
|
*/
|
||||||
|
@ -22,18 +33,49 @@ int mock_yamux_read_protocol(void* context, struct StreamMessage** msg, int netw
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Sends back the yamux protocol to fake negotiation
|
* Sends back the identify protocol (in a yamux wrapper) to fake negotiation
|
||||||
*/
|
*/
|
||||||
int mock_identify_read_protocol(void* context, struct StreamMessage** msg, int network_timeout) {
|
int mock_identify_read_protocol(void* context, struct StreamMessage** msg, int network_timeout) {
|
||||||
*msg = libp2p_stream_message_new();
|
struct StreamMessage message;
|
||||||
struct StreamMessage* message = *msg;
|
|
||||||
const char* id = "/ipfs/id/1.0.0\n";
|
const char* id = "/ipfs/id/1.0.0\n";
|
||||||
message->data_size = strlen(id);
|
message.data_size = strlen(id);
|
||||||
message->data = malloc(message->data_size);
|
message.data = (uint8_t*)id;
|
||||||
memcpy(message->data, id, message->data_size);
|
|
||||||
|
*msg = libp2p_yamux_prepare_to_send(&message);
|
||||||
|
// adjust the frame
|
||||||
|
struct yamux_frame* frame = (struct yamux_frame*)(*msg)->data;
|
||||||
|
frame->streamid = 1;
|
||||||
|
frame->flags = yamux_frame_syn;
|
||||||
|
encode_frame(frame);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mock_counter = 0;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Sends back the yamux protocol to fake negotiation
|
||||||
|
*/
|
||||||
|
int mock_multistream_then_identify_read_protocol(void* context, struct StreamMessage** msg, int network_timeout) {
|
||||||
|
// prepare the message
|
||||||
|
*msg = libp2p_stream_message_new();
|
||||||
|
struct StreamMessage* message = *msg;
|
||||||
|
message->data_size = mock_message->data_size - mock_message_position;
|
||||||
|
message->data = malloc(message->data_size);
|
||||||
|
memcpy(message->data, &mock_message->data[mock_message_position], message->data_size);
|
||||||
|
if (mock_counter == 0) {
|
||||||
|
// this is the first time through. Set mock_message to the identify protocol
|
||||||
|
libp2p_stream_message_free(mock_message);
|
||||||
|
mock_message = libp2p_net_multistream_prepare_to_send(build_message("/ipfs/id/1.0.0\n"));
|
||||||
|
mock_message_position = 0;
|
||||||
|
} else {
|
||||||
|
libp2p_stream_message_free(mock_message);
|
||||||
|
mock_message = NULL;
|
||||||
|
mock_message_position = 0;
|
||||||
|
}
|
||||||
|
return (*msg != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Tests
|
* Tests
|
||||||
*/
|
*/
|
||||||
|
@ -43,10 +85,11 @@ int mock_identify_read_protocol(void* context, struct StreamMessage** msg, int n
|
||||||
*/
|
*/
|
||||||
int test_yamux_stream_new() {
|
int test_yamux_stream_new() {
|
||||||
int retVal = 0;
|
int retVal = 0;
|
||||||
|
const char* yamux_id = "/yamux/1.0.0\n";
|
||||||
// setup
|
// setup
|
||||||
struct Stream* mock_stream = mock_stream_new();
|
struct Stream* mock_stream = mock_stream_new();
|
||||||
mock_stream->read = mock_yamux_read_protocol;
|
mock_message = build_message(yamux_id);
|
||||||
struct Stream* yamux_stream = libp2p_yamux_stream_new(mock_stream);
|
struct Stream* yamux_stream = libp2p_yamux_stream_new(mock_stream, 0, NULL);
|
||||||
if (yamux_stream == NULL)
|
if (yamux_stream == NULL)
|
||||||
goto exit;
|
goto exit;
|
||||||
// tear down
|
// tear down
|
||||||
|
@ -54,6 +97,8 @@ int test_yamux_stream_new() {
|
||||||
exit:
|
exit:
|
||||||
if (yamux_stream != NULL)
|
if (yamux_stream != NULL)
|
||||||
yamux_stream->close(yamux_stream);
|
yamux_stream->close(yamux_stream);
|
||||||
|
if (mock_message != NULL)
|
||||||
|
libp2p_stream_message_free(mock_message);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,9 +108,15 @@ int test_yamux_stream_new() {
|
||||||
int test_yamux_identify() {
|
int test_yamux_identify() {
|
||||||
int retVal = 0;
|
int retVal = 0;
|
||||||
// setup
|
// setup
|
||||||
|
// mock
|
||||||
struct Stream* mock_stream = mock_stream_new();
|
struct Stream* mock_stream = mock_stream_new();
|
||||||
mock_stream->read = mock_yamux_read_protocol;
|
mock_stream->read = mock_yamux_read_protocol;
|
||||||
struct Stream* yamux_stream = libp2p_yamux_stream_new(mock_stream);
|
// protocol handlers
|
||||||
|
struct Libp2pVector* protocol_handlers = libp2p_utils_vector_new(1);
|
||||||
|
struct Libp2pProtocolHandler* handler = libp2p_identify_build_protocol_handler(protocol_handlers);
|
||||||
|
libp2p_utils_vector_add(protocol_handlers, handler);
|
||||||
|
// yamux
|
||||||
|
struct Stream* yamux_stream = libp2p_yamux_stream_new(mock_stream, 0, protocol_handlers);
|
||||||
if (yamux_stream == NULL)
|
if (yamux_stream == NULL)
|
||||||
goto exit;
|
goto exit;
|
||||||
// Now add in another protocol
|
// Now add in another protocol
|
||||||
|
@ -78,12 +129,91 @@ int test_yamux_identify() {
|
||||||
exit:
|
exit:
|
||||||
if (yamux_stream != NULL)
|
if (yamux_stream != NULL)
|
||||||
yamux_stream->close(yamux_stream);
|
yamux_stream->close(yamux_stream);
|
||||||
|
libp2p_protocol_handlers_shutdown(protocol_handlers);
|
||||||
|
if (mock_message != NULL) {
|
||||||
|
libp2p_stream_message_free(mock_message);
|
||||||
|
mock_message = NULL;
|
||||||
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
int test_yamux_incoming_protocol_request() {
|
int test_yamux_incoming_protocol_request() {
|
||||||
int retVal = 0;
|
int retVal = 0;
|
||||||
|
|
||||||
|
// setup
|
||||||
|
// build the protocol handler that can handle yamux, multistream, and identify protocol
|
||||||
|
struct Libp2pVector* protocol_handlers = libp2p_utils_vector_new(1);
|
||||||
|
struct Libp2pProtocolHandler* handler = libp2p_identify_build_protocol_handler(protocol_handlers);
|
||||||
|
libp2p_utils_vector_add(protocol_handlers, handler);
|
||||||
|
handler = libp2p_yamux_build_protocol_handler(protocol_handlers);
|
||||||
|
libp2p_utils_vector_add(protocol_handlers, handler);
|
||||||
|
handler = libp2p_net_multistream_build_protocol_handler(protocol_handlers);
|
||||||
|
libp2p_utils_vector_add(protocol_handlers, handler);
|
||||||
|
// set up basic streams
|
||||||
|
struct Stream* mock_stream = mock_stream_new();
|
||||||
|
struct SessionContext* session_context = ((struct ConnectionContext*)mock_stream->stream_context)->session_context;
|
||||||
|
mock_message = build_message("/yamux/1.0.0\n");
|
||||||
|
struct StreamMessage* result_message = NULL;
|
||||||
|
if (!session_context->default_stream->read(session_context->default_stream->stream_context, &result_message, 10)) {
|
||||||
|
libp2p_logger_error("test_yamux", "Unable to read Yamux protocol from mock stream.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (libp2p_protocol_marshal(result_message, session_context->default_stream, protocol_handlers) < 0) {
|
||||||
|
libp2p_logger_error("test_yamux", "Upgrade to Yamux protocol unsuccessful.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
// now we should have upgraded to the yamux protocol
|
||||||
|
libp2p_stream_message_free(result_message);
|
||||||
|
result_message = NULL;
|
||||||
|
if (session_context->default_stream->parent_stream == NULL) {
|
||||||
|
libp2p_logger_error("test_yamux", "Upgrade to Yamux protocol appeared susccessful, but was not.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
// Someone is requesting the multistream protocol
|
||||||
|
libp2p_stream_message_free(mock_message);
|
||||||
|
mock_message = libp2p_yamux_prepare_to_send(libp2p_net_multistream_prepare_to_send(build_message("/multistream/1.0.0\n")));
|
||||||
|
// act like this is new
|
||||||
|
struct yamux_frame* frame = (struct yamux_frame*)mock_message->data;
|
||||||
|
frame->streamid = (uint32_t)1;
|
||||||
|
frame->flags = yamux_frame_syn;
|
||||||
|
encode_frame(frame);
|
||||||
|
mock_stream->read = mock_stream_read;
|
||||||
|
if (!session_context->default_stream->read(session_context->default_stream->stream_context, &result_message, 10)) {
|
||||||
|
libp2p_logger_error("test_yamux", "Unable to read multistream protocol.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
// handle the marshaling of the multistream protocol
|
||||||
|
libp2p_protocol_marshal(result_message, session_context->default_stream, protocol_handlers);
|
||||||
|
libp2p_stream_message_free(result_message);
|
||||||
|
result_message = NULL;
|
||||||
|
// now verify the results
|
||||||
|
if (session_context->default_stream->stream_type != STREAM_TYPE_YAMUX) {
|
||||||
|
libp2p_logger_error("test_yamux", "Expected stream type of %d, but received %d.\n", STREAM_TYPE_YAMUX, session_context->default_stream->stream_type);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
struct YamuxContext* yamux_context = (struct YamuxContext*)session_context->default_stream->stream_context;
|
||||||
|
if (yamux_context->channels->total != 2) {
|
||||||
|
libp2p_logger_error("test_yamux", "Identify protocol was not found.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tear down
|
||||||
|
retVal = 1;
|
||||||
|
exit:
|
||||||
|
if (session_context->default_stream != NULL)
|
||||||
|
session_context->default_stream->close(session_context->default_stream);
|
||||||
|
libp2p_protocol_handlers_shutdown(protocol_handlers);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to negotiate the identity protocol, then use it.
|
||||||
|
* This makes sure the framing is working correctly betwee identity
|
||||||
|
* and yamux
|
||||||
|
*/
|
||||||
|
int test_yamux_identity_frame() {
|
||||||
|
int retVal = 0;
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
// build the protocol handler that can handle yamux and identify protocol
|
// build the protocol handler that can handle yamux and identify protocol
|
||||||
struct Libp2pVector* protocol_handlers = libp2p_utils_vector_new(1);
|
struct Libp2pVector* protocol_handlers = libp2p_utils_vector_new(1);
|
||||||
|
@ -111,7 +241,7 @@ int test_yamux_incoming_protocol_request() {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
// Someone is requesting the identity protocol
|
// Someone is requesting the identity protocol
|
||||||
mock_stream->read = mock_identify_read_protocol;
|
mock_stream->read = mock_multistream_then_identify_read_protocol;
|
||||||
if (!session_context->default_stream->read(session_context->default_stream->stream_context, &result_message, 10)) {
|
if (!session_context->default_stream->read(session_context->default_stream->stream_context, &result_message, 10)) {
|
||||||
libp2p_logger_error("test_yamux", "Unable to read identify protocol.\n");
|
libp2p_logger_error("test_yamux", "Unable to read identify protocol.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -122,11 +252,15 @@ int test_yamux_incoming_protocol_request() {
|
||||||
result_message = NULL;
|
result_message = NULL;
|
||||||
// now verify the results
|
// now verify the results
|
||||||
struct YamuxContext* yamux_context = (struct YamuxContext*)session_context->default_stream->stream_context;
|
struct YamuxContext* yamux_context = (struct YamuxContext*)session_context->default_stream->stream_context;
|
||||||
if (yamux_context->channels->total != 1) {
|
if (yamux_context->channels->total != 2) {
|
||||||
libp2p_logger_error("test_yamux", "Identify protocol was not found.\n");
|
libp2p_logger_error("test_yamux", "Identify protocol was not found.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prepare a yamux frame that is an identity message
|
||||||
|
|
||||||
|
// send the message
|
||||||
|
|
||||||
// tear down
|
// tear down
|
||||||
retVal = 1;
|
retVal = 1;
|
||||||
exit:
|
exit:
|
||||||
|
@ -134,4 +268,5 @@ int test_yamux_incoming_protocol_request() {
|
||||||
session_context->default_stream->close(session_context->default_stream);
|
session_context->default_stream->close(session_context->default_stream);
|
||||||
libp2p_protocol_handlers_shutdown(protocol_handlers);
|
libp2p_protocol_handlers_shutdown(protocol_handlers);
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,12 +147,27 @@ ssize_t yamux_session_ping(struct yamux_session* session, uint32_t value, int po
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode an incoming message
|
* Decode an incoming message
|
||||||
* @param session the session
|
* @param context the YamuxContext or YamuxChannelContext
|
||||||
* @param incoming the incoming bytes
|
* @param incoming the incoming bytes
|
||||||
* @param incoming_size the size of the incoming bytes
|
* @param incoming_size the size of the incoming bytes
|
||||||
* @returns true(1) on success, false(0) otherwise
|
* @param return_message the results (usually the stuff after the frame)
|
||||||
|
* @returns 0 on success, negative number on error
|
||||||
*/
|
*/
|
||||||
int yamux_decode(struct YamuxChannelContext* channelContext, const uint8_t* incoming, size_t incoming_size) {
|
int yamux_decode(void* context, const uint8_t* incoming, size_t incoming_size, struct StreamMessage** return_message) {
|
||||||
|
|
||||||
|
// retrieve the yamux context
|
||||||
|
struct yamux_session* yamux_session = NULL;
|
||||||
|
struct YamuxContext* yamuxContext = NULL;
|
||||||
|
if (context == NULL)
|
||||||
|
return 0;
|
||||||
|
if ( ((char*)context)[0] == YAMUX_CONTEXT) {
|
||||||
|
yamuxContext = (struct YamuxContext*)context;
|
||||||
|
yamux_session = yamuxContext->session;
|
||||||
|
} else if ( ((char*)context)[0] == YAMUX_CHANNEL_CONTEXT) {
|
||||||
|
struct YamuxChannelContext* channelContext = (struct YamuxChannelContext*)context;
|
||||||
|
yamuxContext = channelContext->yamux_context;
|
||||||
|
yamux_session = channelContext->yamux_context->session;
|
||||||
|
}
|
||||||
// decode frame
|
// decode frame
|
||||||
struct yamux_frame f;
|
struct yamux_frame f;
|
||||||
|
|
||||||
|
@ -173,14 +188,14 @@ int yamux_decode(struct YamuxChannelContext* channelContext, const uint8_t* inco
|
||||||
case yamux_frame_ping: // ping
|
case yamux_frame_ping: // ping
|
||||||
if (f.flags & yamux_frame_syn)
|
if (f.flags & yamux_frame_syn)
|
||||||
{
|
{
|
||||||
yamux_session_ping(channelContext->yamux_context->session, f.length, 1);
|
yamux_session_ping(yamux_session, f.length, 1);
|
||||||
|
|
||||||
if (channelContext->yamux_context->session->ping_fn)
|
if (yamux_session->ping_fn)
|
||||||
channelContext->yamux_context->session->ping_fn(channelContext->yamux_context->session, f.length);
|
yamux_session->ping_fn(yamux_session, f.length);
|
||||||
}
|
}
|
||||||
else if ((f.flags & yamux_frame_ack) && channelContext->yamux_context->session->pong_fn)
|
else if ((f.flags & yamux_frame_ack) && yamux_session->pong_fn)
|
||||||
{
|
{
|
||||||
struct timespec now, dt, last = channelContext->yamux_context->session->since_ping;
|
struct timespec now, dt, last = yamux_session->since_ping;
|
||||||
if (!timespec_get(&now, TIME_UTC))
|
if (!timespec_get(&now, TIME_UTC))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
|
@ -193,32 +208,33 @@ int yamux_decode(struct YamuxChannelContext* channelContext, const uint8_t* inco
|
||||||
else
|
else
|
||||||
dt.tv_nsec = now.tv_nsec - last.tv_nsec;
|
dt.tv_nsec = now.tv_nsec - last.tv_nsec;
|
||||||
|
|
||||||
channelContext->yamux_context->session->pong_fn(channelContext->yamux_context->session, f.length, dt);
|
yamux_session->pong_fn(yamux_session, f.length, dt);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
break;
|
break;
|
||||||
case yamux_frame_go_away: // go away (hanging up)
|
case yamux_frame_go_away: // go away (hanging up)
|
||||||
channelContext->yamux_context->session->closed = 1;
|
yamux_session->closed = 1;
|
||||||
if (channelContext->yamux_context->session->go_away_fn)
|
if (yamux_session->go_away_fn)
|
||||||
channelContext->yamux_context->session->go_away_fn(channelContext->yamux_context->session, (enum yamux_error)f.length);
|
yamux_session->go_away_fn(yamux_session, (enum yamux_error)f.length);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
}
|
}
|
||||||
else { // we're handling a stream, not something at the yamux protocol level
|
else { // we're handling a stream, not something at the yamux protocol level
|
||||||
for (size_t i = 0; i < channelContext->yamux_context->session->cap_streams; ++i)
|
for (size_t i = 0; i < yamux_session->cap_streams; ++i)
|
||||||
{
|
{
|
||||||
struct yamux_session_stream* ss = &channelContext->yamux_context->session->streams[i];
|
struct yamux_session_stream* ss = &yamux_session->streams[i];
|
||||||
struct yamux_stream* s = ss->stream;
|
struct yamux_stream* s = ss->stream;
|
||||||
|
|
||||||
if (!ss->alive || s->state == yamux_stream_closed)
|
if (!ss->alive || s->state == yamux_stream_closed) // skip dead or closed streams
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (s->id == f.streamid)
|
if (s->id == f.streamid) // we have a match between the stored stream and the current stream
|
||||||
{
|
{
|
||||||
if (f.flags & yamux_frame_rst)
|
if (f.flags & yamux_frame_rst)
|
||||||
{
|
{
|
||||||
|
// close the stream
|
||||||
s->state = yamux_stream_closed;
|
s->state = yamux_stream_closed;
|
||||||
|
|
||||||
if (s->rst_fn)
|
if (s->rst_fn)
|
||||||
|
@ -228,7 +244,7 @@ int yamux_decode(struct YamuxChannelContext* channelContext, const uint8_t* inco
|
||||||
{
|
{
|
||||||
// local stream didn't initiate FIN
|
// local stream didn't initiate FIN
|
||||||
if (s->state != yamux_stream_closing)
|
if (s->state != yamux_stream_closing)
|
||||||
yamux_stream_close(channelContext);
|
yamux_stream_close(context);
|
||||||
|
|
||||||
s->state = yamux_stream_closed;
|
s->state = yamux_stream_closed;
|
||||||
|
|
||||||
|
@ -237,6 +253,7 @@ int yamux_decode(struct YamuxChannelContext* channelContext, const uint8_t* inco
|
||||||
}
|
}
|
||||||
else if (f.flags & yamux_frame_ack)
|
else if (f.flags & yamux_frame_ack)
|
||||||
{
|
{
|
||||||
|
// acknowldegement
|
||||||
if (s->state != yamux_stream_syn_sent)
|
if (s->state != yamux_stream_syn_sent)
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
|
|
||||||
|
@ -246,7 +263,7 @@ int yamux_decode(struct YamuxChannelContext* channelContext, const uint8_t* inco
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
|
|
||||||
int sz = sizeof(struct yamux_frame);
|
int sz = sizeof(struct yamux_frame);
|
||||||
ssize_t re = yamux_stream_process(s, &f, &incoming[sz], incoming_size - sz, channelContext->yamux_context->stream->parent_stream->stream_context);
|
ssize_t re = yamux_stream_process(s, &f, &incoming[sz], incoming_size - sz);
|
||||||
return (re < 0) ? re : (re + incoming_size);
|
return (re < 0) ? re : (re + incoming_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,17 +272,22 @@ int yamux_decode(struct YamuxChannelContext* channelContext, const uint8_t* inco
|
||||||
// It must not exist yet, so let's try to make it
|
// It must not exist yet, so let's try to make it
|
||||||
if (f.flags & yamux_frame_syn)
|
if (f.flags & yamux_frame_syn)
|
||||||
{
|
{
|
||||||
void* ud = NULL; // user data
|
struct StreamMessage* msg = libp2p_stream_message_new();
|
||||||
|
|
||||||
if (channelContext->yamux_context->session->get_str_ud_fn)
|
if (incoming_size > sizeof(struct yamux_frame)) {
|
||||||
ud = channelContext->yamux_context->session->get_str_ud_fn(channelContext->yamux_context->session, f.streamid);
|
msg->data_size = incoming_size - sizeof(struct yamux_frame);
|
||||||
|
msg->data = malloc(msg->data_size);
|
||||||
|
memcpy(msg->data, &incoming[sizeof(struct yamux_frame)], msg->data_size);
|
||||||
|
}
|
||||||
|
|
||||||
struct yamux_stream* st = yamux_stream_new(channelContext->yamux_context->session, f.streamid, ud);
|
struct Stream* yamuxChannelStream = yamux_channel_new(yamuxContext, f.streamid, msg);
|
||||||
|
struct YamuxChannelContext* channelContext = (struct YamuxChannelContext*)yamuxChannelStream->stream_context;
|
||||||
|
|
||||||
if (channelContext->yamux_context->session->new_stream_fn)
|
if (yamux_session->new_stream_fn)
|
||||||
channelContext->yamux_context->session->new_stream_fn(channelContext->yamux_context->session, st);
|
yamux_session->new_stream_fn(yamuxContext, yamuxContext->stream, msg);
|
||||||
|
|
||||||
st->state = yamux_stream_syn_recv;
|
channelContext->state = yamux_stream_syn_recv;
|
||||||
|
*return_message = msg;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
|
|
150
yamux/stream.c
150
yamux/stream.c
|
@ -9,57 +9,62 @@
|
||||||
#include "libp2p/net/stream.h"
|
#include "libp2p/net/stream.h"
|
||||||
#include "libp2p/yamux/frame.h"
|
#include "libp2p/yamux/frame.h"
|
||||||
#include "libp2p/yamux/stream.h"
|
#include "libp2p/yamux/stream.h"
|
||||||
|
#include "libp2p/yamux/yamux.h"
|
||||||
|
|
||||||
#define MIN(x,y) (y^((x^y)&-(x<y)))
|
#define MIN(x,y) (y^((x^y)&-(x<y)))
|
||||||
#define MAX(x,y) (x^((x^y)&-(x<y)))
|
#define MAX(x,y) (x^((x^y)&-(x<y)))
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Create a new stream
|
* Create a new stream
|
||||||
* @param session the session
|
* @param context the yamux context
|
||||||
* @param id the id (0 to set it to the next id)
|
* @param id the id (0 to set it to the next id)
|
||||||
* @Param userdata the user data
|
* @Param msg the message (probably the protocol id)
|
||||||
* @returns a new yamux_stream struct
|
* @returns a new yamux_stream struct
|
||||||
*/
|
*/
|
||||||
struct yamux_stream* yamux_stream_new(struct yamux_session* session, yamux_streamid id, void* userdata)
|
struct Stream* yamux_channel_new(struct YamuxContext* context, yamux_streamid id, struct StreamMessage* msg)
|
||||||
{
|
{
|
||||||
if (!session)
|
if (!context)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
struct yamux_session* session = context->session;
|
||||||
|
|
||||||
if (!id)
|
if (!id)
|
||||||
{
|
{
|
||||||
id = session->nextid;
|
id = session->nextid;
|
||||||
session->nextid += 2;
|
session->nextid += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct yamux_stream* st = NULL;
|
struct yamux_stream* y_stream = NULL;
|
||||||
struct yamux_session_stream* ss;
|
struct yamux_session_stream* session_stream = NULL;
|
||||||
|
|
||||||
if (session->num_streams != session->cap_streams)
|
if (session->num_streams != session->cap_streams) {
|
||||||
|
// attempt to reuse dead streams
|
||||||
for (size_t i = 0; i < session->cap_streams; ++i)
|
for (size_t i = 0; i < session->cap_streams; ++i)
|
||||||
{
|
{
|
||||||
ss = &session->streams[i];
|
session_stream = &session->streams[i];
|
||||||
|
|
||||||
if (!ss->alive)
|
if (!session_stream->alive)
|
||||||
{
|
{
|
||||||
st = ss->stream;
|
y_stream = session_stream->stream;
|
||||||
ss->alive = 1;
|
session_stream->alive = 1;
|
||||||
goto FOUND;
|
goto FOUND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (session->cap_streams == session->config->accept_backlog)
|
if (session->cap_streams == session->config->accept_backlog)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ss = &session->streams[session->cap_streams];
|
// we didn't find a dead stream, so create a new one
|
||||||
|
session_stream = &session->streams[session->cap_streams];
|
||||||
|
|
||||||
if (ss->alive)
|
if (session_stream->alive)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
session->cap_streams++;
|
session->cap_streams++;
|
||||||
|
|
||||||
ss->alive = 1;
|
session_stream->alive = 1;
|
||||||
st = ss->stream = malloc(sizeof(struct yamux_stream));
|
y_stream = session_stream->stream = malloc(sizeof(struct yamux_stream));
|
||||||
|
|
||||||
FOUND:;
|
FOUND:;
|
||||||
|
|
||||||
|
@ -72,12 +77,24 @@ FOUND:;
|
||||||
.read_fn = NULL,
|
.read_fn = NULL,
|
||||||
.fin_fn = NULL,
|
.fin_fn = NULL,
|
||||||
.rst_fn = NULL,
|
.rst_fn = NULL,
|
||||||
|
.stream = libp2p_yamux_channel_stream_new(context->stream, id)
|
||||||
.userdata = userdata
|
|
||||||
};
|
};
|
||||||
*st = nst;
|
*y_stream = nst;
|
||||||
|
|
||||||
return st;
|
if (libp2p_protocol_marshal(msg, nst.stream, context->protocol_handlers) >= 0) {
|
||||||
|
// success
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
struct Stream* channelStream = libp2p_yamux_channel_stream_new(context->stream);
|
||||||
|
struct YamuxChannelContext* channel = (struct YamuxChannelContext*)channelStream->stream_context;
|
||||||
|
channel->channel = id;
|
||||||
|
channel->child_stream = NULL;
|
||||||
|
channel->state = yamux_stream_inited;
|
||||||
|
|
||||||
|
|
||||||
|
return channelStream;
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,25 +138,40 @@ ssize_t yamux_stream_init(struct YamuxChannelContext* channel_ctx)
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Close a stream
|
* Close a stream
|
||||||
* @param stream the stream
|
* @param context the YamuxChannelContext or YamuxContext
|
||||||
* @returns the number of bytes sent
|
* @returns the number of bytes sent
|
||||||
*/
|
*/
|
||||||
ssize_t yamux_stream_close(struct YamuxChannelContext* channel_ctx)
|
ssize_t yamux_stream_close(void* context)
|
||||||
{
|
{
|
||||||
if (!channel_ctx || channel_ctx->state != yamux_stream_est || channel_ctx->closed)
|
if ( ((char*)context)[0] == YAMUX_CHANNEL_CONTEXT) {
|
||||||
return -EINVAL;
|
struct YamuxChannelContext* channel_ctx = (struct YamuxChannelContext*) context;
|
||||||
|
if (!channel_ctx || channel_ctx->state != yamux_stream_est || channel_ctx->closed)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
struct yamux_frame f = (struct yamux_frame){
|
struct yamux_frame f = (struct yamux_frame){
|
||||||
.version = YAMUX_VERSION,
|
.version = YAMUX_VERSION,
|
||||||
.type = yamux_frame_window_update,
|
.type = yamux_frame_window_update,
|
||||||
.flags = yamux_frame_fin,
|
.flags = yamux_frame_fin,
|
||||||
.streamid = channel_ctx->channel,
|
.streamid = channel_ctx->channel,
|
||||||
.length = 0
|
.length = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
channel_ctx->state = yamux_stream_closing;
|
channel_ctx->state = yamux_stream_closing;
|
||||||
|
|
||||||
return yamux_write_frame(channel_ctx->yamux_context->stream->stream_context, &f);
|
return yamux_write_frame(channel_ctx->yamux_context->stream->stream_context, &f);
|
||||||
|
} else if ( ((char*)context)[0] == YAMUX_CONTEXT) {
|
||||||
|
struct YamuxContext* ctx = (struct YamuxContext*)context;
|
||||||
|
struct yamux_frame f = (struct yamux_frame){
|
||||||
|
.version = YAMUX_VERSION,
|
||||||
|
.type = yamux_frame_window_update,
|
||||||
|
.flags = yamux_frame_fin,
|
||||||
|
.streamid = 0,
|
||||||
|
.length = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
return yamux_write_frame(ctx, &f);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,19 +197,42 @@ ssize_t yamux_stream_reset(struct YamuxChannelContext* channel_ctx)
|
||||||
return yamux_write_frame(channel_ctx->yamux_context->stream->stream_context, &f);
|
return yamux_write_frame(channel_ctx->yamux_context->stream->stream_context, &f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum yamux_frame_flags get_flags(struct YamuxChannelContext* ctx)
|
/**
|
||||||
{
|
* Retrieve the flags for this context
|
||||||
switch (ctx->state)
|
* @param context the context
|
||||||
{
|
* @returns the correct flag
|
||||||
case yamux_stream_inited:
|
*/
|
||||||
ctx->state = yamux_stream_syn_sent;
|
enum yamux_frame_flags get_flags(void* context) {
|
||||||
return yamux_frame_syn;
|
if (context == NULL)
|
||||||
case yamux_stream_syn_recv:
|
return 0;
|
||||||
ctx->state = yamux_stream_est;
|
if ( ((char*)context)[0] == YAMUX_CHANNEL_CONTEXT) {
|
||||||
return yamux_frame_ack;
|
struct YamuxChannelContext* ctx = (struct YamuxChannelContext*)context;
|
||||||
default:
|
switch (ctx->state)
|
||||||
return 0;
|
{
|
||||||
}
|
case yamux_stream_inited:
|
||||||
|
ctx->state = yamux_stream_syn_sent;
|
||||||
|
return yamux_frame_syn;
|
||||||
|
case yamux_stream_syn_recv:
|
||||||
|
ctx->state = yamux_stream_est;
|
||||||
|
return yamux_frame_ack;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else if ( ((char*)context)[0] == YAMUX_CONTEXT) {
|
||||||
|
struct YamuxContext* ctx = (struct YamuxContext*)context;
|
||||||
|
switch (ctx->state)
|
||||||
|
{
|
||||||
|
case yamux_stream_inited:
|
||||||
|
ctx->state = yamux_stream_syn_sent;
|
||||||
|
return yamux_frame_syn;
|
||||||
|
case yamux_stream_syn_recv:
|
||||||
|
ctx->state = yamux_stream_est;
|
||||||
|
return yamux_frame_ack;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -300,10 +355,9 @@ void yamux_stream_free(struct yamux_stream* stream)
|
||||||
* @param frame the frame
|
* @param frame the frame
|
||||||
* @param incoming the stream bytes (after the frame)
|
* @param incoming the stream bytes (after the frame)
|
||||||
* @param incoming_size the size of incoming
|
* @param incoming_size the size of incoming
|
||||||
* @param session_context the SessionContext
|
|
||||||
* @returns the number of bytes processed (can be zero) or negative number on error
|
* @returns the number of bytes processed (can be zero) or negative number on error
|
||||||
*/
|
*/
|
||||||
ssize_t yamux_stream_process(struct yamux_stream* stream, struct yamux_frame* frame, const uint8_t* incoming, size_t incoming_size, struct SessionContext* session_context)
|
ssize_t yamux_stream_process(struct yamux_stream* stream, struct yamux_frame* frame, const uint8_t* incoming, size_t incoming_size)
|
||||||
{
|
{
|
||||||
struct yamux_frame f = *frame;
|
struct yamux_frame f = *frame;
|
||||||
|
|
||||||
|
|
|
@ -153,7 +153,7 @@ int do_client() {
|
||||||
}
|
}
|
||||||
sess->new_stream_fn = on_new;
|
sess->new_stream_fn = on_new;
|
||||||
|
|
||||||
struct yamux_stream* strm = yamux_stream_new(sess, 0, NULL);
|
struct yamux_stream* strm = yamux_channel_new(sess, 0, NULL);
|
||||||
if (!strm)
|
if (!strm)
|
||||||
{
|
{
|
||||||
printf("yamux_new_stream() failed\n");
|
printf("yamux_new_stream() failed\n");
|
||||||
|
|
171
yamux/yamux.c
171
yamux/yamux.c
|
@ -105,7 +105,7 @@ int yamux_receive_protocol(struct YamuxContext* context) {
|
||||||
* @returns 0 if the caller should not continue looping, <0 on error, >0 on success
|
* @returns 0 if the caller should not continue looping, <0 on error, >0 on success
|
||||||
*/
|
*/
|
||||||
int yamux_handle_message(const struct StreamMessage* msg, struct Stream* stream, void* protocol_context) {
|
int yamux_handle_message(const struct StreamMessage* msg, struct Stream* stream, void* protocol_context) {
|
||||||
struct Stream* new_stream = libp2p_yamux_stream_new(stream);
|
struct Stream* new_stream = libp2p_yamux_stream_new(stream, 1, protocol_context);
|
||||||
if (new_stream == NULL)
|
if (new_stream == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
// upgrade
|
// upgrade
|
||||||
|
@ -127,7 +127,7 @@ int yamux_shutdown(void* protocol_context) {
|
||||||
struct Libp2pProtocolHandler* libp2p_yamux_build_protocol_handler(struct Libp2pVector* handlers) {
|
struct Libp2pProtocolHandler* libp2p_yamux_build_protocol_handler(struct Libp2pVector* handlers) {
|
||||||
struct Libp2pProtocolHandler* handler = libp2p_protocol_handler_new();
|
struct Libp2pProtocolHandler* handler = libp2p_protocol_handler_new();
|
||||||
if (handler != NULL) {
|
if (handler != NULL) {
|
||||||
handler->context = handler;
|
handler->context = handlers;
|
||||||
handler->CanHandle = yamux_can_handle;
|
handler->CanHandle = yamux_can_handle;
|
||||||
handler->HandleMessage = yamux_handle_message;
|
handler->HandleMessage = yamux_handle_message;
|
||||||
handler->Shutdown = yamux_shutdown;
|
handler->Shutdown = yamux_shutdown;
|
||||||
|
@ -146,8 +146,11 @@ int libp2p_yamux_close(struct Stream* stream) {
|
||||||
return 0;
|
return 0;
|
||||||
if (stream->stream_context == NULL)
|
if (stream->stream_context == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
if (stream->parent_stream->close(stream->parent_stream))
|
struct Stream* parent_stream = stream->parent_stream;
|
||||||
libp2p_yamux_stream_free(stream);
|
// this should close everything above yamux (i.e. the protocols that are riding on top of yamux)
|
||||||
|
libp2p_yamux_stream_free(stream);
|
||||||
|
// and this should close everything below
|
||||||
|
parent_stream->close(parent_stream);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,14 +183,50 @@ int libp2p_yamux_read(void* stream_context, struct StreamMessage** message, int
|
||||||
return 0;
|
return 0;
|
||||||
// TODO: This is not right. It must be sorted out.
|
// TODO: This is not right. It must be sorted out.
|
||||||
struct StreamMessage* msg = *message;
|
struct StreamMessage* msg = *message;
|
||||||
return yamux_decode(channel, msg->data, msg->data_size);
|
if (yamux_decode(channel, msg->data, msg->data_size, message) == 0)
|
||||||
|
return 1;
|
||||||
} else if (ctx != NULL) {
|
} else if (ctx != NULL) {
|
||||||
// We are still negotiating...
|
// We are still negotiating. They are probably attempting to negotiate a new protocol
|
||||||
return ctx->stream->parent_stream->read(ctx->stream->parent_stream->stream_context, message, yamux_default_timeout);
|
struct StreamMessage* incoming = NULL;
|
||||||
|
if (ctx->stream->parent_stream->read(ctx->stream->parent_stream->stream_context, &incoming, yamux_default_timeout)) {
|
||||||
|
// parse the frame
|
||||||
|
if (yamux_decode(ctx, incoming->data, incoming->data_size, message) == 0) {
|
||||||
|
libp2p_stream_message_free(incoming);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
libp2p_stream_message_free(incoming);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Prepare a new Yamux StreamMessage based on another StreamMessage
|
||||||
|
* NOTE: The frame is not encoded yet
|
||||||
|
* @param incoming the incoming message
|
||||||
|
* @returns a new StreamMessage that has a yamux_frame
|
||||||
|
*/
|
||||||
|
struct StreamMessage* libp2p_yamux_prepare_to_send(struct StreamMessage* incoming) {
|
||||||
|
struct StreamMessage* out = libp2p_stream_message_new();
|
||||||
|
if (out != NULL) {
|
||||||
|
out->data_size = sizeof(struct yamux_frame) + incoming->data_size;
|
||||||
|
out->data = (uint8_t*) malloc(out->data_size);
|
||||||
|
if (out->data == NULL) {
|
||||||
|
libp2p_stream_message_free(out);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(out->data, 0, out->data_size);
|
||||||
|
// the first part of the data is the yamux frame
|
||||||
|
// Set values in the frame, which is the first part of the outgoing message data
|
||||||
|
struct yamux_frame* frame = (struct yamux_frame*)out->data;
|
||||||
|
frame->length = incoming->data_size;
|
||||||
|
frame->type = yamux_frame_data;
|
||||||
|
frame->version = YAMUX_VERSION;
|
||||||
|
// the last part of the data is the original data
|
||||||
|
memcpy(&out->data[sizeof(struct yamux_frame)], incoming->data, incoming->data_size);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
/***
|
/***
|
||||||
* Write to the remote
|
* Write to the remote
|
||||||
* @param stream_context the context. Could be a YamuxContext or YamuxChannelContext
|
* @param stream_context the context. Could be a YamuxContext or YamuxChannelContext
|
||||||
|
@ -209,14 +248,28 @@ int libp2p_yamux_write(void* stream_context, struct StreamMessage* message) {
|
||||||
ctx = (struct YamuxContext*)stream_context;
|
ctx = (struct YamuxContext*)stream_context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx == NULL && channel == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
struct StreamMessage* outgoing_message = libp2p_yamux_prepare_to_send(message);
|
||||||
|
// now convert fame for network use
|
||||||
|
struct yamux_frame* frame = (struct yamux_frame*)outgoing_message->data;
|
||||||
|
// set a few more flags
|
||||||
|
frame->flags = get_flags(stream_context);
|
||||||
|
if (channel != NULL)
|
||||||
|
frame->streamid = channel->channel;
|
||||||
|
encode_frame(frame);
|
||||||
|
|
||||||
|
int retVal = 0;
|
||||||
if (channel != NULL && channel->channel != 0) {
|
if (channel != NULL && channel->channel != 0) {
|
||||||
// we have an established channel. Use it.
|
// we have an established channel. Use it.
|
||||||
return yamux_stream_write(channel, message->data_size, message->data);
|
retVal = channel->stream->write(channel->stream->stream_context, outgoing_message);
|
||||||
} else if (ctx != NULL) {
|
} else if (ctx != NULL) {
|
||||||
// We are still negotiating...
|
retVal = ctx->stream->parent_stream->write(ctx->stream->parent_stream, outgoing_message);
|
||||||
return ctx->stream->parent_stream->write(ctx->stream->parent_stream->stream_context, message);
|
|
||||||
}
|
}
|
||||||
return 0;
|
libp2p_stream_message_free(outgoing_message);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -241,12 +294,20 @@ int libp2p_yamux_read_raw(void* stream_context, uint8_t* buffer, int buffer_size
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct YamuxContext* libp2p_yamux_context_new() {
|
/**
|
||||||
|
* Create a new YamuxContext struct
|
||||||
|
* @param stream the parent stream
|
||||||
|
* @returns a YamuxContext
|
||||||
|
*/
|
||||||
|
struct YamuxContext* libp2p_yamux_context_new(struct Stream* stream) {
|
||||||
struct YamuxContext* ctx = (struct YamuxContext*) malloc(sizeof(struct YamuxContext));
|
struct YamuxContext* ctx = (struct YamuxContext*) malloc(sizeof(struct YamuxContext));
|
||||||
if (ctx != NULL) {
|
if (ctx != NULL) {
|
||||||
ctx->type = YAMUX_CONTEXT;
|
ctx->type = YAMUX_CONTEXT;
|
||||||
ctx->stream = NULL;
|
ctx->stream = NULL;
|
||||||
ctx->channels = libp2p_utils_vector_new(1);
|
ctx->channels = libp2p_utils_vector_new(1);
|
||||||
|
ctx->session = yamux_session_new(NULL, stream, yamux_session_server, NULL);
|
||||||
|
ctx->am_server = 0;
|
||||||
|
ctx->state = 0;
|
||||||
}
|
}
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
@ -324,14 +385,31 @@ int libp2p_yamux_handle_upgrade(struct Stream* yamux_stream, struct Stream* new_
|
||||||
return libp2p_yamux_stream_add(yamux_context, new_stream);
|
return libp2p_yamux_stream_add(yamux_context, new_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void libp2p_yamux_read_from_yamux_session(struct yamux_stream* stream, uint32_t data_len, void* data) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Internal yamux code calls this when a new stream is created
|
||||||
|
* @param context the context
|
||||||
|
* @param stream the new stream
|
||||||
|
*/
|
||||||
|
void libp2p_yamux_new_stream(struct YamuxContext* context, struct Stream* stream, struct StreamMessage* msg) {
|
||||||
|
// ok, we have the new stream structure. We now need to read what was sent.
|
||||||
|
libp2p_protocol_marshal(msg, stream, context->protocol_handlers);
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Negotiate the Yamux protocol
|
* Negotiate the Yamux protocol
|
||||||
* @param parent_stream the parent stream
|
* @param parent_stream the parent stream
|
||||||
|
* @param am_server true(1) if we are considered the server, false(0) if we are the client.
|
||||||
|
* @param protocol_handlers the protocol handlers
|
||||||
* @returns a Stream initialized and ready for yamux
|
* @returns a Stream initialized and ready for yamux
|
||||||
*/
|
*/
|
||||||
struct Stream* libp2p_yamux_stream_new(struct Stream* parent_stream) {
|
struct Stream* libp2p_yamux_stream_new(struct Stream* parent_stream, int am_server, struct Libp2pVector* protocol_handlers) {
|
||||||
struct Stream* out = libp2p_stream_new();
|
struct Stream* out = libp2p_stream_new();
|
||||||
if (out != NULL) {
|
if (out != NULL) {
|
||||||
|
out->stream_type = STREAM_TYPE_YAMUX;
|
||||||
out->parent_stream = parent_stream;
|
out->parent_stream = parent_stream;
|
||||||
out->close = libp2p_yamux_close;
|
out->close = libp2p_yamux_close;
|
||||||
out->read = libp2p_yamux_read;
|
out->read = libp2p_yamux_read;
|
||||||
|
@ -341,13 +419,16 @@ struct Stream* libp2p_yamux_stream_new(struct Stream* parent_stream) {
|
||||||
out->handle_upgrade = libp2p_yamux_handle_upgrade;
|
out->handle_upgrade = libp2p_yamux_handle_upgrade;
|
||||||
out->address = parent_stream->address;
|
out->address = parent_stream->address;
|
||||||
// build YamuxContext
|
// build YamuxContext
|
||||||
struct YamuxContext* ctx = libp2p_yamux_context_new();
|
struct YamuxContext* ctx = libp2p_yamux_context_new(out);
|
||||||
if (ctx == NULL) {
|
if (ctx == NULL) {
|
||||||
libp2p_yamux_stream_free(out);
|
libp2p_yamux_stream_free(out);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
ctx->session->new_stream_fn = libp2p_yamux_new_stream;
|
||||||
out->stream_context = ctx;
|
out->stream_context = ctx;
|
||||||
ctx->stream = out;
|
ctx->stream = out;
|
||||||
|
ctx->am_server = am_server;
|
||||||
|
ctx->protocol_handlers = protocol_handlers;
|
||||||
// attempt to negotiate yamux protocol
|
// attempt to negotiate yamux protocol
|
||||||
if (!libp2p_yamux_negotiate(ctx)) {
|
if (!libp2p_yamux_negotiate(ctx)) {
|
||||||
libp2p_yamux_stream_free(out);
|
libp2p_yamux_stream_free(out);
|
||||||
|
@ -391,6 +472,8 @@ void libp2p_yamux_context_free(struct YamuxContext* ctx) {
|
||||||
}
|
}
|
||||||
libp2p_utils_vector_free(ctx->channels);
|
libp2p_utils_vector_free(ctx->channels);
|
||||||
}
|
}
|
||||||
|
if (ctx->session != NULL)
|
||||||
|
yamux_session_free(ctx->session);
|
||||||
free(ctx);
|
free(ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -419,34 +502,54 @@ int libp2p_yamux_channel_null_close(struct Stream* stream) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a stream that has a "YamuxChannelContext" related to this yamux protocol
|
* Create a stream that has a "YamuxChannelContext" related to this yamux protocol
|
||||||
* NOTE: This "wraps" the incoming stream, so that the returned stream is the parent
|
* NOTE: If incoming_stream is not of the Yamux protocol, this "wraps" the incoming
|
||||||
* of the incoming_stream
|
* stream, so that the returned stream is the parent of the incoming_stream. If the
|
||||||
|
* incoming stream is of the yamux protocol, the YamuxChannelContext.child_stream
|
||||||
|
* will be NULL, awaiting an upgrade to fill it in.
|
||||||
* @param incoming_stream the stream of the new protocol
|
* @param incoming_stream the stream of the new protocol
|
||||||
* @returns a new Stream that has a YamuxChannelContext, and incoming_stream->parent_stream is set to this stream
|
* @param channelNumber the channel number (0 if unknown)
|
||||||
|
* @returns a new Stream that has a YamuxChannelContext
|
||||||
*/
|
*/
|
||||||
struct Stream* libp2p_yamux_channel_stream_new(struct Stream* incoming_stream) {
|
struct Stream* libp2p_yamux_channel_stream_new(struct Stream* incoming_stream, int channelNumber) {
|
||||||
struct Stream* out = libp2p_stream_new();
|
struct Stream* out = libp2p_stream_new();
|
||||||
if (out != NULL) {
|
if (out != NULL) {
|
||||||
|
int isYamux = 0;
|
||||||
|
char first_char = ((uint8_t*)incoming_stream->stream_context)[0];
|
||||||
|
if (first_char == YAMUX_CONTEXT)
|
||||||
|
isYamux = 1;
|
||||||
|
out->stream_type = STREAM_TYPE_YAMUX;
|
||||||
out->address = incoming_stream->address;
|
out->address = incoming_stream->address;
|
||||||
// don't allow the incoming_stream to close the channel
|
// don't allow the incoming_stream to close the channel
|
||||||
out->close = libp2p_yamux_channel_null_close;
|
out->close = libp2p_yamux_channel_null_close;
|
||||||
out->parent_stream = incoming_stream->parent_stream;
|
|
||||||
out->peek = incoming_stream->parent_stream->peek;
|
|
||||||
out->read = incoming_stream->parent_stream->read;
|
|
||||||
out->read_raw = incoming_stream->parent_stream->read_raw;
|
|
||||||
out->socket_mutex = incoming_stream->parent_stream->socket_mutex;
|
|
||||||
struct YamuxChannelContext* ctx = (struct YamuxChannelContext*)malloc(sizeof(struct YamuxChannelContext));
|
struct YamuxChannelContext* ctx = (struct YamuxChannelContext*)malloc(sizeof(struct YamuxChannelContext));
|
||||||
ctx->channel = 0;
|
if (!isYamux) {
|
||||||
|
out->parent_stream = incoming_stream->parent_stream;
|
||||||
|
out->peek = incoming_stream->parent_stream->peek;
|
||||||
|
out->read = incoming_stream->parent_stream->read;
|
||||||
|
out->read_raw = incoming_stream->parent_stream->read_raw;
|
||||||
|
out->write = incoming_stream->parent_stream->write;
|
||||||
|
out->socket_mutex = incoming_stream->parent_stream->socket_mutex;
|
||||||
|
ctx->yamux_context = incoming_stream->parent_stream->stream_context;
|
||||||
|
ctx->child_stream = incoming_stream;
|
||||||
|
// this does the wrap
|
||||||
|
incoming_stream->parent_stream = out;
|
||||||
|
} else {
|
||||||
|
out->parent_stream = incoming_stream;
|
||||||
|
out->peek = incoming_stream->peek;
|
||||||
|
out->read = incoming_stream->read;
|
||||||
|
out->read_raw = incoming_stream->read_raw;
|
||||||
|
out->write = incoming_stream->write;
|
||||||
|
out->socket_mutex = incoming_stream->socket_mutex;
|
||||||
|
ctx->yamux_context = incoming_stream->stream_context;
|
||||||
|
ctx->child_stream = NULL;
|
||||||
|
}
|
||||||
|
ctx->channel = channelNumber;
|
||||||
ctx->closed = 0;
|
ctx->closed = 0;
|
||||||
ctx->state = 0;
|
ctx->state = 0;
|
||||||
ctx->window_size = 0;
|
ctx->window_size = 0;
|
||||||
ctx->type = YAMUX_CHANNEL_CONTEXT;
|
ctx->type = YAMUX_CHANNEL_CONTEXT;
|
||||||
ctx->yamux_context = incoming_stream->parent_stream->stream_context;
|
|
||||||
ctx->stream = out;
|
ctx->stream = out;
|
||||||
ctx->child_stream = incoming_stream;
|
|
||||||
out->stream_context = ctx;
|
out->stream_context = ctx;
|
||||||
out->write = incoming_stream->parent_stream->write;
|
|
||||||
incoming_stream->parent_stream = out;
|
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -461,12 +564,22 @@ int libp2p_yamux_stream_add(struct YamuxContext* ctx, struct Stream* stream) {
|
||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
// wrap the new stream in a YamuxChannelContext
|
// wrap the new stream in a YamuxChannelContext
|
||||||
struct Stream* channel_stream = libp2p_yamux_channel_stream_new(stream);
|
struct Stream* channel_stream = libp2p_yamux_channel_stream_new(stream, 0);
|
||||||
if (channel_stream == NULL)
|
if (channel_stream == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
struct YamuxChannelContext* channel_context = (struct YamuxChannelContext*)channel_stream->stream_context;
|
struct YamuxChannelContext* channel_context = (struct YamuxChannelContext*)channel_stream->stream_context;
|
||||||
// the negotiation was successful. Add it to the list of channels that we have
|
// the negotiation was successful. Add it to the list of channels that we have
|
||||||
int itemNo = libp2p_utils_vector_add(ctx->channels, channel_stream);
|
int itemNo = libp2p_utils_vector_add(ctx->channels, channel_stream);
|
||||||
|
// There are 2 streams for each protocol. A server has the even numbered streams, the
|
||||||
|
// client the odd number streams. If we are the server, we need to kick off the
|
||||||
|
// process to add a stream of the same type.
|
||||||
channel_context->channel = itemNo;
|
channel_context->channel = itemNo;
|
||||||
|
if (ctx->am_server && itemNo % 2 != 0) {
|
||||||
|
// we're the server, and they have a negotiated a new protocol.
|
||||||
|
// negotiate a stream for us to talk to them.
|
||||||
|
struct Stream* yamux_stream = stream->parent_stream->parent_stream;
|
||||||
|
struct Stream* server_to_client_stream = stream->negotiate(yamux_stream);
|
||||||
|
libp2p_yamux_stream_add(ctx, server_to_client_stream);
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue