1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
|
//
// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2018 Devolutions <info@devolutions.net>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
// file was obtained (LICENSE.txt). A copy of the license may also be
// found online at https://opensource.org/licenses/MIT.
//
#ifndef PROTOCOL_SP_TRANSPORT_H
#define PROTOCOL_SP_TRANSPORT_H
#include "core/defs.h"
#include "core/list.h"
#include "core/options.h"
// Endpoint operations are called by the socket in a
// protocol-independent fashion. The socket makes individual calls,
// which are expected to block if appropriate (except for destroy), or
// run asynchronously if an aio is provided. Endpoints are unable to
// call back into the socket, to prevent recusive entry and deadlock.
//
// For a given endpoint, the framework holds a lock so that each entry
// point is run exclusively of the others. (Transports must still guard
// against any asynchronous operations they manage themselves, though.)
struct nni_sp_dialer_ops {
// d_size is the size of transport specific data allocated
// to the listener.
size_t d_size;
// d_init creates a vanilla dialer. The value created is
// used for the first argument for all other dialer functions.
nng_err (*d_init)(void *, nng_url *, nni_dialer *);
// d_fini frees the resources associated with the dialer.
// The dialer will already have been closed.
void (*d_fini)(void *);
// d_connect establishes a connection. It can return errors
// NNG_EACCESS, NNG_ECONNREFUSED, NNG_EBADADDR,
// NNG_ECONNFAILED, NNG_ETIMEDOUT, and NNG_EPROTO.
void (*d_connect)(void *, nni_aio *);
// d_close stops the dialer from operating altogether.
// It is nonblocking.
void (*d_close)(void *);
// d_stop is close, but also waits for the operation to be
// be fully stopped.
void (*d_stop)(void *);
// d_getopt is used to obtain an option.
nng_err (*d_getopt)(void *, const char *, void *, size_t *, nni_type);
// d_setopt is used to set or change an option.
nng_err (*d_setopt)(
void *, const char *, const void *, size_t, nni_type);
// d_get_tls is used to get the TLS configuration to use for dialing.
// This may be NULL if the dialer does not support TLS.
nng_err (*d_get_tls)(void *, nng_tls_config **);
// d_set_tls is used to set the TLS configruation to use for the
// dialer. This may be NULL if this dialer does not support TLS.
nng_err (*d_set_tls)(void *, nng_tls_config *);
// d_options is an array of dialer options. The final
// element must have a NULL name. If this member is NULL, then
// no dialer specific options are available.
nni_option *d_options;
};
struct nni_sp_listener_ops {
// l_size is the size of transport specific data allocated
// to the listener.
size_t l_size;
// l_init creates a vanilla listener. The value created is
// used for the first argument for all other listener functions.
nng_err (*l_init)(void *, nng_url *, nni_listener *);
// l_fini frees the resources associated with the listener.
// The listener will already have been closed.
void (*l_fini)(void *);
// l_bind just does the bind() and listen() work,
// reserving the address but not creating any connections.
// It should return NNG_EADDRINUSE if the address is already
// taken. It can also return NNG_EBADADDR for an unsuitable
// address, or NNG_EACCESS for permission problems. The transport
// should update the url if it has changed (e.g. due to converting
// from port 0 to a real port.)
nng_err (*l_bind)(void *, nng_url *);
// l_accept accepts an inbound connection.
void (*l_accept)(void *, nni_aio *);
// l_close stops the listener from operating altogether.
// It is nonblocking.
void (*l_close)(void *);
// l_stop is close, but also waits for the operation to be
// be fully stopped.
void (*l_stop)(void *);
// l_getopt is used to obtain an option.
nng_err (*l_getopt)(void *, const char *, void *, size_t *, nni_type);
// l_setopt is used to set or change an option.
nng_err (*l_setopt)(
void *, const char *, const void *, size_t, nni_type);
// l_get_tls is used to get the TLS configuration to use for listening.
// This may be NULL if the listener does not support TLS.
nng_err (*l_get_tls)(void *, nng_tls_config **);
// l_set_tls is used to set the TLS configruation to use for listening.
// This may be NULL if this listener does not support TLS.
nng_err (*l_set_tls)(void *, nng_tls_config *);
// l_set_security_descriptor is used exclusively on Windows.
nng_err (*l_set_security_descriptor)(void *, void *);
// l_options is an array of listener options. The final
// element must have a NULL name. If this member is NULL, then
// no dialer specific options are available.
nni_option *l_options;
};
// Pipe operations are entry points called by the socket. These may be
// called with socket locks held, so it is forbidden for the transport
// to call back into the socket at this point. (Which is one reason
// pointers back to socket or even enclosing pipe state, are not
// provided.)
struct nni_sp_pipe_ops {
// p_init initializes the pipe data structures. The main
// purpose of this is so that the pipe will see the upper
// layer nni_pipe and get a chance to register stats and such.
// size_t p_size;
// p_size returns the size of the transport data needed for a pipe.
// This allows for dynamic registration of context size to allow for
// different tunings or different runtimes.
size_t (*p_size)(void);
// p_init initializes the transport's pipe data structure.
// The pipe MUST be left in a state that p_fini can be safely
// called on it, even if it does not succeed. (The upper layers
// will call p_fini as part of the cleanup of a failure.)
// This function should not acquire any locks.
int (*p_init)(void *, nni_pipe *);
// p_fini destroys the pipe. This should clean up all local
// resources, including closing files and freeing memory, used
// by the pipe. After this call returns, the system will not
// make further calls on the same pipe. This call should not block.
void (*p_fini)(void *);
// p_stop stops the pipe, waiting for any callbacks that are
// outstanding to complete. This is done before tearing down
// resources with p_fini. Unlike p_fini, p_stop may block.
void (*p_stop)(void *);
// p_send queues the message for transmit. If this fails,
// then the caller may try again with the same message (or free
// it). If the call succeeds, then the transport has taken
// ownership of the message, and the caller may not use it
// again. The transport will have the responsibility to free
// the message (nng_msg_free()) when it is finished with it.
void (*p_send)(void *, nni_aio *);
// p_recv schedules a message receive. This will be performed
// even for cases where no data is expected, to allow detection
// of a remote disconnect.
void (*p_recv)(void *, nni_aio *);
// p_close closes the pipe. Further recv or send operations
// should return back NNG_ECLOSED.
void (*p_close)(void *);
// p_peer returns the peer protocol. This may arrive in
// whatever transport specific manner is appropriate.
uint16_t (*p_peer)(void *);
// p_getopt is used to obtain an option. Pipes don't implement
// option setting.
nng_err (*p_getopt)(void *, const char *, void *, size_t *, nni_type);
};
// Transport implementation details. Transports must implement the
// interfaces in this file.
struct nni_sp_tran {
// tran_link is for framework use only - it must initialized
// to zero before registration.
nni_list_node tran_link;
// tran_scheme is the transport scheme, such as "tcp" or "inproc".
const char *tran_scheme;
// tran_dialer links our dialer-specific operations.
const nni_sp_dialer_ops *tran_dialer;
// tran_listener links our listener-specific operations.
const nni_sp_listener_ops *tran_listener;
// tran_pipe links our pipe-specific operations.
const nni_sp_pipe_ops *tran_pipe;
// tran_init is called once during library initialization.
void (*tran_init)(void);
// tran_fini is called during library shutdown.
// It should release any global resources.
void (*tran_fini)(void);
};
// These APIs are used by the framework internally, and not for use by
// transport implementations.
extern nni_sp_tran *nni_sp_tran_find(const char *);
extern void nni_sp_tran_sys_init(void);
extern void nni_sp_tran_sys_fini(void);
extern void nni_sp_tran_register(nni_sp_tran *);
#endif // PROTOCOL_SP_TRANSPORT_H
|