/src/haproxy/src/mux_spop.c
Line | Count | Source |
1 | | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
2 | | |
3 | | #include <import/ist.h> |
4 | | #include <import/eb32tree.h> |
5 | | |
6 | | #include <haproxy/api.h> |
7 | | #include <haproxy/cfgparse.h> |
8 | | #include <haproxy/connection.h> |
9 | | #include <haproxy/dynbuf.h> |
10 | | #include <haproxy/list.h> |
11 | | #include <haproxy/mux_spop-t.h> |
12 | | #include <haproxy/net_helper.h> |
13 | | #include <haproxy/proxy.h> |
14 | | #include <haproxy/spoe.h> |
15 | | #include <haproxy/session.h> |
16 | | #include <haproxy/stconn.h> |
17 | | #include <haproxy/sym.h> |
18 | | #include <haproxy/task.h> |
19 | | #include <haproxy/trace.h> |
20 | | |
21 | | /* 32 buffers: one for the ring's root, rest for the mbuf itself */ |
22 | | #define SPOP_C_MBUF_CNT 32 |
23 | | |
24 | | |
25 | | /* SPOP connection descriptor */ |
26 | | struct spop_conn { |
27 | | struct connection *conn; |
28 | | |
29 | | enum spop_conn_st state; /* SPOP connection state */ |
30 | | enum spop_error errcode; /* SPOP error code (SPOP_ERR_*) */ |
31 | | uint32_t streams_limit; /* maximum number of concurrent streams the peer supports */ |
32 | | uint32_t max_id; /* highest ID known on this connection, <0 before HELLO handshake */ |
33 | | uint32_t flags; /* Connection flags: SPOP_CF_* */ |
34 | | |
35 | | int32_t dsi; /* dmux stream ID (<0 = idle ) */ |
36 | | uint32_t dfi; /* dmux frame ID (if dsi >= 0) */ |
37 | | uint32_t dfl; /* demux frame length (if dsi >= 0) */ |
38 | | uint32_t dff; /* demux frame flags */ |
39 | | uint8_t dft; /* demux frame type (if dsi >= 0) */ |
40 | | |
41 | | struct buffer dbuf; /* demux buffer */ |
42 | | struct buffer mbuf[SPOP_C_MBUF_CNT]; /* mux buffers (ring) */ |
43 | | |
44 | | int timeout; /* idle timeout duration in ticks */ |
45 | | int shut_timeout; /* idle timeout duration in ticks after shutdown */ |
46 | | |
47 | | unsigned int max_frame_size; /* the negotiated max-frame-size value */ |
48 | | unsigned int nb_streams; /* number of streams in the tree */ |
49 | | unsigned int nb_sc; /* number of attached stream connectors */ |
50 | | unsigned int nb_reserved; /* number of reserved streams */ |
51 | | unsigned int stream_cnt; /* total number of streams seen */ |
52 | | |
53 | | uint32_t term_evts_log; /* Termination events log: first 4 events reported */ |
54 | | |
55 | | struct proxy *proxy; /* the proxy this connection was created for */ |
56 | | struct spoe_agent *agent; /* SPOE agent used by this mux */ |
57 | | struct task *task; /* timeout management task */ |
58 | | struct eb_root streams_by_id; /* all active streams by their ID */ |
59 | | |
60 | | struct list send_list; /* list of blocked streams requesting to send */ |
61 | | |
62 | | struct buffer_wait buf_wait; /* Wait list for buffer allocation */ |
63 | | struct wait_event wait_event; /* To be used if we're waiting for I/Os */ |
64 | | }; |
65 | | |
66 | | /* SPOP stream descriptor */ |
67 | | struct spop_strm { |
68 | | struct sedesc *sd; |
69 | | struct session *sess; |
70 | | struct spop_conn *spop_conn; |
71 | | |
72 | | int32_t id; /* stream ID */ |
73 | | uint32_t fid; /* frame ID */ |
74 | | |
75 | | uint32_t flags; /* Connection flags: SPOP_SF_* */ |
76 | | enum spop_error errcode; /* SPOP error code (SPOP_ERR_*) */ |
77 | | enum spop_strm_st state; /* SPOP stream state */ |
78 | | |
79 | | struct buffer rxbuf; /* receive buffer, always valid (buf_empty or real buffer) */ |
80 | | |
81 | | struct eb32_node by_id; /* place in spop_conn's streams_by_id */ |
82 | | struct wait_event *subs; /* Address of the wait_event the stream connector associated is waiting on */ |
83 | | struct list list; /* To be used when adding in spop_conn->send_list */ |
84 | | }; |
85 | | |
86 | | /* descriptor for an SPOP frame header */ |
87 | | struct spop_frame_header { |
88 | | uint32_t len; /* length, host order */ |
89 | | uint32_t flags; /* frame flags */ |
90 | | uint64_t sid; /* stream id, host order */ |
91 | | uint64_t fid; /* frame id, host order */ |
92 | | uint8_t type; /* frame type */ |
93 | | }; |
94 | | |
95 | | /* The event representation is split like this : |
96 | | * spop_conn - internal SPOP connection |
97 | | * spop_strm - internal SPOP stream |
98 | | * strm - application layer |
99 | | * rx - data receipt |
100 | | * tx - data transmission |
101 | | */ |
102 | | static const struct trace_event spop_trace_events[] __maybe_unused = { |
103 | | #define SPOP_EV_SPOP_CONN_NEW (1ULL << 0) |
104 | | { .mask = SPOP_EV_SPOP_CONN_NEW, .name = "spop_conn_new", .desc = "new SPOP connection" }, |
105 | 0 | #define SPOP_EV_SPOP_CONN_RECV (1ULL << 1) |
106 | | { .mask = SPOP_EV_SPOP_CONN_RECV, .name = "spop_conn_recv", .desc = "Rx on SPOP connection" }, |
107 | 0 | #define SPOP_EV_SPOP_CONN_SEND (1ULL << 2) |
108 | | { .mask = SPOP_EV_SPOP_CONN_SEND, .name = "spop_conn_send", .desc = "Tx on SPOP connection" }, |
109 | | #define SPOP_EV_SPOP_CONN_BLK (1ULL << 3) |
110 | | { .mask = SPOP_EV_SPOP_CONN_BLK, .name = "spop_conn_blk", .desc = "SPOP connection blocked" }, |
111 | | #define SPOP_EV_SPOP_CONN_WAKE (1ULL << 4) |
112 | | { .mask = SPOP_EV_SPOP_CONN_WAKE, .name = "spop_conn_wake", .desc = "SPOP connection woken up" }, |
113 | | #define SPOP_EV_SPOP_CONN_END (1ULL << 5) |
114 | | { .mask = SPOP_EV_SPOP_CONN_END, .name = "spop_conn_end", .desc = "SPOP connection terminated" }, |
115 | | #define SPOP_EV_SPOP_CONN_ERR (1ULL << 6) |
116 | | { .mask = SPOP_EV_SPOP_CONN_ERR, .name = "spop_conn_err", .desc = "error on SPOP connection" }, |
117 | | |
118 | | #define SPOP_EV_RX_FHDR (1ULL << 7) |
119 | | { .mask = SPOP_EV_RX_FHDR, .name = "rx_fhdr", .desc = "SPOP frame header received" }, |
120 | 0 | #define SPOP_EV_RX_FRAME (1ULL << 8) |
121 | | { .mask = SPOP_EV_RX_FRAME, .name = "rx_frame", .desc = "receipt of any SPOP frame" }, |
122 | | #define SPOP_EV_RX_EOI (1ULL << 9) |
123 | | { .mask = SPOP_EV_RX_EOI, .name = "rx_eoi", .desc = "receipt of end of SPOP input" }, |
124 | | #define SPOP_EV_RX_HELLO (1ULL << 10) |
125 | | { .mask = SPOP_EV_RX_HELLO, .name = "rx_hello", .desc = "receipt of SPOP AGENT HELLO frame" }, |
126 | | #define SPOP_EV_RX_ACK (1ULL << 11) |
127 | | { .mask = SPOP_EV_RX_ACK, .name = "rx_ack", .desc = "receipt of SPOP AGENT ACK frame" }, |
128 | | #define SPOP_EV_RX_DISCO (1ULL << 12) |
129 | | { .mask = SPOP_EV_RX_DISCO, .name = "rx_disconnect", .desc = "receipt of SPOP AGENT DISCONNECT frame" }, |
130 | | |
131 | 0 | #define SPOP_EV_TX_FRAME (1ULL << 13) |
132 | | { .mask = SPOP_EV_TX_FRAME, .name = "tx_frame", .desc = "transmission of any SPOP frame" }, |
133 | | #define SPOP_EV_TX_EOI (1ULL << 14) |
134 | | { .mask = SPOP_EV_TX_EOI, .name = "tx_eoi", .desc = "transmission of SPOP end of input" }, |
135 | | #define SPOP_EV_TX_HELLO (1ULL << 15) |
136 | | { .mask = SPOP_EV_TX_HELLO, .name = "tx_hello", .desc = "transmission of SPOP HAPROXY HELLO frame" }, |
137 | | #define SPOP_EV_TX_NOTIFY (1ULL << 16) |
138 | | { .mask = SPOP_EV_TX_NOTIFY, .name = "tx_notify", .desc = "transmission of SPOP HAPROXY NOTIFY frame" }, |
139 | | #define SPOP_EV_TX_DISCO (1ULL << 17) |
140 | | { .mask = SPOP_EV_TX_DISCO, .name = "tx_disconnect", .desc = "transmission of SPOP HAPROXY DISCONNECT frame" }, |
141 | | |
142 | | #define SPOP_EV_SPOP_STRM_NEW (1ULL << 18) |
143 | | { .mask = SPOP_EV_SPOP_STRM_NEW, .name = "spop_strm_new", .desc = "new SPOP stream" }, |
144 | | #define SPOP_EV_SPOP_STRM_BLK (1ULL << 19) |
145 | | { .mask = SPOP_EV_SPOP_STRM_BLK, .name = "spop_strm_blk", .desc = "SPOP stream blocked" }, |
146 | | #define SPOP_EV_SPOP_STRM_END (1ULL << 20) |
147 | | { .mask = SPOP_EV_SPOP_STRM_END, .name = "spop_strm_end", .desc = "SPOP stream terminated" }, |
148 | | #define SPOP_EV_SPOP_STRM_ERR (1ULL << 21) |
149 | | { .mask = SPOP_EV_SPOP_STRM_ERR, .name = "spop_strm_err", .desc = "error on SPOP stream" }, |
150 | | |
151 | | #define SPOP_EV_STRM_NEW (1ULL << 22) |
152 | | { .mask = SPOP_EV_STRM_NEW, .name = "strm_new", .desc = "app-layer stream creation" }, |
153 | 0 | #define SPOP_EV_STRM_RECV (1ULL << 23) |
154 | | { .mask = SPOP_EV_STRM_RECV, .name = "strm_recv", .desc = "receiving data for stream" }, |
155 | | #define SPOP_EV_STRM_SEND (1ULL << 24) |
156 | | { .mask = SPOP_EV_STRM_SEND, .name = "strm_send", .desc = "sending data for stream" }, |
157 | | #define SPOP_EV_STRM_FULL (1ULL << 25) |
158 | | { .mask = SPOP_EV_STRM_FULL, .name = "strm_full", .desc = "stream buffer full" }, |
159 | | #define SPOP_EV_STRM_WAKE (1ULL << 26) |
160 | | { .mask = SPOP_EV_STRM_WAKE, .name = "strm_wake", .desc = "stream woken up" }, |
161 | | #define SPOP_EV_STRM_SHUT (1ULL << 27) |
162 | | { .mask = SPOP_EV_STRM_SHUT, .name = "strm_shut", .desc = "stream shutdown" }, |
163 | | #define SPOP_EV_STRM_END (1ULL << 28) |
164 | | { .mask = SPOP_EV_STRM_END, .name = "strm_end", .desc = "detaching app-layer stream" }, |
165 | | #define SPOP_EV_STRM_ERR (1ULL << 29) |
166 | | { .mask = SPOP_EV_STRM_ERR, .name = "strm_err", .desc = "stream error" }, |
167 | | |
168 | | { } |
169 | | }; |
170 | | |
171 | | #if defined(USE_TRACE) |
172 | | |
173 | | /* trace source and events */ |
174 | | static void spop_trace(enum trace_level level, uint64_t mask, |
175 | | const struct trace_source *src, |
176 | | const struct ist where, const struct ist func, |
177 | | const void *a1, const void *a2, const void *a3, const void *a4); |
178 | | |
179 | | static const struct name_desc spop_trace_lockon_args[4] = { |
180 | | /* arg1 */ { /* already used by the connection */ }, |
181 | | /* arg2 */ { .name="spop_strm", .desc="SPOP stream" }, |
182 | | /* arg3 */ { }, |
183 | | /* arg4 */ { } |
184 | | }; |
185 | | |
186 | | |
187 | | static const struct name_desc spop_trace_decoding[] = { |
188 | 0 | #define SPOP_VERB_CLEAN 1 |
189 | | { .name="clean", .desc="only user-friendly stuff, generally suitable for level \"user\"" }, |
190 | 0 | #define SPOP_VERB_MINIMAL 2 |
191 | | { .name="minimal", .desc="report only spop_conn/spop_strm state and flags, no real decoding" }, |
192 | | #define SPOP_VERB_SIMPLE 3 |
193 | | { .name="simple", .desc="add request/response status line or htx info when available" }, |
194 | 0 | #define SPOP_VERB_ADVANCED 4 |
195 | | { .name="advanced", .desc="add header fields or record decoding when available" }, |
196 | 0 | #define SPOP_VERB_COMPLETE 5 |
197 | | { .name="complete", .desc="add full data dump when available" }, |
198 | | { /* end */ } |
199 | | }; |
200 | | |
201 | | static struct trace_source trace_spop __read_mostly = { |
202 | | .name = IST("spop"), |
203 | | .desc = "SPOP multiplexer", |
204 | | .arg_def = TRC_ARG1_CONN, // TRACE()'s first argument is always a connection |
205 | | .default_cb = spop_trace, |
206 | | .known_events = spop_trace_events, |
207 | | .lockon_args = spop_trace_lockon_args, |
208 | | .decoding = spop_trace_decoding, |
209 | | .report_events = ~0, // report everything by default |
210 | | }; |
211 | | |
212 | | #define TRACE_SOURCE &trace_spop |
213 | | INITCALL1(STG_REGISTER, trace_register_source, TRACE_SOURCE); |
214 | | |
215 | | #endif /* USE_TRACE */ |
216 | | |
217 | | /* SPOP connection and stream pools */ |
218 | | DECLARE_STATIC_TYPED_POOL(pool_head_spop_conn, "spop_conn", struct spop_conn); |
219 | | DECLARE_STATIC_TYPED_POOL(pool_head_spop_strm, "spop_strm", struct spop_strm); |
220 | | |
221 | | |
222 | | const struct ist spop_err_reasons[SPOP_ERR_ENTRIES] = { |
223 | | [SPOP_ERR_NONE] = IST("normal"), |
224 | | [SPOP_ERR_IO] = IST("I/O error"), |
225 | | [SPOP_ERR_TOUT] = IST("a timeout occurred"), |
226 | | [SPOP_ERR_TOO_BIG] = IST("frame is too big"), |
227 | | [SPOP_ERR_INVALID] = IST("invalid frame received"), |
228 | | [SPOP_ERR_NO_VSN] = IST("version value not found"), |
229 | | [SPOP_ERR_NO_FRAME_SIZE] = IST("max-frame-size value not found"), |
230 | | [SPOP_ERR_NO_CAP] = IST("capabilities value not found"), |
231 | | [SPOP_ERR_BAD_VSN] = IST("unsupported version"), |
232 | | [SPOP_ERR_BAD_FRAME_SIZE] = IST("max-frame-size too big or too small"), |
233 | | [SPOP_ERR_FRAG_NOT_SUPPORTED] = IST("fragmentation not supported"), |
234 | | [SPOP_ERR_INTERLACED_FRAMES] = IST("invalid interlaced frames"), |
235 | | [SPOP_ERR_FRAMEID_NOTFOUND] = IST("frame-id not found"), |
236 | | [SPOP_ERR_RES] = IST("resource allocation error"), |
237 | | [SPOP_ERR_UNKNOWN] = IST("an unknown error occurred"), |
238 | | }; |
239 | | |
240 | | |
241 | | /* Helper to get static string length, excluding the terminating null byte */ |
242 | 0 | #define SPOP_SLEN(str) (sizeof(str)-1) |
243 | | |
244 | | /* Predefined key used in HELLO/DISCONNECT frames */ |
245 | 0 | #define SPOP_SUPPORTED_VERSIONS_KEY "supported-versions" |
246 | 0 | #define SPOP_VERSION_KEY "version" |
247 | 0 | #define SPOP_MAX_FRAME_SIZE_KEY "max-frame-size" |
248 | 0 | #define SPOP_CAPABILITIES_KEY "capabilities" |
249 | 0 | #define SPOP_ENGINE_ID_KEY "engine-id" |
250 | 0 | #define SPOP_HEALTHCHECK_KEY "healthcheck" |
251 | 0 | #define SPOP_STATUS_CODE_KEY "status-code" |
252 | 0 | #define SPOP_MSG_KEY "message" |
253 | | |
254 | | /* All supported versions */ |
255 | | const struct spop_version spop_supported_versions[] = { |
256 | | /* 1.0 is now unsupported because of a bug about frame's flags*/ |
257 | | {"2.0", 2000, 2000}, |
258 | | {NULL, 0, 0} |
259 | | }; |
260 | | |
261 | | /* Comma-separated list of supported versions */ |
262 | 0 | #define SPOP_SUPPORTED_VERSIONS_VAL "2.0" |
263 | | |
264 | | static struct task *spop_timeout_task(struct task *t, void *context, unsigned int state); |
265 | | static int spop_process(struct spop_conn *spop_conn); |
266 | | static struct task *spop_io_cb(struct task *t, void *ctx, unsigned int state); |
267 | | static inline struct spop_strm *spop_conn_st_by_id(struct spop_conn *spop_conn, int id); |
268 | | static struct spop_strm *spop_stconn_new(struct spop_conn *spop_conn, struct stconn *sc, struct session *sess); |
269 | | static void spop_strm_notify_recv(struct spop_strm *spop_strm); |
270 | | static void spop_strm_notify_send(struct spop_strm *spop_strm); |
271 | | static void spop_strm_alert(struct spop_strm *spop_strm); |
272 | | static inline void spop_remove_from_list(struct spop_strm *spop_strm); |
273 | | static inline void spop_conn_restart_reading(const struct spop_conn *spop_conn, int consider_buffer); |
274 | | static int spop_dump_spop_conn_info(struct buffer *msg, struct spop_conn *spop_conn, const char *pfx); |
275 | | static int spop_dump_spop_strm_info(struct buffer *msg, const struct spop_strm *spop_strm, const char *pfx); |
276 | | |
277 | | /* a dummy closed endpoint */ |
278 | | static const struct sedesc closed_ep = { |
279 | | .sc = NULL, |
280 | | .flags = SE_FL_DETACHED, |
281 | | }; |
282 | | |
283 | | /* a dmumy closed stream */ |
284 | | static const struct spop_strm *spop_closed_stream = &(const struct spop_strm){ |
285 | | .sd = (struct sedesc *)&closed_ep, |
286 | | .spop_conn = NULL, |
287 | | .state = SPOP_SS_CLOSED, |
288 | | .flags = SPOP_SF_NONE, |
289 | | .id = 0, |
290 | | }; |
291 | | |
292 | | /* a dummy idle stream for use with any unknown stream */ |
293 | | static const struct spop_strm *spop_unknown_stream = &(const struct spop_strm){ |
294 | | .sd = (struct sedesc*)&closed_ep, |
295 | | .spop_conn = NULL, |
296 | | .state = SPOP_SS_IDLE, |
297 | | .flags = SPOP_SF_NONE, |
298 | | .id = 0, |
299 | | }; |
300 | | |
301 | | /* returns the stconn associated to the stream */ |
302 | | static forceinline struct stconn *spop_strm_sc(const struct spop_strm *spop_strm) |
303 | 0 | { |
304 | 0 | return spop_strm->sd->sc; |
305 | 0 | } |
306 | | |
307 | | static inline struct sedesc *spop_strm_opposite_sd(struct spop_strm *spop_strm) |
308 | 0 | { |
309 | 0 | return se_opposite(spop_strm->sd); |
310 | 0 | } |
311 | | |
312 | | #if defined(USE_TRACE) |
313 | | |
314 | | static inline void spop_trace_buf(const struct buffer *buf, size_t ofs, size_t len) |
315 | 0 | { |
316 | 0 | size_t block1, block2; |
317 | 0 | int line, ptr, newptr; |
318 | |
|
319 | 0 | block1 = b_contig_data(buf, ofs); |
320 | 0 | block2 = 0; |
321 | 0 | if (block1 > len) |
322 | 0 | block1 = len; |
323 | 0 | block2 = len - block1; |
324 | |
|
325 | 0 | ofs = b_peek_ofs(buf, ofs); |
326 | |
|
327 | 0 | line = 0; |
328 | 0 | ptr = ofs; |
329 | 0 | while (ptr < ofs + block1) { |
330 | 0 | newptr = dump_text_line(&trace_buf, b_orig(buf), b_size(buf), ofs + block1, &line, ptr); |
331 | 0 | if (newptr == ptr) |
332 | 0 | break; |
333 | 0 | ptr = newptr; |
334 | 0 | } |
335 | |
|
336 | 0 | line = ptr = 0; |
337 | 0 | while (ptr < block2) { |
338 | 0 | newptr = dump_text_line(&trace_buf, b_orig(buf), b_size(buf), block2, &line, ptr); |
339 | 0 | if (newptr == ptr) |
340 | 0 | break; |
341 | 0 | ptr = newptr; |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | | /* the SPOP traces always expect that arg1, if non-null, is of type connection |
346 | | * (from which we can derive spop_conn), that arg2, if non-null, is of type spop_strm, |
347 | | * and that arg3, if non-null, is a buffer for rx/tx headers. |
348 | | */ |
349 | | static void spop_trace(enum trace_level level, uint64_t mask, const struct trace_source *src, |
350 | | const struct ist where, const struct ist func, |
351 | | const void *a1, const void *a2, const void *a3, const void *a4) |
352 | 0 | { |
353 | 0 | const struct connection *conn = a1; |
354 | 0 | struct spop_conn *spop_conn = conn ? conn->ctx : NULL; |
355 | 0 | const struct spop_strm *spop_strm = a2; |
356 | 0 | const struct buffer *buf = a3; |
357 | 0 | const size_t *val = a4; |
358 | |
|
359 | 0 | if (!spop_conn) |
360 | 0 | spop_conn = (spop_strm ? spop_strm->spop_conn : NULL); |
361 | |
|
362 | 0 | if (!spop_conn || src->verbosity < SPOP_VERB_CLEAN) |
363 | 0 | return; |
364 | | |
365 | 0 | if (src->verbosity == SPOP_VERB_CLEAN) |
366 | 0 | return; |
367 | | |
368 | | /* Display the value to the 4th argument (level > STATE) */ |
369 | 0 | if (src->level > TRACE_LEVEL_STATE && val) |
370 | 0 | chunk_appendf(&trace_buf, " - VAL=%lu", (long)*val); |
371 | | |
372 | | /* Display spop_conn info and, if defined, spop_strm info */ |
373 | 0 | chunk_appendf(&trace_buf, " - spop_conn=%p(%s,0x%08x)", spop_conn, spop_conn_st_to_str(spop_conn->state), spop_conn->flags); |
374 | 0 | if (spop_conn->conn) |
375 | 0 | chunk_appendf(&trace_buf, " conn=%p(0x%08x)", spop_conn->conn, spop_conn->conn->flags); |
376 | 0 | if (spop_conn->errcode) |
377 | 0 | chunk_appendf(&trace_buf, " err=%02x", spop_conn->errcode); |
378 | |
|
379 | 0 | if (spop_strm) { |
380 | 0 | if (spop_strm->id <= 0) |
381 | 0 | chunk_appendf(&trace_buf, " dsi=%d dfi=%d", spop_conn->dsi, spop_conn->dfi); |
382 | 0 | if (spop_strm != spop_closed_stream && spop_strm != spop_unknown_stream) { |
383 | 0 | chunk_appendf(&trace_buf, " spop_strm=%p(%d,%s,0x%08x)", spop_strm, spop_strm->id, spop_strm_st_to_str(spop_strm->state), spop_strm->flags); |
384 | 0 | if (spop_strm->sd) { |
385 | 0 | chunk_appendf(&trace_buf, " sd=%p(0x%08x)", spop_strm->sd, se_fl_get(spop_strm->sd)); |
386 | 0 | if (spop_strm_sc(spop_strm)) |
387 | 0 | chunk_appendf(&trace_buf, " sc=%p(0x%08x)", spop_strm_sc(spop_strm), spop_strm_sc(spop_strm)->flags); |
388 | 0 | } |
389 | 0 | if (spop_strm->errcode) |
390 | 0 | chunk_appendf(&trace_buf, " err=%02x", spop_strm->errcode); |
391 | 0 | } |
392 | 0 | } |
393 | 0 | if (src->verbosity == SPOP_VERB_MINIMAL) |
394 | 0 | return; |
395 | | |
396 | | /* Display mbuf and dbuf info (level > USER & verbosity > SIMPLE) */ |
397 | 0 | if (src->level > TRACE_LEVEL_USER) { |
398 | 0 | if (src->verbosity == SPOP_VERB_COMPLETE || |
399 | 0 | (src->verbosity == SPOP_VERB_ADVANCED && (mask & (SPOP_EV_SPOP_CONN_RECV|SPOP_EV_RX_FRAME)))) |
400 | 0 | chunk_appendf(&trace_buf, " dbuf=%u@%p+%u/%u", |
401 | 0 | (unsigned int)b_data(&spop_conn->dbuf), b_orig(&spop_conn->dbuf), |
402 | 0 | (unsigned int)b_head_ofs(&spop_conn->dbuf), (unsigned int)b_size(&spop_conn->dbuf)); |
403 | 0 | if (src->verbosity == SPOP_VERB_COMPLETE || |
404 | 0 | (src->verbosity == SPOP_VERB_ADVANCED && (mask & (SPOP_EV_SPOP_CONN_SEND|SPOP_EV_TX_FRAME)))) { |
405 | 0 | struct buffer *hmbuf = br_head(spop_conn->mbuf); |
406 | 0 | struct buffer *tmbuf = br_tail(spop_conn->mbuf); |
407 | |
|
408 | 0 | chunk_appendf(&trace_buf, " .mbuf=[%u..%u|%u],h=[%u@%p+%u/%u],t=[%u@%p+%u/%u]", |
409 | 0 | br_head_idx(spop_conn->mbuf), br_tail_idx(spop_conn->mbuf), br_size(spop_conn->mbuf), |
410 | 0 | (unsigned int)b_data(hmbuf), b_orig(hmbuf), |
411 | 0 | (unsigned int)b_head_ofs(hmbuf), (unsigned int)b_size(hmbuf), |
412 | 0 | (unsigned int)b_data(tmbuf), b_orig(tmbuf), |
413 | 0 | (unsigned int)b_head_ofs(tmbuf), (unsigned int)b_size(tmbuf)); |
414 | 0 | } |
415 | |
|
416 | 0 | if (spop_strm && (src->verbosity == SPOP_VERB_COMPLETE || |
417 | 0 | (src->verbosity == SPOP_VERB_ADVANCED && (mask & (SPOP_EV_STRM_RECV))))) |
418 | 0 | chunk_appendf(&trace_buf, " rxbuf=%u@%p+%u/%u", |
419 | 0 | (unsigned int)b_data(&spop_strm->rxbuf), b_orig(&spop_strm->rxbuf), |
420 | 0 | (unsigned int)b_head_ofs(&spop_strm->rxbuf), (unsigned int)b_size(&spop_strm->rxbuf)); |
421 | 0 | } |
422 | | |
423 | | /* Display htx info if defined (level > USER) */ |
424 | 0 | if (src->level > TRACE_LEVEL_USER && buf) { |
425 | 0 | int full = 0, max = 3000, chunk = 1024; |
426 | | |
427 | | /* Full info (level > STATE && verbosity > SIMPLE) */ |
428 | 0 | if (src->level > TRACE_LEVEL_STATE) { |
429 | 0 | if (src->verbosity == SPOP_VERB_COMPLETE) |
430 | 0 | full = 1; |
431 | 0 | else if (src->verbosity == SPOP_VERB_ADVANCED) { |
432 | 0 | full = 1; |
433 | 0 | max = 256; |
434 | 0 | chunk = 64; |
435 | 0 | } |
436 | 0 | } |
437 | |
|
438 | 0 | chunk_appendf(&trace_buf, " buf=%u@%p+%u/%u", |
439 | 0 | (unsigned int)b_data(buf), b_orig(buf), |
440 | 0 | (unsigned int)b_head_ofs(buf), (unsigned int)b_size(buf)); |
441 | |
|
442 | 0 | if (b_data(buf) && full) { |
443 | 0 | chunk_memcat(&trace_buf, "\n", 1); |
444 | 0 | if (b_data(buf) < max) |
445 | 0 | spop_trace_buf(buf, 0, b_data(buf)); |
446 | 0 | else { |
447 | 0 | spop_trace_buf(buf, 0, chunk); |
448 | 0 | chunk_memcat(&trace_buf, " ...\n", 6); |
449 | 0 | spop_trace_buf(buf, b_data(buf) - chunk, chunk); |
450 | 0 | } |
451 | 0 | } |
452 | 0 | } |
453 | 0 | } |
454 | | |
455 | | #endif /* USE_TRACE */ |
456 | | |
457 | | /*****************************************************/ |
458 | | /* functions below are for dynamic buffer management */ |
459 | | /*****************************************************/ |
460 | | /* Tries to grab a buffer and to re-enable processing on mux <target>. The |
461 | | * spop_conn flags are used to figure what buffer was requested. It returns 1 if |
462 | | * the allocation succeeds, in which case the connection is woken up, or 0 if |
463 | | * it's impossible to wake up and we prefer to be woken up later. |
464 | | */ |
465 | | static int spop_buf_available(void *target) |
466 | 0 | { |
467 | 0 | struct spop_conn *spop_conn = target; |
468 | 0 | struct spop_strm *spop_strm; |
469 | |
|
470 | 0 | if ((spop_conn->flags & SPOP_CF_DEM_DALLOC) && b_alloc(&spop_conn->dbuf, DB_MUX_RX)) { |
471 | 0 | TRACE_STATE("unblocking spop_conn, dbuf allocated", SPOP_EV_SPOP_CONN_RECV|SPOP_EV_SPOP_CONN_BLK|SPOP_EV_SPOP_CONN_WAKE, spop_conn->conn); |
472 | 0 | spop_conn->flags &= ~SPOP_CF_DEM_DALLOC; |
473 | 0 | spop_conn_restart_reading(spop_conn, 1); |
474 | 0 | return 1; |
475 | 0 | } |
476 | | |
477 | 0 | if ((spop_conn->flags & SPOP_CF_MUX_MALLOC) && b_alloc(br_tail(spop_conn->mbuf), DB_MUX_TX)) { |
478 | 0 | TRACE_STATE("unblocking spop_conn, mbuf allocated", SPOP_EV_SPOP_CONN_SEND|SPOP_EV_SPOP_CONN_BLK|SPOP_EV_SPOP_CONN_WAKE, spop_conn->conn); |
479 | 0 | spop_conn->flags &= ~SPOP_CF_MUX_MALLOC; |
480 | 0 | if (spop_conn->flags & SPOP_CF_DEM_MROOM) { |
481 | 0 | spop_conn->flags &= ~SPOP_CF_DEM_MROOM; |
482 | 0 | spop_conn_restart_reading(spop_conn, 1); |
483 | 0 | } |
484 | 0 | return 1; |
485 | 0 | } |
486 | | |
487 | 0 | if ((spop_conn->flags & SPOP_CF_DEM_SALLOC) && |
488 | 0 | (spop_strm = spop_conn_st_by_id(spop_conn, spop_conn->dsi)) && spop_strm_sc(spop_strm) && |
489 | 0 | b_alloc(&spop_strm->rxbuf, DB_SE_RX)) { |
490 | 0 | TRACE_STATE("unblocking spop_strm, rxbuf allocated", SPOP_EV_STRM_RECV|SPOP_EV_SPOP_STRM_BLK|SPOP_EV_STRM_WAKE, spop_conn->conn, spop_strm); |
491 | 0 | spop_conn->flags &= ~SPOP_CF_DEM_SALLOC; |
492 | 0 | spop_conn_restart_reading(spop_conn, 1); |
493 | 0 | return 1; |
494 | 0 | } |
495 | | |
496 | 0 | return 0; |
497 | 0 | } |
498 | | |
499 | | static inline struct buffer *spop_get_buf(struct spop_conn *spop_conn, struct buffer *bptr) |
500 | 0 | { |
501 | 0 | struct buffer *buf = NULL; |
502 | |
|
503 | 0 | if (likely(!LIST_INLIST(&spop_conn->buf_wait.list)) && |
504 | 0 | unlikely((buf = b_alloc(bptr, DB_MUX_RX)) == NULL)) { |
505 | 0 | b_queue(DB_MUX_RX, &spop_conn->buf_wait, spop_conn, spop_buf_available); |
506 | 0 | } |
507 | 0 | return buf; |
508 | 0 | } |
509 | | |
510 | | static inline void spop_release_buf(struct spop_conn *spop_conn, struct buffer *bptr) |
511 | 0 | { |
512 | 0 | if (bptr->size) { |
513 | 0 | b_free(bptr); |
514 | 0 | offer_buffers(NULL, 1); |
515 | 0 | } |
516 | 0 | } |
517 | | |
518 | | static inline void spop_release_mbuf(struct spop_conn *spop_conn) |
519 | 0 | { |
520 | 0 | struct buffer *buf; |
521 | 0 | unsigned int count = 0; |
522 | |
|
523 | 0 | while (b_size(buf = br_head_pick(spop_conn->mbuf))) { |
524 | 0 | b_free(buf); |
525 | 0 | count++; |
526 | 0 | } |
527 | |
|
528 | 0 | spop_conn->flags &= ~(SPOP_CF_MUX_MFULL | SPOP_CF_DEM_MROOM); |
529 | |
|
530 | 0 | if (count) |
531 | 0 | offer_buffers(NULL, count); |
532 | 0 | } |
533 | | |
534 | | /*****************************************************************/ |
535 | | /* functions below are dedicated to the mux setup and management */ |
536 | | /*****************************************************************/ |
537 | | /* Indicates whether or not the we may call the spop_recv() function to attempt |
538 | | * to receive data into the buffer and/or demux pending data. The condition is |
539 | | * a bit complex due to some API limits for now. The rules are the following : |
540 | | * - if an error or a shutdown was detected on the connection and the buffer |
541 | | * is empty, we must not attempt to receive |
542 | | * - if the demux buf failed to be allocated, we must not try to receive and |
543 | | * we know there is nothing pending |
544 | | * - if no flag indicates a blocking condition, we may attempt to receive, |
545 | | * regardless of whether the demux buffer is full or not, so that only |
546 | | * de demux part decides whether or not to block. This is needed because |
547 | | * the connection API indeed prevents us from re-enabling receipt that is |
548 | | * already enabled in a polled state, so we must always immediately stop |
549 | | * as soon as the demux can't proceed so as never to hit an end of read |
550 | | * with data pending in the buffers. |
551 | | * - otherwise must may not attempt |
552 | | */ |
553 | | static inline int spop_recv_allowed(const struct spop_conn *spop_conn) |
554 | 0 | { |
555 | 0 | if (b_data(&spop_conn->dbuf) == 0 && |
556 | 0 | ((spop_conn->flags & (SPOP_CF_RCVD_SHUT|SPOP_CF_ERROR)) || spop_conn->state == SPOP_CS_CLOSED)) |
557 | 0 | return 0; |
558 | | |
559 | 0 | if (!(spop_conn->flags & SPOP_CF_DEM_DALLOC) && |
560 | 0 | !(spop_conn->flags & SPOP_CF_DEM_BLOCK_ANY)) |
561 | 0 | return 1; |
562 | | |
563 | 0 | return 0; |
564 | 0 | } |
565 | | |
566 | | static inline void spop_conn_report_term_evt(struct spop_conn *spop_conn, enum muxc_term_event_type type) |
567 | 0 | { |
568 | 0 | enum term_event_loc loc = tevt_loc_muxc + 8; /* Always on backend side for now */ |
569 | |
|
570 | 0 | spop_conn->term_evts_log = tevt_report_event(spop_conn->term_evts_log, loc, type); |
571 | 0 | } |
572 | | |
573 | | /* Restarts reading on the connection if it was not enabled */ |
574 | | static inline void spop_conn_restart_reading(const struct spop_conn *spop_conn, int consider_buffer) |
575 | 0 | { |
576 | 0 | if (!spop_recv_allowed(spop_conn)) |
577 | 0 | return; |
578 | 0 | if ((!consider_buffer || !b_data(&spop_conn->dbuf)) && |
579 | 0 | (spop_conn->wait_event.events & SUB_RETRY_RECV)) |
580 | 0 | return; |
581 | 0 | tasklet_wakeup(spop_conn->wait_event.tasklet); |
582 | 0 | } |
583 | | |
584 | | /* Returns the number of allocatable outgoing streams for the connection taking |
585 | | * the number reserved streams into account. |
586 | | */ |
587 | | static inline int spop_streams_left(const struct spop_conn *spop_conn) |
588 | 0 | { |
589 | 0 | int ret; |
590 | |
|
591 | 0 | ret = (unsigned int)(0x7FFFFFFF - spop_conn->max_id) - spop_conn->nb_reserved - 1; |
592 | 0 | if (ret < 0) |
593 | 0 | ret = 0; |
594 | 0 | return ret; |
595 | 0 | } |
596 | | |
597 | | /* Returns the number of streams in use on a connection to figure if it's |
598 | | * idle or not. We check nb_sc and not nb_streams as the caller will want |
599 | | * to know if it was the last one after a detach(). |
600 | | */ |
601 | | static int spop_used_streams(struct connection *conn) |
602 | 0 | { |
603 | 0 | struct spop_conn *spop_conn = conn->ctx; |
604 | |
|
605 | 0 | return spop_conn->nb_sc; |
606 | 0 | } |
607 | | |
608 | | /* Returns the number of concurrent streams available on the connection */ |
609 | | static int spop_avail_streams(struct connection *conn) |
610 | 0 | { |
611 | 0 | struct server *srv = objt_server(conn->target); |
612 | 0 | struct spop_conn *spop_conn = conn->ctx; |
613 | 0 | int ret1, ret2; |
614 | | |
615 | | /* Don't open new stream if the connection is closed */ |
616 | 0 | if (spop_conn->state >= SPOP_CS_ERROR || (spop_conn->flags & (SPOP_CF_ERR_PENDING|SPOP_CF_ERROR))) |
617 | 0 | return 0; |
618 | | |
619 | | /* May be negative if this setting has changed */ |
620 | 0 | ret1 = (spop_conn->streams_limit - spop_conn->nb_streams); |
621 | | |
622 | | /* we must also consider the limit imposed by stream IDs */ |
623 | 0 | ret2 = spop_streams_left(spop_conn); |
624 | 0 | ret1 = MIN(ret1, ret2); |
625 | 0 | if (ret1 > 0 && srv && srv->max_reuse >= 0) { |
626 | 0 | ret2 = ((spop_conn->stream_cnt <= srv->max_reuse) ? srv->max_reuse - spop_conn->stream_cnt + 1: 0); |
627 | 0 | ret1 = MIN(ret1, ret2); |
628 | 0 | } |
629 | 0 | return ret1; |
630 | 0 | } |
631 | | |
632 | | /* Initializes the mux once it's attached. Only outgoing connections are |
633 | | * supported. So the context is already initialized before installing the |
634 | | * mux. <input> is always used as Input buffer and may contain data. It is the |
635 | | * caller responsibility to not reuse it anymore. Returns < 0 on error. |
636 | | */ |
637 | | static int spop_init(struct connection *conn, struct proxy *px, struct session *sess, |
638 | | struct buffer *input) |
639 | 0 | { |
640 | 0 | struct spop_conn *spop_conn; |
641 | 0 | struct spop_strm *spop_strm; |
642 | 0 | struct sedesc *sdo = NULL; |
643 | 0 | struct task *t = NULL; |
644 | 0 | void *conn_ctx = conn->ctx; |
645 | |
|
646 | 0 | TRACE_ENTER(SPOP_EV_SPOP_CONN_NEW); |
647 | |
|
648 | 0 | spop_conn = pool_alloc(pool_head_spop_conn); |
649 | 0 | if (!spop_conn) { |
650 | 0 | TRACE_ERROR("spop_conn allocation failure", SPOP_EV_SPOP_CONN_NEW|SPOP_EV_SPOP_CONN_END|SPOP_EV_SPOP_CONN_ERR); |
651 | 0 | goto fail_conn; |
652 | 0 | } |
653 | | |
654 | 0 | spop_conn->shut_timeout = spop_conn->timeout = px->timeout.server; |
655 | 0 | if (tick_isset(px->timeout.serverfin)) |
656 | 0 | spop_conn->shut_timeout = px->timeout.serverfin; |
657 | 0 | spop_conn->flags = SPOP_CF_NONE; |
658 | |
|
659 | 0 | spop_conn->proxy = px; |
660 | 0 | spop_conn->agent = NULL; |
661 | 0 | spop_conn->task = NULL; |
662 | 0 | if (tick_isset(spop_conn->timeout)) { |
663 | 0 | t = task_new_here(); |
664 | 0 | if (!t) { |
665 | 0 | TRACE_ERROR("spop_conn task allocation failure", SPOP_EV_SPOP_CONN_NEW|SPOP_EV_SPOP_CONN_END|SPOP_EV_SPOP_CONN_ERR); |
666 | 0 | goto fail; |
667 | 0 | } |
668 | | |
669 | 0 | spop_conn->task = t; |
670 | 0 | t->process = spop_timeout_task; |
671 | 0 | t->context = spop_conn; |
672 | 0 | t->expire = tick_add(now_ms, spop_conn->timeout); |
673 | 0 | } |
674 | | |
675 | 0 | spop_conn->wait_event.tasklet = tasklet_new(); |
676 | 0 | if (!spop_conn->wait_event.tasklet) |
677 | 0 | goto fail; |
678 | 0 | spop_conn->wait_event.tasklet->process = spop_io_cb; |
679 | 0 | spop_conn->wait_event.tasklet->context = spop_conn; |
680 | 0 | spop_conn->wait_event.events = 0; |
681 | | |
682 | | /* Initialise the context. */ |
683 | 0 | spop_conn->state = SPOP_CS_HA_HELLO; |
684 | 0 | spop_conn->errcode = SPOP_ERR_NONE; |
685 | 0 | spop_conn->conn = conn; |
686 | 0 | spop_conn->max_frame_size = SPOP_MAX_FRAME_SIZE; |
687 | 0 | spop_conn->streams_limit = 1; |
688 | 0 | spop_conn->max_id = -1; |
689 | 0 | spop_conn->nb_streams = 0; |
690 | 0 | spop_conn->nb_sc = 0; |
691 | 0 | spop_conn->nb_reserved = 0; |
692 | 0 | spop_conn->stream_cnt = 0; |
693 | 0 | spop_conn->term_evts_log = 0; |
694 | |
|
695 | 0 | spop_conn->dbuf = *input; |
696 | 0 | spop_conn->dsi = -1; |
697 | |
|
698 | 0 | br_init(spop_conn->mbuf, sizeof(spop_conn->mbuf) / sizeof(spop_conn->mbuf[0])); |
699 | 0 | spop_conn->streams_by_id = EB_ROOT; |
700 | 0 | LIST_INIT(&spop_conn->send_list); |
701 | 0 | LIST_INIT(&spop_conn->buf_wait.list); |
702 | |
|
703 | 0 | conn->ctx = spop_conn; |
704 | |
|
705 | 0 | if (t) |
706 | 0 | task_queue(t); |
707 | | |
708 | | /* FIXME: this is temporary, for outgoing connections we need to |
709 | | * immediately allocate a stream until the code is modified so that the |
710 | | * caller calls ->attach(). For now the outgoing sc is stored as |
711 | | * conn->ctx by the caller and saved in conn_ctx. |
712 | | */ |
713 | 0 | spop_strm = spop_stconn_new(spop_conn, conn_ctx, sess); |
714 | 0 | if (!spop_strm) |
715 | 0 | goto fail; |
716 | | |
717 | | /* Retrieve the SPOE agent attached to the opposite endpoint. Only |
718 | | * undefined when there is no opposite endpoint (healthcheck) |
719 | | */ |
720 | 0 | sdo = spop_strm_opposite_sd(spop_strm); |
721 | 0 | if (sdo) { |
722 | 0 | spop_conn->agent = spoe_appctx_agent(sc_appctx(sdo->sc)); |
723 | 0 | if (!spop_conn->agent) { |
724 | 0 | TRACE_ERROR("not a SPOP stream", SPOP_EV_SPOP_CONN_NEW|SPOP_EV_SPOP_CONN_END|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
725 | 0 | goto fail; |
726 | 0 | } |
727 | 0 | spop_conn->max_frame_size = spop_conn->agent->max_frame_size; |
728 | 0 | if (spop_conn->agent->flags & SPOE_FL_PIPELINING) |
729 | 0 | spop_conn->streams_limit = 20; |
730 | 0 | } |
731 | | |
732 | | /* Repare to read something */ |
733 | 0 | spop_conn_restart_reading(spop_conn, 1); |
734 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_CONN_NEW, conn); |
735 | 0 | return 0; |
736 | | |
737 | 0 | fail: |
738 | 0 | task_destroy(t); |
739 | 0 | tasklet_free(spop_conn->wait_event.tasklet); |
740 | 0 | pool_free(pool_head_spop_conn, spop_conn); |
741 | 0 | fail_conn: |
742 | 0 | conn->ctx = conn_ctx; // restore saved ctx |
743 | 0 | TRACE_DEVEL("leaving in error", SPOP_EV_SPOP_CONN_NEW|SPOP_EV_SPOP_CONN_END|SPOP_EV_SPOP_CONN_ERR); |
744 | 0 | return -1; |
745 | 0 | } |
746 | | |
747 | | /* Release function. This one should be called to free all resources allocated |
748 | | * to the mux. |
749 | | */ |
750 | | static void spop_release(struct spop_conn *spop_conn) |
751 | 0 | { |
752 | 0 | struct connection *conn = spop_conn->conn; |
753 | |
|
754 | 0 | TRACE_POINT(SPOP_EV_SPOP_CONN_END); |
755 | |
|
756 | 0 | b_dequeue(&spop_conn->buf_wait); |
757 | |
|
758 | 0 | spop_release_buf(spop_conn, &spop_conn->dbuf); |
759 | 0 | spop_release_mbuf(spop_conn); |
760 | |
|
761 | 0 | if (spop_conn->task) { |
762 | 0 | spop_conn->task->context = NULL; |
763 | 0 | task_wakeup(spop_conn->task, TASK_WOKEN_OTHER); |
764 | 0 | spop_conn->task = NULL; |
765 | 0 | } |
766 | 0 | tasklet_free(spop_conn->wait_event.tasklet); |
767 | 0 | if (conn) { |
768 | 0 | if (spop_conn->wait_event.events != 0) |
769 | 0 | conn->xprt->unsubscribe(conn, conn->xprt_ctx, spop_conn->wait_event.events, |
770 | 0 | &spop_conn->wait_event); |
771 | 0 | spop_conn_report_term_evt(spop_conn, muxc_tevt_type_shutw); |
772 | 0 | } |
773 | |
|
774 | 0 | pool_free(pool_head_spop_conn, spop_conn); |
775 | |
|
776 | 0 | if (conn) { |
777 | 0 | conn->mux = NULL; |
778 | 0 | conn->ctx = NULL; |
779 | 0 | TRACE_DEVEL("freeing conn", SPOP_EV_SPOP_CONN_END, conn); |
780 | |
|
781 | 0 | conn_stop_tracking(conn); |
782 | 0 | conn_full_close(conn); |
783 | 0 | if (conn->destroy_cb) |
784 | 0 | conn->destroy_cb(conn); |
785 | 0 | conn_free(conn); |
786 | 0 | } |
787 | 0 | } |
788 | | |
789 | | |
790 | | /* Returns the next allocatable outgoing stream ID for the SPOP connection, or |
791 | | * -1 if no more is allocatable. |
792 | | */ |
793 | | static inline int32_t spop_conn_get_next_sid(const struct spop_conn *spop_conn) |
794 | 0 | { |
795 | 0 | int32_t id = (spop_conn->max_id + 1) | 1; |
796 | |
|
797 | 0 | if ((id & 0x80000000U)) |
798 | 0 | id = -1; |
799 | 0 | return id; |
800 | 0 | } |
801 | | |
802 | | /* Returns the stream associated with id <id> or NULL if not found */ |
803 | | static inline struct spop_strm *spop_conn_st_by_id(struct spop_conn *spop_conn, int id) |
804 | 0 | { |
805 | 0 | struct eb32_node *node; |
806 | |
|
807 | 0 | if (id == 0) |
808 | 0 | return (struct spop_strm *)spop_closed_stream; |
809 | | |
810 | 0 | if (id > spop_conn->max_id) |
811 | 0 | return (struct spop_strm *)spop_unknown_stream; |
812 | | |
813 | 0 | node = eb32_lookup(&spop_conn->streams_by_id, id); |
814 | 0 | if (!node) |
815 | 0 | return (struct spop_strm *)spop_unknown_stream; |
816 | 0 | return container_of(node, struct spop_strm, by_id); |
817 | 0 | } |
818 | | |
819 | | /* Detect a pending read0 for a SPOP connection. It happens if a read0 was |
820 | | * already reported on a previous xprt->rcvbuf() AND a frame parser failed |
821 | | * to parse pending data, confirming no more progress is possible because |
822 | | * we're facing a truncated frame. The function returns 1 to report a read0 |
823 | | * or 0 otherwise. |
824 | | */ |
825 | | static int spop_conn_read0_pending(struct spop_conn *spop_conn) |
826 | 0 | { |
827 | 0 | return !!(spop_conn->flags & SPOP_CF_END_REACHED); |
828 | 0 | } |
829 | | |
830 | | /* returns true if the connection is allowed to expire, false otherwise. A |
831 | | * connection may expire when it has no attached streams. As long as streams |
832 | | * are attached, the application layer is responsible for timeout management, |
833 | | * and each layer will detach when it doesn't want to wait anymore. When the |
834 | | * last one leaves, the connection must take over timeout management. |
835 | | */ |
836 | | static inline int spop_conn_may_expire(const struct spop_conn *spop_conn) |
837 | 0 | { |
838 | 0 | return !spop_conn->nb_sc; |
839 | 0 | } |
840 | | |
841 | | // TODO: spop_conn_max_concurrent_streams() : default 20 but add a agent parameter ! |
842 | | |
843 | | /* Returns true if the SPOP connection must be release */ |
844 | | static inline int spop_conn_is_dead(struct spop_conn *spop_conn) |
845 | 0 | { |
846 | 0 | if (eb_is_empty(&spop_conn->streams_by_id) && /* don't close if streams exist */ |
847 | 0 | ((spop_conn->flags & SPOP_CF_ERROR) || /* errors close immediately */ |
848 | 0 | (spop_conn->flags & SPOP_CF_ERR_PENDING && spop_conn->state < SPOP_CS_RUNNING) || /* early error during connect */ |
849 | 0 | (spop_conn->state == SPOP_CS_CLOSED && !spop_conn->task) ||/* a timeout stroke earlier */ |
850 | 0 | (!(spop_conn->conn->owner)) || /* Nobody's left to take care of the connection, drop it now */ |
851 | 0 | (!br_data(spop_conn->mbuf) && /* mux buffer empty, also process clean events below */ |
852 | 0 | (spop_conn->flags & SPOP_CF_RCVD_SHUT)))) |
853 | 0 | return 1; |
854 | 0 | return 0; |
855 | 0 | } |
856 | | |
857 | | /* update spop_conn timeout if needed */ |
858 | | static void spop_conn_update_timeout(struct spop_conn *spop_conn) |
859 | 0 | { |
860 | 0 | int is_idle_conn = 0; |
861 | |
|
862 | 0 | if (!spop_conn->task) |
863 | 0 | goto leave; |
864 | | |
865 | 0 | TRACE_ENTER(SPOP_EV_SPOP_CONN_WAKE, spop_conn->conn); |
866 | |
|
867 | 0 | if (spop_conn_may_expire(spop_conn)) { |
868 | | /* no more streams attached */ |
869 | 0 | if (br_data(spop_conn->mbuf)) { |
870 | | /* pending output data: always the regular data timeout */ |
871 | 0 | spop_conn->task->expire = tick_add_ifset(now_ms, spop_conn->timeout); |
872 | 0 | } |
873 | 0 | else { |
874 | 0 | if (spop_conn->flags & (SPOP_CF_DISCO_SENT|SPOP_CF_DISCO_FAILED)) { |
875 | | /* DISCONNECT sent (or failed), closing in progress */ |
876 | 0 | int exp = tick_add_ifset(now_ms, spop_conn->shut_timeout); |
877 | |
|
878 | 0 | spop_conn->task->expire = tick_first(spop_conn->task->expire, exp); |
879 | 0 | is_idle_conn = 1; |
880 | 0 | } |
881 | | |
882 | | /* if a timeout above was not set, fall back to the default one */ |
883 | 0 | if (!tick_isset(spop_conn->task->expire)) |
884 | 0 | spop_conn->task->expire = tick_add_ifset(now_ms, spop_conn->timeout); |
885 | 0 | } |
886 | |
|
887 | 0 | if ((spop_conn->proxy->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) && |
888 | 0 | is_idle_conn && tick_isset(global.close_spread_end)) { |
889 | | /* If a soft-stop is in progress and a close-spread-time |
890 | | * is set, we want to spread idle connection closing roughly |
891 | | * evenly across the defined window. This should only |
892 | | * act on idle frontend connections. |
893 | | * If the window end is already in the past, we wake the |
894 | | * timeout task up immediately so that it can be closed. |
895 | | */ |
896 | 0 | int remaining_window = tick_remain(now_ms, global.close_spread_end); |
897 | 0 | if (remaining_window) { |
898 | | /* We don't need to reset the expire if it would |
899 | | * already happen before the close window end. |
900 | | */ |
901 | 0 | if (tick_isset(spop_conn->task->expire) && |
902 | 0 | tick_is_le(global.close_spread_end, spop_conn->task->expire)) { |
903 | | /* Set an expire value shorter than the current value |
904 | | * because the close spread window end comes earlier. |
905 | | */ |
906 | 0 | spop_conn->task->expire = tick_add(now_ms, statistical_prng_range(remaining_window)); |
907 | 0 | } |
908 | 0 | } |
909 | 0 | else { |
910 | | /* We are past the soft close window end, wake the timeout |
911 | | * task up immediately. |
912 | | */ |
913 | 0 | task_wakeup(spop_conn->task, TASK_WOKEN_TIMER); |
914 | 0 | } |
915 | 0 | } |
916 | 0 | } |
917 | 0 | else |
918 | 0 | spop_conn->task->expire = TICK_ETERNITY; |
919 | 0 | task_queue(spop_conn->task); |
920 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_CONN_WAKE, spop_conn->conn, 0, 0, (size_t[]){spop_conn->task->expire}); |
921 | 0 | leave: |
922 | 0 | return; |
923 | 0 | } |
924 | | |
925 | | /********************************************************/ |
926 | | /* functions below are for the SPOP protocol processing */ |
927 | | /********************************************************/ |
928 | | /* Marks an error on the connection. Before HELLO frame are sent, we must not send |
929 | | * a DISCONNECT frame, so we set SPOP_CF_DISCO_FAILED to make sure it will not |
930 | | * even try.. */ |
931 | | static inline void spop_conn_error(struct spop_conn *spop_conn, enum spop_error err) |
932 | 0 | { |
933 | 0 | TRACE_POINT(SPOP_EV_SPOP_CONN_ERR, spop_conn->conn, 0, 0, (size_t[]){err}); |
934 | 0 | spop_conn->errcode = err; |
935 | 0 | if (spop_conn->state == SPOP_CS_HA_HELLO) |
936 | 0 | spop_conn->flags |= SPOP_CF_DISCO_FAILED; |
937 | 0 | spop_conn->state = SPOP_CS_ERROR; |
938 | 0 | } |
939 | | |
940 | | /* Marks an error on the stream */ |
941 | | static inline void spop_strm_error(struct spop_strm *spop_strm, enum spop_error err) |
942 | 0 | { |
943 | 0 | if (spop_strm->id && spop_strm->state != SPOP_SS_ERROR) { |
944 | 0 | TRACE_POINT(SPOP_EV_SPOP_STRM_ERR, spop_strm->spop_conn->conn, spop_strm); |
945 | 0 | spop_strm->errcode = err; |
946 | 0 | if (spop_strm->state < SPOP_SS_ERROR) { |
947 | 0 | spop_strm->state = SPOP_SS_ERROR; |
948 | 0 | TRACE_STATE("switching to ERROR", SPOP_EV_SPOP_STRM_ERR, spop_strm->spop_conn->conn, spop_strm); |
949 | 0 | } |
950 | 0 | se_fl_set_error(spop_strm->sd); |
951 | 0 | if (!spop_strm->sd->abort_info.info) { |
952 | 0 | spop_strm->sd->abort_info.info = (SE_ABRT_SRC_MUX_SPOP << SE_ABRT_SRC_SHIFT); |
953 | 0 | spop_strm->sd->abort_info.code = spop_strm->errcode; |
954 | 0 | } |
955 | 0 | } |
956 | 0 | } |
957 | | |
958 | | /* Attempts to notify the data layer of recv availability */ |
959 | | static void spop_strm_notify_recv(struct spop_strm *spop_strm) |
960 | 0 | { |
961 | 0 | if (spop_strm->subs && (spop_strm->subs->events & SUB_RETRY_RECV)) { |
962 | 0 | TRACE_POINT(SPOP_EV_STRM_WAKE, spop_strm->spop_conn->conn, spop_strm); |
963 | 0 | tasklet_wakeup(spop_strm->subs->tasklet, TASK_WOKEN_IO); |
964 | 0 | spop_strm->subs->events &= ~SUB_RETRY_RECV; |
965 | 0 | if (!spop_strm->subs->events) |
966 | 0 | spop_strm->subs = NULL; |
967 | 0 | } |
968 | 0 | } |
969 | | |
970 | | /* Attempts to notify the data layer of send availability */ |
971 | | static void spop_strm_notify_send(struct spop_strm *spop_strm) |
972 | 0 | { |
973 | 0 | if (spop_strm->subs && (spop_strm->subs->events & SUB_RETRY_SEND)) { |
974 | 0 | TRACE_POINT(SPOP_EV_STRM_WAKE, spop_strm->spop_conn->conn, spop_strm); |
975 | 0 | spop_strm->flags |= SPOP_SF_NOTIFIED; |
976 | 0 | tasklet_wakeup(spop_strm->subs->tasklet, TASK_WOKEN_IO); |
977 | 0 | spop_strm->subs->events &= ~SUB_RETRY_SEND; |
978 | 0 | if (!spop_strm->subs->events) |
979 | 0 | spop_strm->subs = NULL; |
980 | 0 | } |
981 | 0 | } |
982 | | |
983 | | /* Alerts the data layer by waking it up. TASK_WOKEN_MSG state is used by |
984 | | * default and if the data layer is also subscribed to recv or send, |
985 | | * TASK_WOKEN_IO is added. |
986 | | */ |
987 | | static void spop_strm_alert(struct spop_strm *spop_strm) |
988 | 0 | { |
989 | 0 | unsigned int state = TASK_WOKEN_MSG; |
990 | |
|
991 | 0 | TRACE_POINT(SPOP_EV_STRM_WAKE, spop_strm->spop_conn->conn, spop_strm); |
992 | 0 | if (!spop_strm_sc(spop_strm)) |
993 | 0 | return; |
994 | | |
995 | 0 | if (spop_strm->subs) { |
996 | 0 | if (spop_strm->subs->events & SUB_RETRY_SEND) |
997 | 0 | spop_strm->flags |= SPOP_SF_NOTIFIED; |
998 | 0 | spop_strm->subs->events = 0; |
999 | 0 | spop_strm->subs = NULL; |
1000 | 0 | state |= TASK_WOKEN_IO; |
1001 | 0 | } |
1002 | 0 | tasklet_wakeup(spop_strm_sc(spop_strm)->wait_event.tasklet, state); |
1003 | 0 | } |
1004 | | |
1005 | | /* Writes the 32-bit frame size <len> at address <frame> */ |
1006 | | static inline void spop_set_frame_size(void *frame, uint32_t len) |
1007 | 0 | { |
1008 | 0 | write_n32(frame, len); |
1009 | 0 | } |
1010 | | |
1011 | | /* reads <bytes> bytes from buffer <b> starting at relative offset <o> from the |
1012 | | * current pointer, dealing with wrapping, and stores the result in <dst>. It's |
1013 | | * the caller's responsibility to verify that there are at least <bytes> bytes |
1014 | | * available in the buffer's input prior to calling this function. The buffer |
1015 | | * is assumed not to hold any output data. |
1016 | | */ |
1017 | | static inline __maybe_unused void spop_get_buf_bytes(void *dst, size_t bytes, |
1018 | | const struct buffer *b, int o) |
1019 | 0 | { |
1020 | 0 | readv_bytes(dst, bytes, b_peek(b, o), b_wrap(b) - b_peek(b, o), b_orig(b)); |
1021 | 0 | } |
1022 | | |
1023 | | static inline __maybe_unused uint16_t spop_get_n16(const struct buffer *b, int o) |
1024 | 0 | { |
1025 | 0 | return readv_n16(b_peek(b, o), b_wrap(b) - b_peek(b, o), b_orig(b)); |
1026 | 0 | } |
1027 | | |
1028 | | static inline __maybe_unused uint32_t spop_get_n32(const struct buffer *b, int o) |
1029 | 0 | { |
1030 | 0 | return readv_n32(b_peek(b, o), b_wrap(b) - b_peek(b, o), b_orig(b)); |
1031 | 0 | } |
1032 | | |
1033 | | static inline __maybe_unused uint64_t spop_get_n64(const struct buffer *b, int o) |
1034 | 0 | { |
1035 | 0 | return readv_n64(b_peek(b, o), b_wrap(b) - b_peek(b, o), b_orig(b)); |
1036 | 0 | } |
1037 | | |
1038 | | static __maybe_unused int spop_get_varint(const struct buffer *b, int o, uint64_t *i) |
1039 | 0 | { |
1040 | 0 | unsigned char *p; |
1041 | 0 | size_t idx = o; |
1042 | 0 | int r; |
1043 | |
|
1044 | 0 | if (idx >= b_data(b)) |
1045 | 0 | return -1; |
1046 | | |
1047 | 0 | p = (unsigned char *)b_peek(b, idx++); |
1048 | 0 | *i = (uint64_t)*p; |
1049 | 0 | if (*i < 240) |
1050 | 0 | return 1; |
1051 | | |
1052 | 0 | r = 4; |
1053 | 0 | do { |
1054 | 0 | if (idx >= b_data(b)) |
1055 | 0 | return -1; |
1056 | 0 | p = (unsigned char *)b_peek(b, idx++); |
1057 | 0 | *i += (uint64_t)*p << r; |
1058 | 0 | r += 7; |
1059 | 0 | } while (*p >= 128); |
1060 | | |
1061 | 0 | return (idx - o); |
1062 | 0 | } |
1063 | | |
1064 | | /* Peeks an SPOP frame header from offset <o> of buffer <b> into descriptor <h>. |
1065 | | * Returns zero if some bytes are missing, otherwise the number of read bytes is |
1066 | | * returned on success. The buffer is assumed not to contain any output data. |
1067 | | */ |
1068 | | static int spop_peek_frame_hdr(const struct buffer *b, int o, struct spop_frame_header *hdr) |
1069 | 0 | { |
1070 | 0 | int o1, o2; |
1071 | |
|
1072 | 0 | if (b_data(b) < o + 11) |
1073 | 0 | return 0; |
1074 | | |
1075 | 0 | o1 = o; |
1076 | 0 | hdr->len = spop_get_n32(b, o1); |
1077 | |
|
1078 | 0 | hdr->type = *(uint8_t*)b_peek(b, o1+4); |
1079 | 0 | hdr->flags = spop_get_n32(b, o1+5); |
1080 | 0 | o1 += 9; |
1081 | | |
1082 | | /* Get the stream-id and the frame-id */ |
1083 | 0 | o2 = spop_get_varint(b, o1, &hdr->sid); |
1084 | 0 | if (o2 == -1) |
1085 | 0 | return 0; |
1086 | 0 | o1 += o2; |
1087 | 0 | o2 = spop_get_varint(b, o1, &hdr->fid); |
1088 | 0 | if (o2 == -1) |
1089 | 0 | return 0; |
1090 | 0 | o1 += o2; |
1091 | | |
1092 | | /* Remove the header length from the frame length */ |
1093 | 0 | hdr->len -= o1 - (o + 4); |
1094 | 0 | return (o1 - o); |
1095 | 0 | } |
1096 | | |
1097 | | /* skip the <o> bytes corresponding to the frame header possibly parsed by |
1098 | | * spop_peek_frame_hdr() above. |
1099 | | */ |
1100 | | static inline void spop_skip_frame_hdr(struct buffer *b, size_t o) |
1101 | 0 | { |
1102 | 0 | b_del(b, o); |
1103 | 0 | } |
1104 | | |
1105 | | /* same as above, automatically advances the buffer on success */ |
1106 | | static inline int spop_get_frame_hdr(struct buffer *b, struct spop_frame_header *hdr) |
1107 | 0 | { |
1108 | 0 | int ret; |
1109 | |
|
1110 | 0 | ret = spop_peek_frame_hdr(b, 0, hdr); |
1111 | 0 | if (ret > 0) |
1112 | 0 | spop_skip_frame_hdr(b, ret); |
1113 | 0 | return ret; |
1114 | 0 | } |
1115 | | |
1116 | | |
1117 | | /* Marks a SPOP stream as CLOSED and decrement the number of active streams for |
1118 | | * its connection if the stream was not yet closed. Please use this exclusively |
1119 | | * before closing a stream to ensure stream count is well maintained. |
1120 | | */ |
1121 | | static inline void spop_strm_close(struct spop_strm *spop_strm) |
1122 | 0 | { |
1123 | 0 | if (spop_strm->state != SPOP_SS_CLOSED) { |
1124 | 0 | TRACE_ENTER(SPOP_EV_SPOP_STRM_END, spop_strm->spop_conn->conn, spop_strm); |
1125 | 0 | spop_strm->spop_conn->nb_streams--; |
1126 | 0 | if (!spop_strm->id) |
1127 | 0 | spop_strm->spop_conn->nb_reserved--; |
1128 | 0 | if (spop_strm_sc(spop_strm)) { |
1129 | 0 | if (!se_fl_test(spop_strm->sd, SE_FL_EOS) && !b_data(&spop_strm->rxbuf)) |
1130 | 0 | spop_strm_notify_recv(spop_strm); |
1131 | 0 | } |
1132 | 0 | spop_strm->state = SPOP_SS_CLOSED; |
1133 | 0 | TRACE_STATE("switching to CLOSED", SPOP_EV_SPOP_STRM_END, spop_strm->spop_conn->conn, spop_strm); |
1134 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_STRM_END, spop_strm->spop_conn->conn, spop_strm); |
1135 | 0 | } |
1136 | 0 | } |
1137 | | |
1138 | | /* Check spop_conn and spop_strm flags to evaluate if EOI/EOS/ERR_PENDING/ERROR flags must |
1139 | | * be set on the SE. |
1140 | | */ |
1141 | | static inline void spop_strm_propagate_term_flags(struct spop_conn *spop_conn, struct spop_strm *spop_strm) |
1142 | 0 | { |
1143 | 0 | if (spop_strm->flags & SPOP_SF_ACK_RCVD) { |
1144 | 0 | se_fl_set(spop_strm->sd, SE_FL_EOI); |
1145 | 0 | } |
1146 | 0 | if (spop_conn_read0_pending(spop_conn) || spop_strm->state == SPOP_SS_CLOSED) { |
1147 | 0 | se_fl_set(spop_strm->sd, SE_FL_EOS); |
1148 | 0 | if (!se_fl_test(spop_strm->sd, SE_FL_EOI)) { |
1149 | 0 | se_fl_set(spop_strm->sd, SE_FL_ERROR); |
1150 | 0 | se_report_term_evt(spop_strm->sd, (spop_conn->flags & SPOP_CF_ERROR ? se_tevt_type_truncated_rcv_err : se_tevt_type_truncated_eos)); |
1151 | 0 | } |
1152 | 0 | else |
1153 | 0 | se_report_term_evt(spop_strm->sd, (spop_conn->flags & SPOP_CF_ERROR ? se_tevt_type_rcv_err : se_tevt_type_eos)); |
1154 | 0 | } |
1155 | 0 | if (se_fl_test(spop_strm->sd, SE_FL_ERR_PENDING)) |
1156 | 0 | se_fl_set(spop_strm->sd, SE_FL_ERROR); |
1157 | 0 | } |
1158 | | |
1159 | | |
1160 | | /* Detaches a SPOP stream from its SPOP connection and releases it to the |
1161 | | * spop_strm pool. |
1162 | | */ |
1163 | | static void spop_strm_destroy(struct spop_strm *spop_strm) |
1164 | 0 | { |
1165 | 0 | struct connection __maybe_unused *conn = spop_strm->spop_conn->conn; |
1166 | |
|
1167 | 0 | TRACE_ENTER(SPOP_EV_SPOP_STRM_END, conn, spop_strm); |
1168 | |
|
1169 | 0 | spop_strm_close(spop_strm); |
1170 | 0 | eb32_delete(&spop_strm->by_id); |
1171 | 0 | if (b_size(&spop_strm->rxbuf)) { |
1172 | 0 | b_free(&spop_strm->rxbuf); |
1173 | 0 | offer_buffers(NULL, 1); |
1174 | 0 | } |
1175 | 0 | if (spop_strm->subs) |
1176 | 0 | spop_strm->subs->events = 0; |
1177 | | |
1178 | | /* There's no need to explicitly call unsubscribe here, the only |
1179 | | * reference left would be in the spop_conn send_list/fctl_list, and if |
1180 | | * we're in it, we're getting out anyway |
1181 | | */ |
1182 | 0 | spop_remove_from_list(spop_strm); |
1183 | |
|
1184 | 0 | BUG_ON(spop_strm->sd && !se_fl_test(spop_strm->sd, SE_FL_ORPHAN)); |
1185 | 0 | sedesc_free(spop_strm->sd); |
1186 | 0 | pool_free(pool_head_spop_strm, spop_strm); |
1187 | |
|
1188 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_STRM_END, conn); |
1189 | 0 | } |
1190 | | |
1191 | | /* Allocates a new stream <id> for connection <spop_conn> and adds it into spop_conn's |
1192 | | * stream tree. In case of error, nothing is added and NULL is returned. The |
1193 | | * causes of errors can be any failed memory allocation. The caller is |
1194 | | * responsible for checking if the connection may support an extra stream prior |
1195 | | * to calling this function. |
1196 | | */ |
1197 | | static struct spop_strm *spop_strm_new(struct spop_conn *spop_conn, int id) |
1198 | 0 | { |
1199 | 0 | struct spop_strm *spop_strm; |
1200 | |
|
1201 | 0 | TRACE_ENTER(SPOP_EV_SPOP_STRM_NEW, spop_conn->conn); |
1202 | |
|
1203 | 0 | spop_strm = pool_alloc(pool_head_spop_strm); |
1204 | 0 | if (!spop_strm) { |
1205 | 0 | TRACE_ERROR("spop_strm allocation failure", SPOP_EV_SPOP_STRM_NEW|SPOP_EV_SPOP_STRM_ERR|SPOP_EV_SPOP_STRM_END, spop_conn->conn); |
1206 | 0 | goto out; |
1207 | 0 | } |
1208 | | |
1209 | 0 | spop_strm->subs = NULL; |
1210 | 0 | LIST_INIT(&spop_strm->list); |
1211 | 0 | spop_strm->spop_conn = spop_conn; |
1212 | 0 | spop_strm->sd = NULL; |
1213 | 0 | spop_strm->flags = SPOP_SF_NONE; |
1214 | 0 | spop_strm->state = SPOP_SS_IDLE; |
1215 | 0 | spop_strm->rxbuf = BUF_NULL; |
1216 | |
|
1217 | 0 | spop_strm->by_id.key = spop_strm->id = id; |
1218 | 0 | spop_strm->fid = 0; |
1219 | 0 | if (id > 0) { |
1220 | 0 | spop_conn->max_id = id; |
1221 | 0 | spop_strm->state = SPOP_SS_OPEN; |
1222 | 0 | } |
1223 | 0 | else |
1224 | 0 | spop_conn->nb_reserved++; |
1225 | |
|
1226 | 0 | eb32_insert(&spop_conn->streams_by_id, &spop_strm->by_id); |
1227 | 0 | spop_conn->nb_streams++; |
1228 | 0 | spop_conn->stream_cnt++; |
1229 | |
|
1230 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_STRM_NEW, spop_conn->conn, spop_strm); |
1231 | 0 | return spop_strm; |
1232 | | |
1233 | 0 | out: |
1234 | 0 | TRACE_DEVEL("leaving in error", SPOP_EV_SPOP_STRM_NEW|SPOP_EV_SPOP_STRM_ERR|SPOP_EV_SPOP_STRM_END, spop_conn->conn); |
1235 | 0 | return NULL; |
1236 | 0 | } |
1237 | | |
1238 | | /* Allocates a new stream associated to stream connector <sc> on the SPOP connection |
1239 | | * <spop_conn> and returns it, or NULL in case of memory allocation error or if the |
1240 | | * highest possible stream ID was reached. |
1241 | | */ |
1242 | | static struct spop_strm *spop_stconn_new(struct spop_conn *spop_conn, struct stconn *sc, |
1243 | | struct session *sess) |
1244 | 0 | { |
1245 | 0 | struct spop_strm *spop_strm = NULL; |
1246 | |
|
1247 | 0 | TRACE_ENTER(SPOP_EV_SPOP_STRM_NEW, spop_conn->conn); |
1248 | 0 | if (spop_conn->nb_streams >= spop_conn->streams_limit) { |
1249 | 0 | TRACE_ERROR("streams_limit reached", SPOP_EV_SPOP_STRM_NEW|SPOP_EV_SPOP_STRM_END|SPOP_EV_SPOP_STRM_ERR, spop_conn->conn); |
1250 | 0 | goto out; |
1251 | 0 | } |
1252 | | |
1253 | 0 | if (spop_streams_left(spop_conn) < 1) { |
1254 | 0 | TRACE_ERROR("!streams_left", SPOP_EV_SPOP_STRM_NEW|SPOP_EV_SPOP_STRM_END|SPOP_EV_SPOP_STRM_ERR, spop_conn->conn); |
1255 | 0 | goto out; |
1256 | 0 | } |
1257 | | |
1258 | | /* Defer choosing the ID until we send the first message to create the stream */ |
1259 | 0 | spop_strm = spop_strm_new(spop_conn, 0); |
1260 | 0 | if (!spop_strm) { |
1261 | 0 | TRACE_ERROR("fstream allocation failure", SPOP_EV_SPOP_STRM_NEW|SPOP_EV_SPOP_STRM_END|SPOP_EV_SPOP_STRM_ERR, spop_conn->conn); |
1262 | 0 | goto out; |
1263 | 0 | } |
1264 | 0 | if (sc_attach_mux(sc, spop_strm, spop_conn->conn) < 0) |
1265 | 0 | goto out; |
1266 | 0 | spop_strm->sd = sc->sedesc; |
1267 | 0 | spop_strm->sess = sess; |
1268 | 0 | spop_conn->nb_sc++; |
1269 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_STRM_NEW, spop_conn->conn, spop_strm); |
1270 | 0 | return spop_strm; |
1271 | | |
1272 | 0 | out: |
1273 | 0 | TRACE_DEVEL("leaving on error", SPOP_EV_SPOP_STRM_NEW|SPOP_EV_SPOP_STRM_END|SPOP_EV_SPOP_STRM_ERR, spop_conn->conn); |
1274 | 0 | if (spop_strm) |
1275 | 0 | spop_strm_destroy(spop_strm); |
1276 | 0 | return NULL; |
1277 | 0 | } |
1278 | | |
1279 | | /* Wakes a specific stream and assign its stream connector some SE_FL_* flags among |
1280 | | * SE_FL_ERR_PENDING and SE_FL_ERROR if needed. The stream's state is |
1281 | | * automatically updated accordingly. If the stream is orphaned, it is |
1282 | | * destroyed. |
1283 | | */ |
1284 | | static void spop_strm_wake_one_stream(struct spop_strm *spop_strm) |
1285 | 0 | { |
1286 | 0 | struct spop_conn *spop_conn = spop_strm->spop_conn; |
1287 | |
|
1288 | 0 | TRACE_ENTER(SPOP_EV_STRM_WAKE, spop_conn->conn, spop_strm); |
1289 | |
|
1290 | 0 | if (!spop_strm_sc(spop_strm)) { |
1291 | | /* this stream was already orphaned */ |
1292 | 0 | spop_strm_destroy(spop_strm); |
1293 | 0 | TRACE_DEVEL("leaving with no spop_strm", SPOP_EV_STRM_WAKE, spop_conn->conn); |
1294 | 0 | return; |
1295 | 0 | } |
1296 | | |
1297 | 0 | if (spop_conn_read0_pending(spop_conn)) { |
1298 | 0 | if (spop_strm->state == SPOP_SS_OPEN) { |
1299 | 0 | spop_strm->state = SPOP_SS_HREM; |
1300 | 0 | TRACE_STATE("switching to HREM", SPOP_EV_STRM_WAKE|SPOP_EV_SPOP_STRM_END, spop_conn->conn, spop_strm); |
1301 | 0 | } |
1302 | 0 | else if (spop_strm->state == SPOP_SS_HLOC) |
1303 | 0 | spop_strm_close(spop_strm); |
1304 | 0 | } |
1305 | |
|
1306 | 0 | if (!(spop_strm->flags & SPOP_SF_ACK_RCVD) && (spop_conn->state == SPOP_CS_CLOSED || (spop_conn->flags & (SPOP_CF_ERR_PENDING|SPOP_CF_ERROR)))) { |
1307 | 0 | se_fl_set_error(spop_strm->sd); |
1308 | 0 | spop_strm_propagate_term_flags(spop_conn, spop_strm); |
1309 | 0 | if (!spop_strm->sd->abort_info.info) { |
1310 | 0 | spop_strm->sd->abort_info.info = (SE_ABRT_SRC_MUX_SPOP << SE_ABRT_SRC_SHIFT); |
1311 | 0 | spop_strm->sd->abort_info.code = spop_conn->errcode; |
1312 | 0 | } |
1313 | |
|
1314 | 0 | if (spop_strm->state < SPOP_SS_ERROR) { |
1315 | 0 | spop_strm->state = SPOP_SS_ERROR; |
1316 | 0 | TRACE_STATE("switching to ERROR", SPOP_EV_STRM_WAKE|SPOP_EV_SPOP_STRM_END, spop_conn->conn, spop_strm); |
1317 | 0 | } |
1318 | 0 | } |
1319 | |
|
1320 | 0 | spop_strm_alert(spop_strm); |
1321 | |
|
1322 | 0 | TRACE_LEAVE(SPOP_EV_STRM_WAKE, spop_conn->conn, spop_strm); |
1323 | 0 | } |
1324 | | |
1325 | | /* Wakes unassigned streams (ID == 0) attached to the connection. */ |
1326 | | static void spop_wake_unassigned_streams(struct spop_conn *spop_conn) |
1327 | 0 | { |
1328 | 0 | struct eb32_node *node; |
1329 | 0 | struct spop_strm *spop_strm; |
1330 | |
|
1331 | 0 | node = eb32_lookup(&spop_conn->streams_by_id, 0); |
1332 | 0 | while (node) { |
1333 | 0 | spop_strm = container_of(node, struct spop_strm, by_id); |
1334 | 0 | if (spop_strm->id > 0) |
1335 | 0 | break; |
1336 | 0 | node = eb32_next(node); |
1337 | 0 | spop_strm_wake_one_stream(spop_strm); |
1338 | 0 | } |
1339 | 0 | } |
1340 | | |
1341 | | /* Wakes the streams attached to the connection, whose id is greater than <last> |
1342 | | * or unassigned. |
1343 | | */ |
1344 | | static void spop_wake_some_streams(struct spop_conn *spop_conn, int last) |
1345 | 0 | { |
1346 | 0 | struct eb32_node *node; |
1347 | 0 | struct spop_strm *spop_strm; |
1348 | |
|
1349 | 0 | TRACE_ENTER(SPOP_EV_STRM_WAKE, spop_conn->conn); |
1350 | | |
1351 | | /* Wake all streams with ID > last */ |
1352 | 0 | node = eb32_lookup_ge(&spop_conn->streams_by_id, last + 1); |
1353 | 0 | while (node) { |
1354 | 0 | spop_strm = container_of(node, struct spop_strm, by_id); |
1355 | 0 | node = eb32_next(node); |
1356 | 0 | spop_strm_wake_one_stream(spop_strm); |
1357 | 0 | } |
1358 | 0 | spop_wake_unassigned_streams(spop_conn); |
1359 | |
|
1360 | 0 | TRACE_LEAVE(SPOP_EV_STRM_WAKE, spop_conn->conn); |
1361 | 0 | } |
1362 | | |
1363 | | /* Sends a HAPROXY HELLO frame. Returns > 0 on success, 0 if it couldn't do |
1364 | | * anything. It is highly unexpected, but if the frame is larger than a buffer |
1365 | | * and cannot be encoded in one time, an error is triggered and the connection is |
1366 | | * closed. HEELO frame cannot be split. |
1367 | | */ |
1368 | | static int spop_conn_send_hello(struct spop_conn *spop_conn) |
1369 | 0 | { |
1370 | 0 | struct buffer outbuf; |
1371 | 0 | struct buffer *mbuf; |
1372 | 0 | struct buffer *chk; |
1373 | 0 | char *p, *end; |
1374 | 0 | size_t sz; |
1375 | 0 | int ret = 0; |
1376 | |
|
1377 | 0 | TRACE_ENTER(SPOP_EV_TX_FRAME|SPOP_EV_TX_HELLO, spop_conn->conn); |
1378 | |
|
1379 | 0 | mbuf = br_tail(spop_conn->mbuf); |
1380 | 0 | retry: |
1381 | 0 | if (!spop_get_buf(spop_conn, mbuf)) { |
1382 | 0 | spop_conn->flags |= SPOP_CF_MUX_MALLOC; |
1383 | 0 | spop_conn->flags |= SPOP_CF_DEM_MROOM; |
1384 | 0 | TRACE_STATE("waiting for fconn mbuf ring allocation", SPOP_EV_TX_FRAME|SPOP_EV_SPOP_CONN_BLK, spop_conn->conn); |
1385 | 0 | ret = 0; |
1386 | 0 | goto end; |
1387 | 0 | } |
1388 | | |
1389 | 0 | while (1) { |
1390 | 0 | outbuf = b_make(b_tail(mbuf), b_contig_space(mbuf), 0, 0); |
1391 | 0 | if (outbuf.size >= 11 || !b_space_wraps(mbuf)) |
1392 | 0 | break; |
1393 | 0 | realign_again: |
1394 | 0 | b_slow_realign(mbuf, trash.area, b_data(mbuf)); |
1395 | 0 | } |
1396 | | |
1397 | 0 | if (outbuf.size < 11) |
1398 | 0 | goto full; |
1399 | | |
1400 | | /* len: 4-bytes (fill later) type: (1)HAPROXY-HELLO, flags: 4-bytes (FIN=1) |
1401 | | * stream-id: 0x00, frame-id: 0x00 */ |
1402 | 0 | memcpy(outbuf.area, "\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00", 11); |
1403 | 0 | outbuf.data = 11; |
1404 | |
|
1405 | 0 | p = b_tail(&outbuf); |
1406 | 0 | end = b_orig(&outbuf) + b_size(&outbuf); |
1407 | | |
1408 | | /* "supported-versions" K/V item */ |
1409 | 0 | sz = SPOP_SLEN(SPOP_SUPPORTED_VERSIONS_KEY); |
1410 | 0 | if (spoe_encode_buffer(SPOP_SUPPORTED_VERSIONS_KEY, sz, &p, end) == -1) |
1411 | 0 | goto full; |
1412 | | |
1413 | 0 | *p++ = SPOP_DATA_T_STR; |
1414 | 0 | sz = SPOP_SLEN(SPOP_SUPPORTED_VERSIONS_VAL); |
1415 | 0 | if (spoe_encode_buffer(SPOP_SUPPORTED_VERSIONS_VAL, sz, &p, end) == -1) |
1416 | 0 | goto full; |
1417 | | |
1418 | | /* "max-fram-size" K/V item */ |
1419 | 0 | sz = SPOP_SLEN(SPOP_MAX_FRAME_SIZE_KEY); |
1420 | 0 | if (spoe_encode_buffer(SPOP_MAX_FRAME_SIZE_KEY, sz, &p, end) == -1) |
1421 | 0 | goto full; |
1422 | | |
1423 | 0 | *p++ = SPOP_DATA_T_UINT32; |
1424 | 0 | if (encode_varint(spop_conn->max_frame_size, &p, end) == -1) |
1425 | 0 | goto full; |
1426 | | |
1427 | | /* "capabilities" K/V item */ |
1428 | 0 | sz = SPOP_SLEN(SPOP_CAPABILITIES_KEY); |
1429 | 0 | if (spoe_encode_buffer(SPOP_CAPABILITIES_KEY, sz, &p, end) == -1) |
1430 | 0 | goto full; |
1431 | | |
1432 | 0 | *p++ = SPOP_DATA_T_STR; |
1433 | 0 | chk = get_trash_chunk(); |
1434 | 0 | if (spop_conn->agent && (spop_conn->agent->flags & SPOE_FL_PIPELINING)) { |
1435 | 0 | memcpy(chk->area, "pipelining", 10); |
1436 | 0 | chk->data += 10; |
1437 | 0 | } |
1438 | 0 | if (spoe_encode_buffer(chk->area, chk->data, &p, end) == -1) |
1439 | 0 | goto full; |
1440 | | |
1441 | | /* (optional) "engine-id" K/V item, if present */ |
1442 | 0 | if (spop_conn->agent && spop_conn->agent->engine_id != NULL) { |
1443 | 0 | sz = SPOP_SLEN(SPOP_ENGINE_ID_KEY); |
1444 | 0 | if (spoe_encode_buffer(SPOP_ENGINE_ID_KEY, sz, &p, end) == -1) |
1445 | 0 | goto full; |
1446 | | |
1447 | 0 | *p++ = SPOP_DATA_T_STR; |
1448 | 0 | sz = strlen(spop_conn->agent->engine_id); |
1449 | 0 | if (spoe_encode_buffer(spop_conn->agent->engine_id, sz, &p, end) == -1) |
1450 | 0 | goto full; |
1451 | 0 | } |
1452 | | /* If there is no agent attached to the opposite endpoint, it means it is a standalone connection. |
1453 | | * This only happens for healthchecks |
1454 | | */ |
1455 | 0 | if (!spop_conn->agent) { |
1456 | | /* Add "healthcheck" K/V item */ |
1457 | 0 | sz = SPOP_SLEN(SPOP_HEALTHCHECK_KEY); |
1458 | 0 | if (spoe_encode_buffer(SPOP_HEALTHCHECK_KEY, sz, &p, end) == -1) |
1459 | 0 | goto full; |
1460 | 0 | *p++ = (SPOP_DATA_T_BOOL | SPOP_DATA_FL_TRUE); |
1461 | 0 | } |
1462 | | |
1463 | 0 | outbuf.data += p - b_tail(&outbuf); |
1464 | | |
1465 | | /* update the frame's size now */ |
1466 | 0 | TRACE_PROTO("SPOP HAPROXY HELLO frame xferred", SPOP_EV_TX_FRAME|SPOP_EV_TX_HELLO, spop_conn->conn, 0, 0, (size_t[]){outbuf.data}); |
1467 | 0 | spop_set_frame_size(outbuf.area, outbuf.data - 4); |
1468 | 0 | b_add(mbuf, outbuf.data); |
1469 | 0 | ret = 1; |
1470 | |
|
1471 | 0 | end: |
1472 | 0 | TRACE_LEAVE(SPOP_EV_TX_FRAME|SPOP_EV_TX_HELLO, spop_conn->conn); |
1473 | 0 | return ret; |
1474 | 0 | full: |
1475 | | /* Too large to be encoded. For HELLO frame, it is an error */ |
1476 | 0 | if (!b_data(mbuf)) { |
1477 | 0 | TRACE_ERROR("HAPROXY HELLO frame too large", SPOP_EV_TX_FRAME|SPOP_EV_TX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
1478 | 0 | goto fail; |
1479 | 0 | } |
1480 | | |
1481 | 0 | if ((mbuf = br_tail_add(spop_conn->mbuf)) != NULL) |
1482 | 0 | goto retry; |
1483 | 0 | spop_conn->flags |= SPOP_CF_MUX_MFULL; |
1484 | 0 | spop_conn->flags |= SPOP_CF_DEM_MROOM; |
1485 | 0 | TRACE_STATE("mbuf ring full", SPOP_EV_TX_FRAME|SPOP_EV_SPOP_CONN_BLK, spop_conn->conn); |
1486 | 0 | ret = 0; |
1487 | 0 | goto end; |
1488 | 0 | fail: |
1489 | 0 | spop_conn->state = SPOP_CS_CLOSED; |
1490 | 0 | TRACE_STATE("switching to CLOSED", SPOP_EV_TX_FRAME|SPOP_EV_TX_HELLO|SPOP_EV_SPOP_CONN_END, spop_conn->conn); |
1491 | 0 | TRACE_DEVEL("leaving on error", SPOP_EV_TX_FRAME|SPOP_EV_TX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
1492 | 0 | return 0; |
1493 | 0 | } |
1494 | | |
1495 | | /* Sends an DISCONNECT frame for each active streams. Closed streams are |
1496 | | * excluded, as the streams which already received the end-of-stream. It returns |
1497 | | * > 0 if the record was sent tp all streams. Otherwise it returns 0. |
1498 | | */ |
1499 | | static int spop_conn_send_disconnect(struct spop_conn *spop_conn) |
1500 | 0 | { |
1501 | 0 | struct ist reason; |
1502 | 0 | struct buffer outbuf; |
1503 | 0 | struct buffer *mbuf; |
1504 | 0 | char *p, *end; |
1505 | 0 | size_t sz; |
1506 | 0 | int ret = 0; |
1507 | |
|
1508 | 0 | TRACE_ENTER(SPOP_EV_TX_FRAME|SPOP_EV_TX_DISCO, spop_conn->conn); |
1509 | |
|
1510 | 0 | mbuf = br_tail(spop_conn->mbuf); |
1511 | 0 | retry: |
1512 | 0 | if (!spop_get_buf(spop_conn, mbuf)) { |
1513 | 0 | spop_conn->flags |= SPOP_CF_MUX_MALLOC; |
1514 | 0 | spop_conn->flags |= SPOP_CF_DEM_MROOM; |
1515 | 0 | TRACE_STATE("waiting for fconn mbuf ring allocation", SPOP_EV_TX_FRAME|SPOP_EV_SPOP_CONN_BLK, spop_conn->conn); |
1516 | 0 | ret = 0; |
1517 | 0 | goto end; |
1518 | 0 | } |
1519 | | |
1520 | 0 | while (1) { |
1521 | 0 | outbuf = b_make(b_tail(mbuf), b_contig_space(mbuf), 0, 0); |
1522 | 0 | if (outbuf.size >= 11 || !b_space_wraps(mbuf)) |
1523 | 0 | break; |
1524 | 0 | realign_again: |
1525 | 0 | b_slow_realign(mbuf, trash.area, b_data(mbuf)); |
1526 | 0 | } |
1527 | | |
1528 | 0 | if (outbuf.size < 11) |
1529 | 0 | goto full; |
1530 | | |
1531 | | /* len: 4-bytes (fill later) type: (2)HAPROXY-DISCONNECT, flags: 4-bytes (FIN=1) |
1532 | | * stream-id: 0x00, frame-id: 0x00 */ |
1533 | 0 | memcpy(outbuf.area, "\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00", 11); |
1534 | 0 | outbuf.data = 11; |
1535 | |
|
1536 | 0 | p = b_tail(&outbuf); |
1537 | 0 | end = b_orig(&outbuf) + b_size(&outbuf); |
1538 | | |
1539 | | /* "status-code" K/V item */ |
1540 | 0 | sz = SPOP_SLEN(SPOP_STATUS_CODE_KEY); |
1541 | 0 | if (spoe_encode_buffer(SPOP_STATUS_CODE_KEY, sz, &p, end) == -1) |
1542 | 0 | goto full; |
1543 | | |
1544 | 0 | *p++ = SPOP_DATA_T_UINT32; |
1545 | 0 | if (encode_varint(spop_conn->errcode, &p, end) == -1) |
1546 | 0 | goto full; |
1547 | | |
1548 | | /* "message" K/V item */ |
1549 | 0 | sz = SPOP_SLEN(SPOP_MSG_KEY); |
1550 | 0 | if (spoe_encode_buffer(SPOP_MSG_KEY, sz, &p, end) == -1) |
1551 | 0 | goto full; |
1552 | | |
1553 | | /*Get the message corresponding to the status code */ |
1554 | 0 | reason = spop_err_reasons[spop_conn->errcode]; |
1555 | |
|
1556 | 0 | *p++ = SPOP_DATA_T_STR; |
1557 | 0 | if (spoe_encode_buffer(istptr(reason), istlen(reason), &p, end) == -1) |
1558 | 0 | goto full; |
1559 | | |
1560 | 0 | outbuf.data += p - b_tail(&outbuf); |
1561 | |
|
1562 | 0 | if (outbuf.data - 4 > spop_conn->max_frame_size) |
1563 | 0 | goto too_big; |
1564 | | |
1565 | | /* update the frame's size now */ |
1566 | 0 | TRACE_PROTO("SPOP HAPROXY DISCONNECT frame xferred", SPOP_EV_TX_FRAME|SPOP_EV_TX_DISCO, spop_conn->conn, 0, 0, (size_t[]){outbuf.data}); |
1567 | 0 | spop_set_frame_size(outbuf.area, outbuf.data - 4); |
1568 | 0 | b_add(mbuf, outbuf.data); |
1569 | 0 | spop_conn->flags |= SPOP_CF_DISCO_SENT; |
1570 | 0 | spop_conn->state = SPOP_CS_CLOSING; |
1571 | 0 | ret = 1; |
1572 | |
|
1573 | 0 | switch (spop_conn->errcode) { |
1574 | 0 | case SPOP_ERR_NONE: |
1575 | 0 | spop_conn_report_term_evt(spop_conn, muxc_tevt_type_graceful_shut); |
1576 | 0 | break; |
1577 | | |
1578 | 0 | case SPOP_ERR_IO: |
1579 | 0 | spop_conn_report_term_evt(spop_conn, muxc_tevt_type_other_err); |
1580 | 0 | break; |
1581 | | |
1582 | 0 | case SPOP_ERR_TOUT: |
1583 | 0 | spop_conn_report_term_evt(spop_conn, muxc_tevt_type_tout); |
1584 | 0 | break; |
1585 | | |
1586 | 0 | case SPOP_ERR_TOO_BIG: |
1587 | 0 | case SPOP_ERR_INVALID: |
1588 | 0 | case SPOP_ERR_NO_VSN: |
1589 | 0 | case SPOP_ERR_NO_FRAME_SIZE: |
1590 | 0 | case SPOP_ERR_NO_CAP: |
1591 | 0 | case SPOP_ERR_BAD_VSN: |
1592 | 0 | case SPOP_ERR_BAD_FRAME_SIZE: |
1593 | 0 | case SPOP_ERR_FRAG_NOT_SUPPORTED: |
1594 | 0 | case SPOP_ERR_INTERLACED_FRAMES: |
1595 | 0 | case SPOP_ERR_FRAMEID_NOTFOUND: |
1596 | 0 | spop_conn_report_term_evt(spop_conn, muxc_tevt_type_proto_err); |
1597 | 0 | break; |
1598 | | |
1599 | 0 | case SPOP_ERR_RES: |
1600 | 0 | case SPOP_ERR_UNKNOWN: |
1601 | 0 | spop_conn_report_term_evt(spop_conn, muxc_tevt_type_internal_err); |
1602 | 0 | break; |
1603 | | |
1604 | 0 | default: |
1605 | 0 | spop_conn_report_term_evt(spop_conn, muxc_tevt_type_tout); |
1606 | 0 | break; |
1607 | |
|
1608 | 0 | } |
1609 | | |
1610 | 0 | end: |
1611 | 0 | TRACE_LEAVE(SPOP_EV_TX_FRAME|SPOP_EV_TX_DISCO, spop_conn->conn); |
1612 | 0 | return ret; |
1613 | 0 | full: |
1614 | | /* Too large to be encoded. For DISCONNECT frame, it is an error */ |
1615 | 0 | if (!b_data(mbuf)) |
1616 | 0 | goto too_big; |
1617 | | |
1618 | 0 | if ((mbuf = br_tail_add(spop_conn->mbuf)) != NULL) |
1619 | 0 | goto retry; |
1620 | 0 | spop_conn->flags |= SPOP_CF_MUX_MFULL; |
1621 | 0 | spop_conn->flags |= SPOP_CF_DEM_MROOM; |
1622 | 0 | TRACE_STATE("mbuf ring full", SPOP_EV_TX_FRAME|SPOP_EV_SPOP_CONN_BLK, spop_conn->conn); |
1623 | 0 | ret = 0; |
1624 | 0 | goto end; |
1625 | | |
1626 | 0 | too_big: |
1627 | 0 | TRACE_ERROR("SPOP HAPROXY DISCO frame too large", SPOP_EV_TX_FRAME|SPOP_EV_TX_DISCO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
1628 | |
|
1629 | 0 | fail: |
1630 | 0 | spop_conn->state = SPOP_CS_CLOSED; |
1631 | 0 | TRACE_STATE("switching to CLOSED", SPOP_EV_TX_FRAME|SPOP_EV_TX_DISCO|SPOP_EV_SPOP_CONN_END, spop_conn->conn); |
1632 | 0 | TRACE_DEVEL("leaving on error", SPOP_EV_TX_FRAME|SPOP_EV_TX_DISCO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
1633 | 0 | return 0; |
1634 | 0 | } |
1635 | | |
1636 | | /* Processes an AGENT HELLO frame. Returns > 0 on success, 0 if it couldn't do |
1637 | | * anything. It is highly unexpected, but if the frame is larger than a buffer |
1638 | | * and cannot be decoded in one time, an error is triggered and the connection |
1639 | | * is closed. HELLO frame cannot be split. |
1640 | | */ |
1641 | | static int spop_conn_handle_hello(struct spop_conn *spop_conn) |
1642 | 0 | { |
1643 | 0 | struct buffer *dbuf; |
1644 | 0 | char *p, *end; |
1645 | 0 | int vsn, max_frame_size; |
1646 | 0 | unsigned int flags; |
1647 | |
|
1648 | 0 | TRACE_ENTER(SPOP_EV_RX_FRAME|SPOP_EV_RX_HELLO, spop_conn->conn); |
1649 | |
|
1650 | 0 | dbuf = &spop_conn->dbuf; |
1651 | | |
1652 | | /* Record too large to be fully decoded */ |
1653 | 0 | if (b_size(dbuf) < (spop_conn->dfl)) |
1654 | 0 | goto fail; |
1655 | | |
1656 | | /* process full record only */ |
1657 | 0 | if (b_data(dbuf) < (spop_conn->dfl)) { |
1658 | 0 | spop_conn->flags |= SPOP_CF_DEM_SHORT_READ; |
1659 | 0 | TRACE_DEVEL("leaving on missing data", SPOP_EV_RX_FRAME|SPOP_EV_RX_HELLO, spop_conn->conn); |
1660 | 0 | return 0; |
1661 | 0 | } |
1662 | | |
1663 | 0 | if (unlikely(b_contig_data(dbuf, 0) < spop_conn->dfl)) { |
1664 | | /* Realign the dmux buffer if the frame wraps. It is unexpected |
1665 | | * at this stage because it should be the first record received |
1666 | | * from the SPOA. |
1667 | | */ |
1668 | 0 | b_slow_realign_ofs(dbuf, trash.area, 0); |
1669 | 0 | } |
1670 | |
|
1671 | 0 | p = b_head(dbuf); |
1672 | 0 | end = p + spop_conn->dfl; |
1673 | | |
1674 | | /* There are 3 mandatory items: "version", "max-frame-size" and |
1675 | | * "capabilities" */ |
1676 | | |
1677 | | /* Loop on K/V items */ |
1678 | 0 | vsn = flags = 0; |
1679 | 0 | max_frame_size = spop_conn->max_frame_size; |
1680 | 0 | while (p < end) { |
1681 | 0 | char *str; |
1682 | 0 | uint64_t sz; |
1683 | 0 | int ret; |
1684 | | |
1685 | | /* Decode the item key */ |
1686 | 0 | ret = spoe_decode_buffer(&p, end, &str, &sz); |
1687 | 0 | if (ret == -1 || !sz) { |
1688 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1689 | 0 | goto fail; |
1690 | 0 | } |
1691 | | |
1692 | | /* Check "version" K/V item */ |
1693 | 0 | if (sz >= strlen(SPOP_VERSION_KEY) && !memcmp(str, SPOP_VERSION_KEY, strlen(SPOP_VERSION_KEY))) { |
1694 | 0 | int type = *p++; |
1695 | | |
1696 | | /* The value must be a string */ |
1697 | 0 | if ((type & SPOP_DATA_T_MASK) != SPOP_DATA_T_STR) { |
1698 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1699 | 0 | goto fail; |
1700 | 0 | } |
1701 | 0 | if (spoe_decode_buffer(&p, end, &str, &sz) == -1) { |
1702 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1703 | 0 | goto fail; |
1704 | 0 | } |
1705 | | |
1706 | 0 | vsn = spoe_str_to_vsn(str, sz); |
1707 | 0 | if (vsn == -1) { |
1708 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1709 | 0 | goto fail; |
1710 | 0 | } |
1711 | 0 | if (spoe_check_vsn(vsn) == -1) { |
1712 | 0 | spop_conn_error(spop_conn, SPOP_ERR_BAD_VSN); |
1713 | 0 | goto fail; |
1714 | 0 | } |
1715 | 0 | } |
1716 | | /* Check "max-frame-size" K/V item */ |
1717 | 0 | else if (sz >= strlen(SPOP_MAX_FRAME_SIZE_KEY) && !memcmp(str, SPOP_MAX_FRAME_SIZE_KEY, SPOP_SLEN(SPOP_MAX_FRAME_SIZE_KEY))) { |
1718 | 0 | int type = *p++; |
1719 | | |
1720 | | /* The value must be integer */ |
1721 | 0 | if ((type & SPOP_DATA_T_MASK) != SPOP_DATA_T_INT32 && |
1722 | 0 | (type & SPOP_DATA_T_MASK) != SPOP_DATA_T_INT64 && |
1723 | 0 | (type & SPOP_DATA_T_MASK) != SPOP_DATA_T_UINT32 && |
1724 | 0 | (type & SPOP_DATA_T_MASK) != SPOP_DATA_T_UINT64) { |
1725 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1726 | 0 | goto fail; |
1727 | 0 | } |
1728 | 0 | if (decode_varint(&p, end, &sz) == -1) { |
1729 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1730 | 0 | goto fail; |
1731 | 0 | } |
1732 | 0 | if (sz < SPOP_MIN_FRAME_SIZE || sz > spop_conn->max_frame_size) { |
1733 | 0 | spop_conn_error(spop_conn, SPOP_ERR_BAD_FRAME_SIZE); |
1734 | 0 | goto fail; |
1735 | 0 | } |
1736 | 0 | max_frame_size = sz; |
1737 | 0 | } |
1738 | | /* Check "capabilities" K/V item */ |
1739 | 0 | else if (sz >= strlen(SPOP_CAPABILITIES_KEY) && !memcmp(str, SPOP_CAPABILITIES_KEY, SPOP_SLEN(SPOP_CAPABILITIES_KEY))) { |
1740 | 0 | int type = *p++; |
1741 | | |
1742 | | /* The value must be a string */ |
1743 | 0 | if ((type & SPOP_DATA_T_MASK) != SPOP_DATA_T_STR) { |
1744 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1745 | 0 | goto fail; |
1746 | 0 | } |
1747 | 0 | if (spoe_decode_buffer(&p, end, &str, &sz) == -1) { |
1748 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1749 | 0 | goto fail; |
1750 | 0 | } |
1751 | | |
1752 | 0 | while (sz) { |
1753 | 0 | char *delim; |
1754 | | |
1755 | | /* Skip leading spaces */ |
1756 | 0 | for (; isspace((unsigned char)*str) && sz; str++, sz--); |
1757 | |
|
1758 | 0 | if (sz >= 10 && !strncmp(str, "pipelining", 10)) { |
1759 | 0 | str += 10; sz -= 10; |
1760 | 0 | if (!sz || isspace((unsigned char)*str) || *str == ',') |
1761 | 0 | flags |= SPOE_FL_PIPELINING; |
1762 | 0 | } |
1763 | | |
1764 | | /* Get the next comma or break */ |
1765 | 0 | if (!sz || (delim = memchr(str, ',', sz)) == NULL) |
1766 | 0 | break; |
1767 | 0 | delim++; |
1768 | 0 | sz -= (delim - str); |
1769 | 0 | str = delim; |
1770 | 0 | } |
1771 | 0 | } |
1772 | 0 | else { |
1773 | | /* Silently ignore unknown item */ |
1774 | 0 | if (spoe_skip_data(&p, end) == -1) { |
1775 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1776 | 0 | goto fail; |
1777 | 0 | } |
1778 | 0 | } |
1779 | 0 | } |
1780 | | |
1781 | | /* Final checks */ |
1782 | 0 | if (!vsn) { |
1783 | 0 | spop_conn_error(spop_conn, SPOP_ERR_NO_VSN); |
1784 | 0 | goto fail; |
1785 | 0 | } |
1786 | 0 | if (!max_frame_size) { |
1787 | 0 | spop_conn_error(spop_conn, SPOP_ERR_NO_FRAME_SIZE); |
1788 | 0 | goto fail; |
1789 | 0 | } |
1790 | | |
1791 | 0 | if (!(flags & SPOE_FL_PIPELINING)) |
1792 | 0 | spop_conn->streams_limit = 1; |
1793 | 0 | spop_conn->max_frame_size = (unsigned int)max_frame_size; |
1794 | | |
1795 | |
|
1796 | 0 | TRACE_PROTO("SPOP AGENT HELLO frame rcvd", SPOP_EV_RX_FRAME|SPOP_EV_RX_HELLO, spop_conn->conn, 0, 0, (size_t[]){spop_conn->dfl}); |
1797 | 0 | b_del(&spop_conn->dbuf, spop_conn->dfl); |
1798 | 0 | spop_conn->dfl = 0; |
1799 | 0 | spop_conn->state = SPOP_CS_RUNNING; |
1800 | 0 | spop_wake_unassigned_streams(spop_conn); |
1801 | 0 | TRACE_LEAVE(SPOP_EV_RX_FRAME|SPOP_EV_RX_HELLO, spop_conn->conn); |
1802 | 0 | return 1; |
1803 | 0 | fail: |
1804 | 0 | TRACE_DEVEL("leaving on error", SPOP_EV_RX_FRAME|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
1805 | 0 | return 0; |
1806 | 0 | } |
1807 | | |
1808 | | /* Processes an AGENT DISCONNECT frame. Returns > 0 on success, 0 if it couldn't |
1809 | | * do anything. It is highly unexpected, but if the frame is larger than a |
1810 | | * buffer and cannot be decoded in one time, an error is triggered and the |
1811 | | * connection is closed. DISCONNECT frame cannot be split. |
1812 | | */ |
1813 | | static int spop_conn_handle_disconnect(struct spop_conn *spop_conn) |
1814 | 0 | { |
1815 | 0 | struct buffer *dbuf; |
1816 | 0 | char *p, *end; |
1817 | | //struct ist reason; |
1818 | 0 | unsigned int status_code, flags; |
1819 | |
|
1820 | 0 | TRACE_ENTER(SPOP_EV_RX_FRAME|SPOP_EV_RX_DISCO, spop_conn->conn); |
1821 | |
|
1822 | 0 | dbuf = &spop_conn->dbuf; |
1823 | | |
1824 | | /* Record too large to be fully decoded */ |
1825 | 0 | if (b_size(dbuf) < (spop_conn->dfl)) |
1826 | 0 | goto fail; |
1827 | | |
1828 | | /* process full record only */ |
1829 | 0 | if (b_data(dbuf) < (spop_conn->dfl)) { |
1830 | 0 | spop_conn->flags |= SPOP_CF_DEM_SHORT_READ; |
1831 | 0 | TRACE_DEVEL("leaving on missing data", SPOP_EV_RX_FRAME|SPOP_EV_RX_DISCO, spop_conn->conn); |
1832 | 0 | return 0; |
1833 | 0 | } |
1834 | | |
1835 | 0 | if (unlikely(b_contig_data(dbuf, 0) < spop_conn->dfl)) |
1836 | 0 | b_slow_realign_ofs(dbuf, trash.area, 0); |
1837 | |
|
1838 | 0 | p = b_head(dbuf); |
1839 | 0 | end = p + spop_conn->dfl; |
1840 | | |
1841 | | /* There are 2 mandatory items: "status-code" and "message" */ |
1842 | | |
1843 | | /* Loop on K/V items */ |
1844 | 0 | status_code = flags = 0; |
1845 | 0 | while (p < end) { |
1846 | 0 | char *str; |
1847 | 0 | uint64_t sz; |
1848 | 0 | int ret; |
1849 | | |
1850 | | /* Decode the item key */ |
1851 | 0 | ret = spoe_decode_buffer(&p, end, &str, &sz); |
1852 | 0 | if (ret == -1 || !sz) { |
1853 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1854 | 0 | goto fail; |
1855 | 0 | } |
1856 | | |
1857 | | /* Check "status-code" K/V item */ |
1858 | 0 | if (sz >= strlen(SPOP_STATUS_CODE_KEY) && !memcmp(str, SPOP_STATUS_CODE_KEY, strlen(SPOP_STATUS_CODE_KEY))) { |
1859 | 0 | int type = *p++; |
1860 | | |
1861 | | /* The value must be an integer */ |
1862 | 0 | if ((type & SPOP_DATA_T_MASK) != SPOP_DATA_T_INT32 && |
1863 | 0 | (type & SPOP_DATA_T_MASK) != SPOP_DATA_T_INT64 && |
1864 | 0 | (type & SPOP_DATA_T_MASK) != SPOP_DATA_T_UINT32 && |
1865 | 0 | (type & SPOP_DATA_T_MASK) != SPOP_DATA_T_UINT64) { |
1866 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1867 | 0 | goto fail; |
1868 | 0 | } |
1869 | 0 | if (decode_varint(&p, end, &sz) == -1) { |
1870 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1871 | 0 | goto fail; |
1872 | 0 | } |
1873 | 0 | status_code = sz; |
1874 | 0 | } |
1875 | | // TODO: for now skip the reason |
1876 | | /* /\* Check "message" K/V item *\/ */ |
1877 | | /* else if (sz >= strlen(SPOP_MSG_KEY) && !memcmp(str, SPOP_MSG_KEY, strlen(SPOP_MSG_KEY))) { */ |
1878 | | /* int type = *p++; */ |
1879 | | |
1880 | | /* /\* The value must be a string *\/ */ |
1881 | | /* if ((type & SPOP_DATA_T_MASK) != SPOP_DATA_T_STR) { */ |
1882 | | /* spop_conn_error(spop_conn, SPOP_ERR_INVALID); */ |
1883 | | /* goto fail; */ |
1884 | | /* } */ |
1885 | | /* ret = spoe_decode_buffer(&p, end, &str, &sz); */ |
1886 | | /* if (ret == -1 || sz > 255) { */ |
1887 | | /* spop_conn_error(spop_conn, SPOP_ERR_INVALID); */ |
1888 | | /* goto fail; */ |
1889 | | /* } */ |
1890 | | /* reason = ist2(str, sz); */ |
1891 | | /* } */ |
1892 | 0 | else { |
1893 | | /* Silently ignore unknown item */ |
1894 | 0 | if (spoe_skip_data(&p, end) == -1) { |
1895 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
1896 | 0 | goto fail; |
1897 | 0 | } |
1898 | 0 | } |
1899 | 0 | } |
1900 | | |
1901 | 0 | TRACE_PROTO("SPOP AGENT DISCONNECT frame rcvd", SPOP_EV_RX_FRAME|SPOP_EV_RX_DISCO, spop_conn->conn, 0, 0, (size_t[]){spop_conn->dfl}); |
1902 | 0 | b_del(&spop_conn->dbuf, spop_conn->dfl); |
1903 | 0 | spop_conn->dfl = 0; |
1904 | 0 | spop_conn_error(spop_conn, status_code); |
1905 | 0 | spop_conn->state = SPOP_CS_CLOSED; |
1906 | 0 | spop_wake_some_streams(spop_conn, 0/*last*/); |
1907 | 0 | spop_conn_report_term_evt(spop_conn, muxc_tevt_type_goaway_rcvd); |
1908 | 0 | TRACE_LEAVE(SPOP_EV_RX_FRAME|SPOP_EV_RX_DISCO, spop_conn->conn); |
1909 | 0 | return 1; |
1910 | 0 | fail: |
1911 | 0 | spop_conn->state = SPOP_CS_CLOSED; |
1912 | 0 | TRACE_STATE("switching to CLOSED", SPOP_EV_RX_FRAME|SPOP_EV_RX_DISCO, spop_conn->conn); |
1913 | 0 | TRACE_DEVEL("leaving on error", SPOP_EV_RX_FRAME|SPOP_EV_RX_DISCO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
1914 | 0 | return 0; |
1915 | 0 | } |
1916 | | |
1917 | | /* Processes an AGENT ACK frame. Returns > 0 on success, 0 if it couldn't |
1918 | | * do anything. |
1919 | | * |
1920 | | * TODO: for now cannot be fragmented. |
1921 | | */ |
1922 | | static int spop_conn_handle_ack(struct spop_conn *spop_conn, struct spop_strm *spop_strm) |
1923 | 0 | { |
1924 | 0 | struct buffer *dbuf, *rxbuf; |
1925 | 0 | unsigned int flen, sent = 0; |
1926 | |
|
1927 | 0 | TRACE_ENTER(SPOP_EV_RX_FRAME|SPOP_EV_RX_ACK, spop_conn->conn, spop_strm); |
1928 | |
|
1929 | 0 | dbuf = &spop_conn->dbuf; |
1930 | | |
1931 | | /* Record too large to be fully decoded */ |
1932 | 0 | if (b_size(dbuf) < (spop_conn->dfl)) |
1933 | 0 | goto fail; |
1934 | | |
1935 | | /* process full record only */ |
1936 | 0 | if (b_data(dbuf) < (spop_conn->dfl)) { |
1937 | 0 | spop_conn->flags |= SPOP_CF_DEM_SHORT_READ; |
1938 | 0 | TRACE_DEVEL("leaving on missing data", SPOP_EV_RX_FRAME|SPOP_EV_RX_DISCO, spop_conn->conn); |
1939 | 0 | return 0; |
1940 | 0 | } |
1941 | | |
1942 | 0 | if (unlikely(b_contig_data(dbuf, 0) < spop_conn->dfl)) |
1943 | 0 | b_slow_realign_ofs(dbuf, trash.area, 0); |
1944 | |
|
1945 | 0 | spop_conn->flags &= ~SPOP_CF_DEM_SFULL; |
1946 | 0 | rxbuf = spop_get_buf(spop_conn, &spop_strm->rxbuf); |
1947 | 0 | if (!rxbuf) { |
1948 | 0 | spop_conn->flags |= SPOP_CF_DEM_SALLOC; |
1949 | 0 | TRACE_STATE("waiting for an spop_strm rxbuf", SPOP_EV_RX_FRAME|SPOP_EV_RX_ACK|SPOP_EV_SPOP_STRM_BLK, spop_conn->conn, spop_strm); |
1950 | 0 | goto fail; |
1951 | 0 | } |
1952 | | |
1953 | 0 | flen = spop_conn->dfl; |
1954 | 0 | if (!flen) { |
1955 | 0 | if (!b_room(rxbuf)) { |
1956 | 0 | spop_conn->flags |= SPOP_CF_DEM_SFULL; |
1957 | 0 | TRACE_STATE("spop_strm rxbuf is full", SPOP_EV_RX_FRAME|SPOP_EV_RX_ACK|SPOP_EV_SPOP_STRM_BLK, spop_conn->conn, spop_strm); |
1958 | 0 | goto fail; |
1959 | 0 | } |
1960 | 0 | b_putchr(rxbuf, SPOP_ACT_T_NOOP); |
1961 | 0 | sent = 1; |
1962 | 0 | goto end; |
1963 | 0 | } |
1964 | | |
1965 | | // TODO: For now we know all data were received |
1966 | | /* if (flen > b_data(&h2c->dbuf)) { */ |
1967 | | /* flen = b_data(&h2c->dbuf); */ |
1968 | | /* if (!flen) */ |
1969 | | /* goto fail; */ |
1970 | | /* } */ |
1971 | | |
1972 | | // TODO: for now, we take care to xfer all data at once ! |
1973 | 0 | if (flen > b_room(rxbuf)) { |
1974 | 0 | spop_conn->flags |= SPOP_CF_DEM_SFULL; |
1975 | 0 | TRACE_STATE("spop_strm rxbuf is full", SPOP_EV_RX_FRAME|SPOP_EV_RX_ACK|SPOP_EV_SPOP_STRM_BLK, spop_conn->conn, spop_strm); |
1976 | 0 | goto fail; |
1977 | 0 | } |
1978 | | |
1979 | 0 | sent = b_xfer(rxbuf, dbuf, flen); |
1980 | 0 | BUG_ON(sent != flen); |
1981 | 0 | spop_conn->dfl -= sent; |
1982 | |
|
1983 | 0 | end: |
1984 | 0 | if (spop_strm->state == SPOP_SS_OPEN) |
1985 | 0 | spop_strm->state = SPOP_SS_HREM; |
1986 | 0 | else |
1987 | 0 | spop_strm_close(spop_strm); |
1988 | |
|
1989 | 0 | spop_strm->flags |= SPOP_SF_ACK_RCVD; |
1990 | 0 | TRACE_PROTO("SPOP AGENT ACK frame rcvd", SPOP_EV_RX_FRAME|SPOP_EV_RX_ACK, spop_conn->conn, spop_strm, 0, (size_t[]){sent}); |
1991 | 0 | TRACE_LEAVE(SPOP_EV_RX_FRAME|SPOP_EV_RX_ACK, spop_conn->conn, spop_strm); |
1992 | 0 | return 1; |
1993 | | |
1994 | 0 | fail: |
1995 | 0 | TRACE_DEVEL("leaving on error", SPOP_EV_RX_FRAME|SPOP_EV_RX_ACK|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn, spop_strm); |
1996 | 0 | return 0; |
1997 | 0 | } |
1998 | | |
1999 | | /* resume each spop_strm eligible for sending in list head <head> */ |
2000 | | static void spop_resume_each_sending_spop_strm(struct spop_conn *spop_conn, struct list *head) |
2001 | 0 | { |
2002 | 0 | struct spop_strm *spop_strm, *spop_strm_back; |
2003 | |
|
2004 | 0 | TRACE_ENTER(SPOP_EV_SPOP_CONN_SEND|SPOP_EV_STRM_WAKE, spop_conn->conn); |
2005 | |
|
2006 | 0 | list_for_each_entry_safe(spop_strm, spop_strm_back, head, list) { |
2007 | 0 | if (spop_conn->flags & SPOP_CF_MUX_BLOCK_ANY || |
2008 | 0 | spop_conn->state >= SPOP_CS_ERROR) |
2009 | 0 | break; |
2010 | | |
2011 | 0 | spop_strm->flags &= ~SPOP_SF_BLK_ANY; |
2012 | |
|
2013 | 0 | if (spop_strm->flags & SPOP_SF_NOTIFIED) |
2014 | 0 | continue; |
2015 | | |
2016 | | /* If the sender changed his mind and unsubscribed, let's just |
2017 | | * remove the stream from the send_list. |
2018 | | */ |
2019 | 0 | if (!spop_strm->subs || !(spop_strm->subs->events & SUB_RETRY_SEND)) { |
2020 | 0 | LIST_DEL_INIT(&spop_strm->list); |
2021 | 0 | continue; |
2022 | 0 | } |
2023 | | |
2024 | 0 | spop_strm_notify_send(spop_strm); |
2025 | 0 | } |
2026 | |
|
2027 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_CONN_SEND|SPOP_EV_STRM_WAKE, spop_conn->conn); |
2028 | 0 | } |
2029 | | |
2030 | | /* removes a stream from the list it may be in. If a stream has recently been |
2031 | | * appended to the send_list, it might have been waiting on this one when |
2032 | | * entering spop_snd_buf() and expecting it to complete before starting to send |
2033 | | * in turn. For this reason we check (and clear) SPOP_CF_WAIT_INLIST to detect |
2034 | | * this condition, and we try to resume sending streams if it happens. Note |
2035 | | * that we don't need to do it for fctl_list as this list is relevant before |
2036 | | * (only consulted after) a window update on the connection, and not because |
2037 | | * of any competition with other streams. |
2038 | | */ |
2039 | | static inline void spop_remove_from_list(struct spop_strm *spop_strm) |
2040 | 0 | { |
2041 | 0 | struct spop_conn *spop_conn = spop_strm->spop_conn; |
2042 | |
|
2043 | 0 | if (!LIST_INLIST(&spop_strm->list)) |
2044 | 0 | return; |
2045 | | |
2046 | 0 | LIST_DEL_INIT(&spop_strm->list); |
2047 | 0 | if (spop_conn->flags & SPOP_CF_WAIT_INLIST) { |
2048 | 0 | spop_conn->flags &= ~SPOP_CF_WAIT_INLIST; |
2049 | 0 | spop_resume_each_sending_spop_strm(spop_conn, &spop_conn->send_list); |
2050 | 0 | } |
2051 | 0 | } |
2052 | | |
2053 | | |
2054 | | /* process Rx records to be demultiplexed */ |
2055 | | static void spop_process_demux(struct spop_conn *spop_conn) |
2056 | 0 | { |
2057 | 0 | struct spop_strm *spop_strm = NULL, *tmp_spop_strm; |
2058 | 0 | struct spop_frame_header hdr; |
2059 | |
|
2060 | 0 | TRACE_ENTER(SPOP_EV_SPOP_CONN_WAKE, spop_conn->conn); |
2061 | |
|
2062 | 0 | if (spop_conn->state == SPOP_CS_CLOSED) |
2063 | 0 | goto out; |
2064 | | |
2065 | 0 | if (unlikely(spop_conn->state < SPOP_CS_RUNNING || spop_conn->state == SPOP_CS_CLOSING)) { |
2066 | 0 | if (spop_conn->state == SPOP_CS_HA_HELLO) { |
2067 | 0 | TRACE_STATE("waiting AGENT HELLO frame to be sent", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO, spop_conn->conn); |
2068 | 0 | goto out; |
2069 | 0 | } |
2070 | 0 | else if (spop_conn->dsi == -1) { /* AGENT_HELLO OR CLOSING */ |
2071 | | /* ensure that what is pending is a valid AGENT HELLO/DISCONNECT frame. */ |
2072 | 0 | TRACE_STATE("receiving AGENT HELLO/DISCONNECT frame header", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR, spop_conn->conn); |
2073 | 0 | if (!spop_get_frame_hdr(&spop_conn->dbuf, &hdr)) { |
2074 | 0 | spop_conn->flags |= SPOP_CF_DEM_SHORT_READ; |
2075 | 0 | TRACE_DEVEL("header frame not available yet", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR, spop_conn->conn); |
2076 | 0 | goto done; |
2077 | 0 | } |
2078 | | |
2079 | 0 | if (spop_conn->state == SPOP_CS_AGENT_HELLO && hdr.type != SPOP_FRM_T_AGENT_HELLO) { |
2080 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
2081 | 0 | spop_conn->state = SPOP_CS_CLOSED; |
2082 | 0 | TRACE_ERROR("unexpected frame type (AGENT HELLO expected)", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2083 | 0 | TRACE_STATE("switching to CLOSED", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2084 | 0 | goto done; |
2085 | 0 | } |
2086 | 0 | if ((hdr.type == SPOP_FRM_T_AGENT_HELLO || hdr.type == SPOP_FRM_T_AGENT_DISCON) && (hdr.sid || hdr.fid || !(hdr.flags & SPOP_FRM_FL_FIN))) { |
2087 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
2088 | 0 | spop_conn->state = SPOP_CS_CLOSED; |
2089 | 0 | TRACE_ERROR("unexpected frame meta-data", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2090 | 0 | TRACE_STATE("switching to CLOSED", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2091 | 0 | goto done; |
2092 | 0 | } |
2093 | | |
2094 | 0 | if ((int)hdr.len < 0 || (int)hdr.len > spop_conn->max_frame_size) { |
2095 | 0 | spop_conn_error(spop_conn, SPOP_ERR_BAD_FRAME_SIZE); |
2096 | 0 | spop_conn->state = SPOP_CS_CLOSED; |
2097 | 0 | TRACE_ERROR("invalid AGENT HELLO frame length", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2098 | 0 | TRACE_STATE("switching to CLOSED", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_RX_HELLO|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2099 | 0 | goto done; |
2100 | 0 | } |
2101 | | |
2102 | 0 | goto new_frame; |
2103 | 0 | } |
2104 | 0 | } |
2105 | | |
2106 | | /* process as many incoming frames as possible below */ |
2107 | 0 | while (1) { |
2108 | 0 | int ret = 0; |
2109 | |
|
2110 | 0 | if (!b_data(&spop_conn->dbuf)) { |
2111 | 0 | TRACE_DEVEL("no more Rx data", SPOP_EV_RX_FRAME, spop_conn->conn); |
2112 | 0 | spop_conn->flags |= SPOP_CF_DEM_SHORT_READ; |
2113 | 0 | break; |
2114 | 0 | } |
2115 | | |
2116 | 0 | if (spop_conn->state == SPOP_CS_CLOSED) { |
2117 | 0 | TRACE_STATE("end of connection reported", SPOP_EV_RX_FRAME|SPOP_EV_RX_EOI, spop_conn->conn); |
2118 | 0 | break; |
2119 | 0 | } |
2120 | | |
2121 | 0 | if (spop_conn->dsi == -1) { |
2122 | 0 | TRACE_PROTO("receiving SPOP frame header", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR, spop_conn->conn); |
2123 | 0 | if (!spop_get_frame_hdr(&spop_conn->dbuf, &hdr)) { |
2124 | 0 | spop_conn->flags |= SPOP_CF_DEM_SHORT_READ; |
2125 | 0 | break; |
2126 | 0 | } |
2127 | | |
2128 | 0 | if ((int)hdr.len < 0 || (int)hdr.len > spop_conn->max_frame_size) { |
2129 | 0 | spop_conn_error(spop_conn, SPOP_ERR_BAD_FRAME_SIZE); |
2130 | 0 | TRACE_ERROR("invalid SPOP frame length", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2131 | 0 | TRACE_STATE("switching to ERROR", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2132 | 0 | break; |
2133 | 0 | } |
2134 | | |
2135 | 0 | new_frame: |
2136 | 0 | spop_conn->dsi = hdr.sid; |
2137 | 0 | spop_conn->dfi = hdr.fid; |
2138 | 0 | spop_conn->dft = hdr.type; |
2139 | 0 | spop_conn->dfl = hdr.len; |
2140 | 0 | spop_conn->dff = hdr.flags; |
2141 | 0 | TRACE_STATE("SPOP frame header rcvd", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR, spop_conn->conn); |
2142 | | |
2143 | | /* Perform sanity check on the frame header */ |
2144 | 0 | if (!(spop_conn->dff & SPOP_FRM_FL_FIN)) { |
2145 | 0 | spop_conn_error(spop_conn, SPOP_ERR_FRAG_NOT_SUPPORTED); |
2146 | 0 | TRACE_ERROR("frame fragmentation not supported", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2147 | 0 | TRACE_STATE("switching to ERROR", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2148 | 0 | break; |
2149 | 0 | } |
2150 | 0 | if (!spop_conn->dsi && spop_conn->dft == SPOP_FRM_T_AGENT_ACK) { |
2151 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
2152 | 0 | TRACE_ERROR("invalid SPOP frame (ACK && dsi == 0)", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2153 | 0 | TRACE_STATE("switching to ERROR", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2154 | 0 | break; |
2155 | 0 | } |
2156 | 0 | if (spop_conn->dsi && !spop_conn->dfi) { |
2157 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
2158 | 0 | TRACE_ERROR("invalid SPOP frame (dsi != 0 && dfi == 0)", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2159 | 0 | TRACE_STATE("switching to ERROR", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2160 | 0 | break; |
2161 | 0 | } |
2162 | 0 | } |
2163 | | |
2164 | 0 | tmp_spop_strm = spop_conn_st_by_id(spop_conn, spop_conn->dsi); |
2165 | 0 | if (tmp_spop_strm != spop_strm && spop_strm && spop_strm_sc(spop_strm) && |
2166 | 0 | (b_data(&spop_strm->rxbuf) || |
2167 | 0 | spop_conn_read0_pending(spop_conn) || |
2168 | 0 | spop_strm->state == SPOP_SS_CLOSED || |
2169 | 0 | (spop_strm->flags & SPOP_SF_ACK_RCVD) || |
2170 | 0 | se_fl_test(spop_strm->sd, SE_FL_ERROR | SE_FL_ERR_PENDING | SE_FL_EOS))) { |
2171 | | /* we may have to signal the upper layers */ |
2172 | 0 | TRACE_DEVEL("notifying stream before switching SID", SPOP_EV_RX_FRAME|SPOP_EV_STRM_WAKE, spop_conn->conn, spop_strm); |
2173 | 0 | se_fl_set(spop_strm->sd, SE_FL_RCV_MORE); |
2174 | 0 | spop_strm_notify_recv(spop_strm); |
2175 | 0 | } |
2176 | 0 | spop_strm = tmp_spop_strm; |
2177 | | |
2178 | | /* Perform sanity checks on the SPOP stream */ |
2179 | 0 | if (spop_strm == spop_unknown_stream) { |
2180 | 0 | if (spop_conn->dsi > spop_conn->max_id) { |
2181 | 0 | spop_conn_error(spop_conn, SPOP_ERR_FRAMEID_NOTFOUND); |
2182 | 0 | TRACE_ERROR("invalid SPOP frame (dsi > max_id)", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2183 | 0 | TRACE_STATE("switching to CLOSED", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2184 | 0 | break; |
2185 | 0 | } |
2186 | 0 | else { |
2187 | | /* stream not found, probably because it aborted */ |
2188 | 0 | goto ignore_frame; |
2189 | 0 | } |
2190 | 0 | } |
2191 | 0 | else if (spop_strm == spop_closed_stream) { |
2192 | 0 | if (spop_conn->dft != SPOP_FRM_T_AGENT_HELLO && spop_conn->dft != SPOP_FRM_T_AGENT_DISCON) { |
2193 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
2194 | 0 | TRACE_ERROR("invalid SPOP frame (dsi == 0)", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2195 | 0 | TRACE_STATE("switching to CLOSED", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2196 | 0 | break; |
2197 | 0 | } |
2198 | 0 | } |
2199 | 0 | else { |
2200 | 0 | if (spop_conn->dfi == spop_strm->fid) { |
2201 | | // OK, no problem |
2202 | 0 | } |
2203 | 0 | else if (spop_conn->dfi > spop_strm->fid) { |
2204 | 0 | spop_conn_error(spop_conn, SPOP_ERR_FRAMEID_NOTFOUND); |
2205 | 0 | TRACE_ERROR("invalid SPOP frame (dfi > frame-id)", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2206 | 0 | TRACE_STATE("switching to CLOSED", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2207 | 0 | break; |
2208 | 0 | } |
2209 | 0 | else { |
2210 | | /* frame id not found, probably because it aborted */ |
2211 | 0 | goto ignore_frame; |
2212 | 0 | } |
2213 | 0 | } |
2214 | | |
2215 | 0 | switch (spop_conn->dft) { |
2216 | 0 | case SPOP_FRM_T_AGENT_HELLO: |
2217 | 0 | if (spop_conn->dsi == 0) |
2218 | 0 | ret = spop_conn_handle_hello(spop_conn); |
2219 | 0 | break; |
2220 | 0 | case SPOP_FRM_T_AGENT_DISCON: |
2221 | 0 | if (spop_conn->dsi == 0) |
2222 | 0 | ret = spop_conn_handle_disconnect(spop_conn); |
2223 | 0 | break; |
2224 | 0 | case SPOP_FRM_T_AGENT_ACK: |
2225 | 0 | if (spop_conn->dsi > 0) |
2226 | 0 | ret = spop_conn_handle_ack(spop_conn, spop_strm); |
2227 | 0 | break; |
2228 | 0 | default: |
2229 | 0 | ignore_frame: |
2230 | 0 | TRACE_PROTO("receiving SPOP ignored frame", SPOP_EV_RX_FRAME, spop_conn->conn, spop_strm); |
2231 | | /* drop frames that we ignore. */ |
2232 | 0 | ret = MIN(b_data(&spop_conn->dbuf), spop_conn->dfl); |
2233 | 0 | b_del(&spop_conn->dbuf, ret); |
2234 | 0 | spop_conn->dfl -= ret; |
2235 | 0 | ret = (spop_conn->dfl == 0); |
2236 | 0 | break; |
2237 | 0 | } |
2238 | | |
2239 | | // TODO: SS_ERROR to CS_ERROR ? |
2240 | | |
2241 | 0 | if (spop_conn->state == SPOP_CS_ERROR) { |
2242 | 0 | TRACE_PROTO("sending SPOP HAPROXY DISCONNECT frame", SPOP_EV_TX_FRAME|SPOP_EV_TX_DISCO|SPOP_EV_TX_EOI, spop_conn->conn, spop_strm); |
2243 | 0 | ret = spop_conn_send_disconnect(spop_conn); |
2244 | 0 | } |
2245 | | |
2246 | | /* error or missing data condition met above ? */ |
2247 | 0 | if (ret <= 0) |
2248 | 0 | break; |
2249 | | |
2250 | 0 | if (spop_conn->dsi >= 0) { |
2251 | 0 | if (spop_conn->dfl) { |
2252 | 0 | TRACE_DEVEL("skipping remaining frame payload", SPOP_EV_RX_FRAME, spop_conn->conn, spop_strm); |
2253 | 0 | ret = MIN(b_data(&spop_conn->dbuf), spop_conn->dfl); |
2254 | 0 | b_del(&spop_conn->dbuf, ret); |
2255 | 0 | spop_conn->dfl -= ret; |
2256 | 0 | } |
2257 | 0 | if (!spop_conn->dfl) { |
2258 | 0 | TRACE_STATE("reset dsi", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR, spop_conn->conn); |
2259 | 0 | spop_conn->dsi = -1; |
2260 | 0 | } |
2261 | 0 | } |
2262 | 0 | } |
2263 | | |
2264 | 0 | done: |
2265 | 0 | if (spop_conn->state >= SPOP_CS_ERROR || (spop_conn->flags & SPOP_CF_DEM_SHORT_READ)) { |
2266 | 0 | if (spop_conn->flags & SPOP_CF_RCVD_SHUT) |
2267 | 0 | spop_conn->flags |= SPOP_CF_END_REACHED; |
2268 | 0 | } |
2269 | |
|
2270 | 0 | if (spop_conn_read0_pending(spop_conn) && (spop_conn->flags & SPOP_CF_DEM_SHORT_READ) && b_data(&spop_conn->dbuf)) { |
2271 | 0 | spop_conn_error(spop_conn, SPOP_ERR_INVALID); |
2272 | 0 | spop_conn->state = SPOP_CS_CLOSED; |
2273 | 0 | TRACE_ERROR("truncated data", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2274 | 0 | TRACE_STATE("switching to CLOSED", SPOP_EV_RX_FRAME|SPOP_EV_RX_FHDR|SPOP_EV_SPOP_CONN_ERR, spop_conn->conn); |
2275 | 0 | } |
2276 | |
|
2277 | 0 | if (spop_conn->flags & SPOP_CF_ERROR) |
2278 | 0 | spop_conn_report_term_evt(spop_conn, ((eb_is_empty(&spop_conn->streams_by_id) && (spop_conn->state == SPOP_CS_RUNNING) && spop_conn->dsi == -1) |
2279 | 0 | ? muxc_tevt_type_rcv_err |
2280 | 0 | : muxc_tevt_type_truncated_rcv_err)); |
2281 | 0 | else if (spop_conn->flags & SPOP_CF_END_REACHED) |
2282 | 0 | spop_conn_report_term_evt(spop_conn, ((eb_is_empty(&spop_conn->streams_by_id) && (spop_conn->state == SPOP_CS_RUNNING) && spop_conn->dsi == -1) |
2283 | 0 | ? muxc_tevt_type_shutr |
2284 | 0 | : muxc_tevt_type_truncated_shutr)); |
2285 | |
|
2286 | 0 | if (spop_strm && spop_strm_sc(spop_strm) && |
2287 | 0 | (b_data(&spop_strm->rxbuf) || |
2288 | 0 | spop_conn_read0_pending(spop_conn) || |
2289 | 0 | spop_strm->state == SPOP_SS_CLOSED || |
2290 | 0 | (spop_strm->flags & SPOP_SF_ACK_RCVD) || |
2291 | 0 | se_fl_test(spop_strm->sd, SE_FL_ERROR | SE_FL_ERR_PENDING | SE_FL_EOS))) { |
2292 | | /* we may have to signal the upper layers */ |
2293 | 0 | TRACE_DEVEL("notifying stream before switching SID", SPOP_EV_RX_FRAME|SPOP_EV_STRM_WAKE, spop_conn->conn, spop_strm); |
2294 | 0 | se_fl_set(spop_strm->sd, SE_FL_RCV_MORE); |
2295 | 0 | spop_strm_notify_recv(spop_strm); |
2296 | 0 | } |
2297 | |
|
2298 | 0 | spop_conn_restart_reading(spop_conn, 0); |
2299 | 0 | out: |
2300 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_CONN_WAKE, spop_conn->conn); |
2301 | 0 | return; |
2302 | 0 | } |
2303 | | |
2304 | | |
2305 | | /* process Tx records from streams to be multiplexed. Returns > 0 if it reached |
2306 | | * the end. |
2307 | | */ |
2308 | | static int spop_process_mux(struct spop_conn *spop_conn) |
2309 | 0 | { |
2310 | 0 | TRACE_ENTER(SPOP_EV_SPOP_CONN_WAKE, spop_conn->conn); |
2311 | |
|
2312 | 0 | if (unlikely(spop_conn->state < SPOP_CS_RUNNING)) { |
2313 | 0 | if (unlikely(spop_conn->state == SPOP_CS_HA_HELLO)) { |
2314 | 0 | TRACE_PROTO("sending SPOP HAPROXY HELLO fraame", SPOP_EV_TX_FRAME, spop_conn->conn); |
2315 | 0 | if (unlikely(!spop_conn_send_hello(spop_conn))) |
2316 | 0 | goto fail; |
2317 | 0 | spop_conn->state = SPOP_CS_AGENT_HELLO; |
2318 | 0 | TRACE_STATE("waiting for SPOP AGENT HELLO reply", SPOP_EV_TX_FRAME|SPOP_EV_RX_FRAME, spop_conn->conn); |
2319 | 0 | } |
2320 | | /* need to wait for the other side */ |
2321 | 0 | if (spop_conn->state < SPOP_CS_RUNNING) |
2322 | 0 | goto done; |
2323 | 0 | } |
2324 | | |
2325 | 0 | spop_conn->flags &= ~SPOP_CF_WAIT_INLIST; |
2326 | 0 | spop_resume_each_sending_spop_strm(spop_conn, &spop_conn->send_list); |
2327 | |
|
2328 | 0 | fail: |
2329 | | /* Nothing to do */ |
2330 | |
|
2331 | 0 | done: |
2332 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_CONN_WAKE, spop_conn->conn); |
2333 | 0 | return 1; |
2334 | 0 | } |
2335 | | |
2336 | | /* Attempt to read data, and subscribe if none available. |
2337 | | * The function returns 1 if data has been received, otherwise zero. |
2338 | | */ |
2339 | | static int spop_recv(struct spop_conn *spop_conn) |
2340 | 0 | { |
2341 | 0 | struct connection *conn = spop_conn->conn; |
2342 | 0 | struct buffer *buf; |
2343 | 0 | int max; |
2344 | 0 | size_t ret; |
2345 | |
|
2346 | 0 | TRACE_ENTER(SPOP_EV_SPOP_CONN_RECV, conn); |
2347 | |
|
2348 | 0 | if (spop_conn->wait_event.events & SUB_RETRY_RECV) { |
2349 | 0 | TRACE_DEVEL("leaving on sub_recv", SPOP_EV_SPOP_CONN_RECV, conn); |
2350 | 0 | return (b_data(&spop_conn->dbuf)); |
2351 | 0 | } |
2352 | | |
2353 | 0 | if (!spop_recv_allowed(spop_conn)) { |
2354 | 0 | TRACE_DEVEL("leaving on !recv_allowed", SPOP_EV_SPOP_CONN_RECV, conn); |
2355 | 0 | return 1; |
2356 | 0 | } |
2357 | | |
2358 | 0 | buf = spop_get_buf(spop_conn, &spop_conn->dbuf); |
2359 | 0 | if (!buf) { |
2360 | 0 | TRACE_DEVEL("waiting for spop_conn dbuf allocation", SPOP_EV_SPOP_CONN_RECV|SPOP_EV_SPOP_CONN_BLK, conn); |
2361 | 0 | spop_conn->flags |= SPOP_CF_DEM_DALLOC; |
2362 | 0 | return 0; |
2363 | 0 | } |
2364 | | |
2365 | 0 | max = b_room(buf); |
2366 | 0 | ret = max ? conn->xprt->rcv_buf(conn, conn->xprt_ctx, buf, max, NULL, NULL, 0) : 0; |
2367 | |
|
2368 | 0 | if (max && !ret && spop_recv_allowed(spop_conn)) { |
2369 | 0 | TRACE_DATA("failed to receive data, subscribing", SPOP_EV_SPOP_CONN_RECV, conn); |
2370 | 0 | conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_RECV, &spop_conn->wait_event); |
2371 | 0 | } |
2372 | 0 | else |
2373 | 0 | TRACE_DATA("recv data", SPOP_EV_SPOP_CONN_RECV, conn, 0, 0, (size_t[]){ret}); |
2374 | |
|
2375 | 0 | if (conn_xprt_read0_pending(conn)) { |
2376 | 0 | TRACE_DATA("received read0", SPOP_EV_SPOP_CONN_RECV, conn); |
2377 | 0 | spop_conn->flags |= SPOP_CF_RCVD_SHUT; |
2378 | 0 | } |
2379 | 0 | if ((conn->flags & CO_FL_ERROR) && |
2380 | 0 | (!b_data(&spop_conn->dbuf) || (spop_conn->flags & SPOP_CF_DEM_SHORT_READ))) { |
2381 | 0 | TRACE_DATA("connection error", SPOP_EV_SPOP_CONN_RECV, conn); |
2382 | 0 | spop_conn->flags |= SPOP_CF_ERROR; |
2383 | 0 | } |
2384 | |
|
2385 | 0 | if (!b_data(buf)) { |
2386 | 0 | spop_release_buf(spop_conn, &spop_conn->dbuf); |
2387 | 0 | goto end; |
2388 | 0 | } |
2389 | | |
2390 | 0 | if (ret == max) { |
2391 | 0 | TRACE_DEVEL("spop_conn dbuf full", SPOP_EV_SPOP_CONN_RECV|SPOP_EV_SPOP_CONN_BLK, conn); |
2392 | 0 | spop_conn->flags |= SPOP_CF_DEM_DFULL; |
2393 | 0 | } |
2394 | |
|
2395 | 0 | end: |
2396 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_CONN_RECV, conn); |
2397 | 0 | return !!ret || (spop_conn->flags & (SPOP_CF_RCVD_SHUT|SPOP_CF_ERROR)); |
2398 | 0 | } |
2399 | | |
2400 | | /* Try to send data if possible. |
2401 | | * The function returns 1 if data have been sent, otherwise zero. |
2402 | | */ |
2403 | | static int spop_send(struct spop_conn *spop_conn) |
2404 | 0 | { |
2405 | 0 | struct connection *conn = spop_conn->conn; |
2406 | 0 | int done; |
2407 | 0 | int sent = 0; |
2408 | |
|
2409 | 0 | TRACE_ENTER(SPOP_EV_SPOP_CONN_SEND, conn); |
2410 | |
|
2411 | 0 | if (spop_conn->flags & (SPOP_CF_ERROR|SPOP_CF_ERR_PENDING)) { |
2412 | 0 | TRACE_DEVEL("leaving on connection error", SPOP_EV_SPOP_CONN_SEND, conn); |
2413 | 0 | if (spop_conn->flags & SPOP_CF_END_REACHED) |
2414 | 0 | spop_conn->flags |= SPOP_CF_ERROR; |
2415 | 0 | b_reset(br_tail(spop_conn->mbuf)); |
2416 | 0 | return 1; |
2417 | 0 | } |
2418 | | |
2419 | | |
2420 | 0 | if (conn->flags & CO_FL_WAIT_XPRT) { |
2421 | | /* a handshake was requested */ |
2422 | 0 | goto schedule; |
2423 | 0 | } |
2424 | | |
2425 | | /* This loop is quite simple : it tries to fill as much as it can from |
2426 | | * pending streams into the existing buffer until it's reportedly full |
2427 | | * or the end of send requests is reached. Then it tries to send this |
2428 | | * buffer's contents out, marks it not full if at least one byte could |
2429 | | * be sent, and tries again. |
2430 | | * |
2431 | | * The snd_buf() function normally takes a "flags" argument which may |
2432 | | * be made of a combination of CO_SFL_MSG_MORE to indicate that more |
2433 | | * data immediately comes and CO_SFL_STREAMER to indicate that the |
2434 | | * connection is streaming lots of data (used to increase TLS record |
2435 | | * size at the expense of latency). The former can be sent any time |
2436 | | * there's a buffer full flag, as it indicates at least one stream |
2437 | | * attempted to send and failed so there are pending data. An |
2438 | | * alternative would be to set it as long as there's an active stream |
2439 | | * but that would be problematic for ACKs until we have an absolute |
2440 | | * guarantee that all waiters have at least one byte to send. The |
2441 | | * latter should possibly not be set for now. |
2442 | | */ |
2443 | | |
2444 | 0 | done = 0; |
2445 | 0 | while (!done) { |
2446 | 0 | unsigned int flags = 0; |
2447 | 0 | unsigned int released = 0; |
2448 | 0 | struct buffer *buf; |
2449 | | |
2450 | | /* fill as much as we can into the current buffer */ |
2451 | 0 | while (((spop_conn->flags & (SPOP_CF_MUX_MFULL|SPOP_CF_MUX_MALLOC)) == 0) && !done) |
2452 | 0 | done = spop_process_mux(spop_conn); |
2453 | |
|
2454 | 0 | if (spop_conn->flags & SPOP_CF_MUX_MALLOC) |
2455 | 0 | done = 1; // we won't go further without extra buffers |
2456 | |
|
2457 | 0 | if (conn->flags & CO_FL_ERROR) |
2458 | 0 | break; |
2459 | | |
2460 | 0 | if (spop_conn->flags & (SPOP_CF_MUX_MFULL | SPOP_CF_DEM_MROOM)) |
2461 | 0 | flags |= CO_SFL_MSG_MORE; |
2462 | |
|
2463 | 0 | for (buf = br_head(spop_conn->mbuf); b_size(buf); buf = br_del_head(spop_conn->mbuf)) { |
2464 | 0 | if (b_data(buf)) { |
2465 | 0 | int ret; |
2466 | |
|
2467 | 0 | ret = conn->xprt->snd_buf(conn, conn->xprt_ctx, buf, b_data(buf), NULL, 0, flags); |
2468 | 0 | if (!ret) { |
2469 | 0 | done = 1; |
2470 | 0 | break; |
2471 | 0 | } |
2472 | 0 | sent = 1; |
2473 | 0 | TRACE_DATA("send data", SPOP_EV_SPOP_CONN_SEND, conn, 0, 0, (size_t[]){ret}); |
2474 | 0 | b_del(buf, ret); |
2475 | 0 | if (b_data(buf)) { |
2476 | 0 | done = 1; |
2477 | 0 | break; |
2478 | 0 | } |
2479 | 0 | } |
2480 | 0 | b_free(buf); |
2481 | 0 | released++; |
2482 | 0 | } |
2483 | |
|
2484 | 0 | if (released) |
2485 | 0 | offer_buffers(NULL, released); |
2486 | | |
2487 | | /* wrote at least one byte, the buffer is not full anymore */ |
2488 | 0 | if (spop_conn->flags & (SPOP_CF_MUX_MFULL | SPOP_CF_DEM_MROOM)) |
2489 | 0 | TRACE_STATE("spop_conn mbuf ring not fill anymore", SPOP_EV_SPOP_CONN_SEND|SPOP_EV_SPOP_CONN_BLK, conn); |
2490 | 0 | spop_conn->flags &= ~(SPOP_CF_MUX_MFULL | SPOP_CF_DEM_MROOM); |
2491 | 0 | } |
2492 | |
|
2493 | 0 | if (conn->flags & CO_FL_ERROR) { |
2494 | 0 | spop_conn->flags |= SPOP_CF_ERR_PENDING; |
2495 | 0 | spop_conn_report_term_evt(spop_conn, muxc_tevt_type_snd_err); |
2496 | 0 | if (spop_conn->flags & SPOP_CF_END_REACHED) |
2497 | 0 | spop_conn->flags |= SPOP_CF_ERROR; |
2498 | 0 | b_reset(br_tail(spop_conn->mbuf)); |
2499 | 0 | } |
2500 | | |
2501 | | /* We're not full anymore, so we can wake any task that are waiting |
2502 | | * for us. |
2503 | | */ |
2504 | 0 | if (!(spop_conn->flags & (SPOP_CF_MUX_MFULL | SPOP_CF_DEM_MROOM)) && spop_conn->state >= SPOP_CS_RUNNING) { |
2505 | 0 | spop_conn->flags &= ~SPOP_CF_WAIT_INLIST; |
2506 | 0 | spop_resume_each_sending_spop_strm(spop_conn, &spop_conn->send_list); |
2507 | 0 | } |
2508 | | |
2509 | | /* We're done, no more to send */ |
2510 | 0 | if (!br_data(spop_conn->mbuf)) { |
2511 | 0 | TRACE_DEVEL("leaving with everything sent", SPOP_EV_SPOP_CONN_SEND, conn); |
2512 | 0 | goto end; |
2513 | 0 | } |
2514 | 0 | schedule: |
2515 | 0 | if (!(conn->flags & CO_FL_ERROR) && !(spop_conn->wait_event.events & SUB_RETRY_SEND)) { |
2516 | 0 | TRACE_STATE("more data to send, subscribing", SPOP_EV_SPOP_CONN_SEND, conn); |
2517 | 0 | conn->xprt->subscribe(conn, conn->xprt_ctx, SUB_RETRY_SEND, &spop_conn->wait_event); |
2518 | 0 | } |
2519 | |
|
2520 | 0 | TRACE_DEVEL("leaving with some data left to send", SPOP_EV_SPOP_CONN_SEND, conn); |
2521 | 0 | end: |
2522 | 0 | return sent || (spop_conn->flags & (SPOP_CF_ERR_PENDING|SPOP_CF_ERROR)); |
2523 | 0 | } |
2524 | | |
2525 | | |
2526 | | /* this is the tasklet referenced in spop_conn->wait_event.tasklet */ |
2527 | | static struct task *spop_io_cb(struct task *t, void *ctx, unsigned int state) |
2528 | 0 | { |
2529 | 0 | struct connection *conn; |
2530 | 0 | struct spop_conn *spop_conn = ctx; |
2531 | 0 | struct tasklet *tl = (struct tasklet *)t; |
2532 | 0 | int conn_in_list; |
2533 | 0 | int ret = 0; |
2534 | |
|
2535 | 0 | if (state & TASK_F_USR1) { |
2536 | | /* the tasklet was idling on an idle connection, it might have |
2537 | | * been stolen, let's be careful! |
2538 | | */ |
2539 | 0 | HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); |
2540 | 0 | if (tl->context == NULL) { |
2541 | | /* The connection has been taken over by another thread, |
2542 | | * we're no longer responsible for it, so just free the |
2543 | | * tasklet, and do nothing. |
2544 | | */ |
2545 | 0 | HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); |
2546 | 0 | tasklet_free(tl); |
2547 | 0 | return NULL; |
2548 | 0 | } |
2549 | 0 | conn = spop_conn->conn; |
2550 | 0 | TRACE_POINT(SPOP_EV_SPOP_CONN_WAKE, conn); |
2551 | | |
2552 | | /* Remove the connection from the list, to be sure nobody attempts |
2553 | | * to use it while we handle the I/O events |
2554 | | */ |
2555 | 0 | if (LIST_INLIST(&conn->idle_list)) { |
2556 | 0 | conn_in_list = 1; |
2557 | 0 | BUG_ON(!(conn->flags & CO_FL_LIST_MASK)); |
2558 | 0 | conn_delete_from_tree(conn, tid); |
2559 | 0 | } |
2560 | 0 | else if (LIST_INLIST(&conn->sess_el)) { |
2561 | 0 | conn_in_list = 1; |
2562 | 0 | BUG_ON(!(conn->flags & CO_FL_SESS_IDLE)); |
2563 | | /* session_detach_idle_conn cannot fail thanks to LIST_INLIST() above test */ |
2564 | 0 | session_detach_idle_conn(conn->owner, conn); |
2565 | 0 | } |
2566 | 0 | else { |
2567 | 0 | conn_in_list = 0; |
2568 | 0 | } |
2569 | |
|
2570 | 0 | HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); |
2571 | 0 | } else { |
2572 | | /* we're certain the connection was not in an idle list */ |
2573 | 0 | conn = spop_conn->conn; |
2574 | 0 | TRACE_ENTER(SPOP_EV_SPOP_CONN_WAKE, conn); |
2575 | 0 | conn_in_list = 0; |
2576 | 0 | } |
2577 | | |
2578 | 0 | if (!(spop_conn->wait_event.events & SUB_RETRY_SEND)) |
2579 | 0 | ret = spop_send(spop_conn); |
2580 | 0 | if (!(spop_conn->wait_event.events & SUB_RETRY_RECV)) |
2581 | 0 | ret |= spop_recv(spop_conn); |
2582 | 0 | if (ret || b_data(&spop_conn->dbuf)) |
2583 | 0 | ret = spop_process(spop_conn); |
2584 | | |
2585 | | /* If we were in an idle list, we want to add it back into it, |
2586 | | * unless spop_process() returned -1, which mean it has destroyed |
2587 | | * the connection (testing !ret is enough, if spop_process() wasn't |
2588 | | * called then ret will be 0 anyway. |
2589 | | */ |
2590 | 0 | if (ret < 0) |
2591 | 0 | t = NULL; |
2592 | |
|
2593 | 0 | if (!ret && conn_in_list) { |
2594 | 0 | struct server *srv = objt_server(conn->target); |
2595 | |
|
2596 | 0 | if (!srv || !(srv->cur_admin & SRV_ADMF_MAINT)) { |
2597 | 0 | if (conn->flags & CO_FL_SESS_IDLE) { |
2598 | 0 | if (!session_reinsert_idle_conn(conn->owner, conn)) { |
2599 | | /* session add conn failure */ |
2600 | 0 | goto release; |
2601 | 0 | } |
2602 | 0 | } |
2603 | 0 | else { |
2604 | 0 | ASSUME_NONNULL(srv); /* srv is guaranteed to be non-null if conn is in idle list */ |
2605 | 0 | srv_add_idle(srv, conn, (conn->flags & CO_FL_LIST_MASK) == CO_FL_SAFE_LIST); |
2606 | 0 | } |
2607 | 0 | } |
2608 | 0 | else { |
2609 | | /* Do not store an idle conn if server in maintenance. */ |
2610 | 0 | goto release; |
2611 | 0 | } |
2612 | 0 | } |
2613 | 0 | return t; |
2614 | | |
2615 | 0 | release: |
2616 | 0 | spop_release(spop_conn); |
2617 | 0 | return NULL; |
2618 | 0 | } |
2619 | | |
2620 | | /* callback called on any event by the connection handler. |
2621 | | * It applies changes and returns zero, or < 0 if it wants immediate |
2622 | | * destruction of the connection (which normally doesn not happen in SPOP). |
2623 | | */ |
2624 | | static int spop_process(struct spop_conn *spop_conn) |
2625 | 0 | { |
2626 | 0 | struct connection *conn = spop_conn->conn; |
2627 | |
|
2628 | 0 | TRACE_POINT(SPOP_EV_SPOP_CONN_WAKE, conn); |
2629 | |
|
2630 | 0 | if (!(spop_conn->flags & SPOP_CF_DEM_BLOCK_ANY) && |
2631 | 0 | (b_data(&spop_conn->dbuf) || (spop_conn->flags & SPOP_CF_RCVD_SHUT))) { |
2632 | 0 | spop_process_demux(spop_conn); |
2633 | |
|
2634 | 0 | if (spop_conn->state >= SPOP_CS_ERROR || (spop_conn->flags & SPOP_CF_ERROR)) |
2635 | 0 | b_reset(&spop_conn->dbuf); |
2636 | |
|
2637 | 0 | if (b_room(&spop_conn->dbuf)) |
2638 | 0 | spop_conn->flags &= ~SPOP_CF_DEM_DFULL; |
2639 | 0 | } |
2640 | 0 | spop_send(spop_conn); |
2641 | | |
2642 | | /* |
2643 | | * If we received early data, and the handshake is done, wake |
2644 | | * any stream that was waiting for it. |
2645 | | */ |
2646 | 0 | if (!(spop_conn->flags & SPOP_CF_WAIT_FOR_HS) && |
2647 | 0 | (conn->flags & (CO_FL_EARLY_SSL_HS | CO_FL_WAIT_XPRT | CO_FL_EARLY_DATA)) == CO_FL_EARLY_DATA) { |
2648 | 0 | struct eb32_node *node; |
2649 | 0 | struct spop_strm *spop_strm; |
2650 | |
|
2651 | 0 | spop_conn->flags |= SPOP_CF_WAIT_FOR_HS; |
2652 | 0 | node = eb32_lookup_ge(&spop_conn->streams_by_id, 1); |
2653 | |
|
2654 | 0 | while (node) { |
2655 | 0 | spop_strm = container_of(node, struct spop_strm, by_id); |
2656 | 0 | if (spop_strm_sc(spop_strm) && se_fl_test(spop_strm->sd, SE_FL_WAIT_FOR_HS)) |
2657 | 0 | spop_strm_notify_recv(spop_strm); |
2658 | 0 | node = eb32_next(node); |
2659 | 0 | } |
2660 | 0 | } |
2661 | |
|
2662 | 0 | if ((spop_conn->flags & SPOP_CF_ERROR) || spop_conn_read0_pending(spop_conn) || |
2663 | 0 | spop_conn->state == SPOP_CS_CLOSED || (spop_conn->flags & SPOP_CF_DISCO_FAILED)) { |
2664 | 0 | spop_wake_some_streams(spop_conn, 0); |
2665 | |
|
2666 | 0 | if (eb_is_empty(&spop_conn->streams_by_id)) { |
2667 | | /* no more stream, kill the connection now */ |
2668 | 0 | spop_release(spop_conn); |
2669 | 0 | TRACE_DEVEL("leaving after releasing the connection", SPOP_EV_SPOP_CONN_WAKE); |
2670 | 0 | return -1; |
2671 | 0 | } |
2672 | | |
2673 | | /* connections in error must be removed from the idle lists */ |
2674 | 0 | if (conn->flags & CO_FL_LIST_MASK) { |
2675 | 0 | HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); |
2676 | 0 | conn_delete_from_tree(conn, tid); |
2677 | 0 | HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); |
2678 | 0 | } |
2679 | 0 | } |
2680 | | |
2681 | 0 | if (!b_data(&spop_conn->dbuf)) |
2682 | 0 | spop_release_buf(spop_conn, &spop_conn->dbuf); |
2683 | |
|
2684 | 0 | if (spop_conn->state == SPOP_CS_CLOSED || (spop_conn->flags & SPOP_CF_DISCO_FAILED) || |
2685 | 0 | (!br_data(spop_conn->mbuf) && ((spop_conn->flags & SPOP_CF_MUX_BLOCK_ANY) || LIST_ISEMPTY(&spop_conn->send_list)))) |
2686 | 0 | spop_release_mbuf(spop_conn); |
2687 | |
|
2688 | 0 | spop_conn_update_timeout(spop_conn); |
2689 | 0 | spop_send(spop_conn); |
2690 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_CONN_WAKE, conn); |
2691 | 0 | return 0; |
2692 | 0 | } |
2693 | | |
2694 | | /* wake-up function called by the connection layer (mux_ops.wake) */ |
2695 | | static int spop_wake(struct connection *conn) |
2696 | 0 | { |
2697 | 0 | struct spop_conn *spop_conn = conn->ctx; |
2698 | |
|
2699 | 0 | TRACE_POINT(SPOP_EV_SPOP_CONN_WAKE, conn); |
2700 | 0 | return (spop_process(spop_conn)); |
2701 | 0 | } |
2702 | | |
2703 | | static int spop_ctl(struct connection *conn, enum mux_ctl_type mux_ctl, void *output) |
2704 | 0 | { |
2705 | 0 | struct spop_conn *spop_conn = conn->ctx; |
2706 | 0 | int ret = 0; |
2707 | |
|
2708 | 0 | switch (mux_ctl) { |
2709 | 0 | case MUX_CTL_STATUS: |
2710 | 0 | if (spop_conn->state == SPOP_CS_RUNNING && |
2711 | 0 | !(spop_conn->flags & (SPOP_CF_ERROR|SPOP_CF_ERR_PENDING|SPOP_CF_END_REACHED|SPOP_CF_RCVD_SHUT|SPOP_CF_DISCO_SENT|SPOP_CF_DISCO_FAILED))) |
2712 | 0 | ret |= MUX_STATUS_READY; |
2713 | 0 | return ret; |
2714 | 0 | case MUX_CTL_EXIT_STATUS: |
2715 | 0 | return MUX_ES_UNKNOWN; |
2716 | 0 | case MUX_CTL_GET_NBSTRM: |
2717 | 0 | return spop_conn->nb_streams; |
2718 | 0 | case MUX_CTL_GET_MAXSTRM: |
2719 | 0 | return spop_conn->streams_limit; |
2720 | 0 | case MUX_CTL_TEVTS: |
2721 | 0 | return spop_conn->term_evts_log; |
2722 | 0 | default: |
2723 | 0 | return -1; |
2724 | 0 | } |
2725 | 0 | } |
2726 | | |
2727 | | static int spop_sctl(struct stconn *sc, enum mux_sctl_type mux_sctl, void *output) |
2728 | 0 | { |
2729 | 0 | int ret = 0; |
2730 | 0 | struct spop_strm *spop_strm = __sc_mux_strm(sc); |
2731 | 0 | union mux_sctl_dbg_str_ctx *dbg_ctx; |
2732 | 0 | struct buffer *buf; |
2733 | |
|
2734 | 0 | switch (mux_sctl) { |
2735 | 0 | case MUX_SCTL_SID: |
2736 | 0 | if (output) |
2737 | 0 | *((int64_t *)output) = spop_strm->id; |
2738 | 0 | return ret; |
2739 | 0 | case MUX_SCTL_DBG_STR: |
2740 | 0 | dbg_ctx = output; |
2741 | 0 | buf = get_trash_chunk(); |
2742 | |
|
2743 | 0 | if (dbg_ctx->arg.debug_flags & MUX_SCTL_DBG_STR_L_MUXS) |
2744 | 0 | spop_dump_spop_strm_info(buf, spop_strm, NULL); |
2745 | |
|
2746 | 0 | if (dbg_ctx->arg.debug_flags & MUX_SCTL_DBG_STR_L_MUXC) |
2747 | 0 | spop_dump_spop_conn_info(buf, spop_strm->spop_conn, NULL); |
2748 | |
|
2749 | 0 | if (dbg_ctx->arg.debug_flags & MUX_SCTL_DBG_STR_L_CONN) |
2750 | 0 | chunk_appendf(buf, " conn.flg=%#08x conn.err_code=%u conn.evts=%s", |
2751 | 0 | spop_strm->spop_conn->conn->flags, spop_strm->spop_conn->conn->err_code, |
2752 | 0 | tevt_evts2str(spop_strm->spop_conn->conn->term_evts_log)); |
2753 | | |
2754 | | /* other layers not implemented */ |
2755 | 0 | dbg_ctx->ret.buf = *buf; |
2756 | 0 | return ret; |
2757 | 0 | case MUX_SCTL_TEVTS: |
2758 | 0 | return spop_strm->sd->term_evts_log; |
2759 | 0 | default: |
2760 | 0 | return -1; |
2761 | 0 | } |
2762 | 0 | } |
2763 | | |
2764 | | /* Connection timeout management. The principle is that if there's no receipt |
2765 | | * nor sending for a certain amount of time, the connection is closed. If the |
2766 | | * MUX buffer still has lying data or is not allocatable, the connection is |
2767 | | * immediately killed. If it's allocatable and empty, we attempt to send a |
2768 | | * ABORT records. |
2769 | | */ |
2770 | | static struct task *spop_timeout_task(struct task *t, void *context, unsigned int state) |
2771 | 0 | { |
2772 | 0 | struct spop_conn *spop_conn = context; |
2773 | 0 | int expired = tick_is_expired(t->expire, now_ms); |
2774 | |
|
2775 | 0 | TRACE_ENTER(SPOP_EV_SPOP_CONN_WAKE, (spop_conn ? spop_conn->conn : NULL)); |
2776 | |
|
2777 | 0 | if (spop_conn) { |
2778 | 0 | HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); |
2779 | | |
2780 | | /* Somebody already stole the connection from us, so we should not |
2781 | | * free it, we just have to free the task. |
2782 | | */ |
2783 | 0 | if (!t->context) { |
2784 | 0 | HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); |
2785 | 0 | spop_conn = NULL; |
2786 | 0 | goto do_leave; |
2787 | 0 | } |
2788 | | |
2789 | 0 | if (!expired) { |
2790 | 0 | HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); |
2791 | 0 | TRACE_DEVEL("leaving (not expired)", SPOP_EV_SPOP_CONN_WAKE, spop_conn->conn); |
2792 | 0 | return t; |
2793 | 0 | } |
2794 | | |
2795 | | /* We're about to destroy the connection, so make sure nobody attempts |
2796 | | * to steal it from us. |
2797 | | */ |
2798 | 0 | if (spop_conn->conn->flags & CO_FL_LIST_MASK) |
2799 | 0 | conn_delete_from_tree(spop_conn->conn, tid); |
2800 | 0 | else if (spop_conn->conn->flags & CO_FL_SESS_IDLE) |
2801 | 0 | session_detach_idle_conn(spop_conn->conn->owner, spop_conn->conn); |
2802 | |
|
2803 | 0 | HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); |
2804 | |
|
2805 | 0 | spop_conn_report_term_evt(spop_conn, muxc_tevt_type_tout); |
2806 | 0 | } |
2807 | | |
2808 | 0 | do_leave: |
2809 | 0 | task_destroy(t); |
2810 | |
|
2811 | 0 | if (!spop_conn) { |
2812 | | /* resources were already deleted */ |
2813 | 0 | TRACE_DEVEL("leaving (not more spop_conn)", SPOP_EV_SPOP_CONN_WAKE); |
2814 | 0 | return NULL; |
2815 | 0 | } |
2816 | | |
2817 | 0 | spop_conn->task = NULL; |
2818 | 0 | spop_conn->state = SPOP_CS_CLOSED; |
2819 | 0 | spop_wake_some_streams(spop_conn, 0); |
2820 | |
|
2821 | 0 | if (br_data(spop_conn->mbuf)) { |
2822 | | /* don't even try to send aborts, the buffer is stuck */ |
2823 | 0 | spop_conn->flags |= SPOP_CF_DISCO_FAILED; |
2824 | 0 | goto end; |
2825 | 0 | } |
2826 | | |
2827 | | /* try to send but no need to insist */ |
2828 | 0 | if (!spop_conn_send_disconnect(spop_conn)) |
2829 | 0 | spop_conn->flags |= SPOP_CF_DISCO_FAILED; |
2830 | |
|
2831 | 0 | if (br_data(spop_conn->mbuf) && !(spop_conn->flags & SPOP_CF_DISCO_FAILED) && |
2832 | 0 | conn_xprt_ready(spop_conn->conn)) { |
2833 | 0 | unsigned int released = 0; |
2834 | 0 | struct buffer *buf; |
2835 | |
|
2836 | 0 | for (buf = br_head(spop_conn->mbuf); b_size(buf); buf = br_del_head(spop_conn->mbuf)) { |
2837 | 0 | if (b_data(buf)) { |
2838 | 0 | int ret = spop_conn->conn->xprt->snd_buf(spop_conn->conn, spop_conn->conn->xprt_ctx, |
2839 | 0 | buf, b_data(buf), NULL, 0, 0); |
2840 | 0 | if (!ret) |
2841 | 0 | break; |
2842 | 0 | b_del(buf, ret); |
2843 | 0 | if (b_data(buf)) |
2844 | 0 | break; |
2845 | 0 | b_free(buf); |
2846 | 0 | released++; |
2847 | 0 | } |
2848 | 0 | } |
2849 | |
|
2850 | 0 | if (released) |
2851 | 0 | offer_buffers(NULL, released); |
2852 | 0 | } |
2853 | |
|
2854 | 0 | end: |
2855 | | /* either we can release everything now or it will be done later once |
2856 | | * the last stream closes. |
2857 | | */ |
2858 | 0 | if (eb_is_empty(&spop_conn->streams_by_id)) |
2859 | 0 | spop_release(spop_conn); |
2860 | |
|
2861 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_CONN_WAKE); |
2862 | 0 | return NULL; |
2863 | 0 | } |
2864 | | |
2865 | | |
2866 | | /*******************************************/ |
2867 | | /* functions below are used by the streams */ |
2868 | | /*******************************************/ |
2869 | | |
2870 | | /* Append the description of what is present in error snapshot <es> into <out>. |
2871 | | * The description must be small enough to always fit in a buffer. The output |
2872 | | * buffer may be the trash so the trash must not be used inside this function. |
2873 | | */ |
2874 | | static __maybe_unused void spop_show_error_snapshot(struct buffer *out, const struct error_snapshot *es) |
2875 | 0 | { |
2876 | 0 |
|
2877 | 0 | // TODO !!! |
2878 | 0 | } |
2879 | | /* |
2880 | | * Capture a bad response and archive it in the proxy's structure. By default |
2881 | | * it tries to report the error position as h1m->err_pos. However if this one is |
2882 | | * not set, it will then report h1m->next, which is the last known parsing |
2883 | | * point. The function is able to deal with wrapping buffers. It always displays |
2884 | | * buffers as a contiguous area starting at buf->p. The direction is determined |
2885 | | * thanks to the h1m's flags. |
2886 | | */ |
2887 | | static __maybe_unused void spop_strm_capture_bad_message(struct spop_conn *spop_conn, struct spop_strm *spop_strm, |
2888 | | struct buffer *buf) |
2889 | 0 | { |
2890 | 0 | // TODO !!! |
2891 | 0 | } |
2892 | | |
2893 | | // PARSING FUNCTIONS |
2894 | | |
2895 | | |
2896 | | /* |
2897 | | * Attach a new stream to a connection |
2898 | | * (Used for outgoing connections) |
2899 | | */ |
2900 | | static int spop_attach(struct connection *conn, struct sedesc *sd, struct session *sess) |
2901 | 0 | { |
2902 | 0 | struct spop_strm *spop_strm; |
2903 | 0 | struct spop_conn *spop_conn = conn->ctx; |
2904 | |
|
2905 | 0 | TRACE_ENTER(SPOP_EV_SPOP_STRM_NEW, conn); |
2906 | 0 | spop_strm = spop_stconn_new(spop_conn, sd->sc, sess); |
2907 | 0 | if (!spop_strm) |
2908 | 0 | goto err; |
2909 | | |
2910 | | /* the connection is not idle anymore, let's mark this */ |
2911 | 0 | HA_ATOMIC_AND(&spop_conn->wait_event.tasklet->state, ~TASK_F_USR1); |
2912 | 0 | xprt_set_used(conn, conn->xprt, conn->xprt_ctx); |
2913 | |
|
2914 | 0 | TRACE_LEAVE(SPOP_EV_SPOP_STRM_NEW, conn, spop_strm); |
2915 | 0 | return 0; |
2916 | | |
2917 | 0 | err: |
2918 | 0 | TRACE_DEVEL("leaving on error", SPOP_EV_SPOP_STRM_NEW|SPOP_EV_SPOP_STRM_ERR, conn); |
2919 | 0 | return -1; |
2920 | 0 | } |
2921 | | |
2922 | | /* Retrieves the first valid stream connector from this connection, or returns NULL. |
2923 | | * We have to scan because we may have some orphan streams. It might be |
2924 | | * beneficial to scan backwards from the end to reduce the likeliness to find |
2925 | | * orphans. |
2926 | | */ |
2927 | | static struct stconn *spop_get_first_sc(const struct connection *conn) |
2928 | 0 | { |
2929 | 0 | struct spop_conn *spop_conn = conn->ctx; |
2930 | 0 | struct spop_strm *spop_strm; |
2931 | 0 | struct eb32_node *node; |
2932 | |
|
2933 | 0 | node = eb32_first(&spop_conn->streams_by_id); |
2934 | 0 | while (node) { |
2935 | 0 | spop_strm = container_of(node, struct spop_strm, by_id); |
2936 | 0 | if (spop_strm_sc(spop_strm)) |
2937 | 0 | return spop_strm_sc(spop_strm); |
2938 | 0 | node = eb32_next(node); |
2939 | 0 | } |
2940 | 0 | return NULL; |
2941 | 0 | } |
2942 | | |
2943 | | /* |
2944 | | * Destroy the mux and the associated connection, if it is no longer used |
2945 | | */ |
2946 | | static void spop_destroy(void *ctx) |
2947 | 0 | { |
2948 | 0 | struct spop_conn *spop_conn = ctx; |
2949 | |
|
2950 | 0 | TRACE_POINT(SPOP_EV_SPOP_CONN_END, spop_conn->conn); |
2951 | 0 | if (eb_is_empty(&spop_conn->streams_by_id)) { |
2952 | 0 | BUG_ON(spop_conn->conn->ctx != spop_conn); |
2953 | 0 | spop_release(spop_conn); |
2954 | 0 | } |
2955 | 0 | } |
2956 | | |
2957 | | /* |
2958 | | * Detach the stream from the connection and possibly release the connection. |
2959 | | */ |
2960 | | static void spop_detach(struct sedesc *sd) |
2961 | 0 | { |
2962 | 0 | struct spop_strm *spop_strm = sd->se; |
2963 | 0 | struct spop_conn *spop_conn; |
2964 | 0 | struct session *sess; |
2965 | |
|
2966 | 0 | TRACE_ENTER(SPOP_EV_STRM_END, (spop_strm ? spop_strm->spop_conn->conn : NULL), spop_strm); |
2967 | |
|
2968 | 0 | if (!spop_strm) { |
2969 | 0 | TRACE_LEAVE(SPOP_EV_STRM_END); |
2970 | 0 | return; |
2971 | 0 | } |
2972 | | |
2973 | | /* there's no txbuf so we're certain no to be able to send anything */ |
2974 | 0 | spop_strm->flags &= ~SPOP_SF_NOTIFIED; |
2975 | |
|
2976 | 0 | sess = spop_strm->sess; |
2977 | 0 | spop_conn = spop_strm->spop_conn; |
2978 | 0 | spop_conn->nb_sc--; |
2979 | | |
2980 | | /* this stream may be blocked waiting for some data to leave, so orphan |
2981 | | * it in this case. |
2982 | | */ |
2983 | 0 | if (!(spop_conn->flags & (SPOP_CF_ERR_PENDING|SPOP_CF_ERROR)) && |
2984 | 0 | (spop_conn->state != SPOP_CS_CLOSED) && |
2985 | 0 | (spop_strm->flags & (SPOP_SF_BLK_MBUSY|SPOP_SF_BLK_MROOM)) && |
2986 | 0 | spop_strm->subs) { |
2987 | 0 | TRACE_DEVEL("leaving on stream blocked", SPOP_EV_STRM_END|SPOP_EV_SPOP_STRM_BLK, spop_conn->conn, spop_strm); |
2988 | | /* refresh the timeout if none was active, so that the last |
2989 | | * leaving stream may arm it. |
2990 | | */ |
2991 | 0 | if (spop_conn->task && !tick_isset(spop_conn->task->expire)) |
2992 | 0 | spop_conn_update_timeout(spop_conn); |
2993 | 0 | return; |
2994 | 0 | } |
2995 | | |
2996 | 0 | if ((spop_conn->flags & SPOP_CF_DEM_BLOCK_ANY && spop_strm->id == spop_conn->dsi)) { |
2997 | | /* unblock the connection if it was blocked on this stream. */ |
2998 | 0 | spop_conn->flags &= ~SPOP_CF_DEM_BLOCK_ANY; |
2999 | 0 | spop_conn->flags &= ~SPOP_CF_MUX_BLOCK_ANY; |
3000 | 0 | spop_conn_restart_reading(spop_conn, 1); |
3001 | 0 | } |
3002 | |
|
3003 | 0 | spop_strm_destroy(spop_strm); |
3004 | |
|
3005 | 0 | if (!(spop_conn->flags & (SPOP_CF_RCVD_SHUT|SPOP_CF_ERR_PENDING|SPOP_CF_ERROR))) { |
3006 | 0 | if (spop_conn->conn->flags & CO_FL_PRIVATE) { |
3007 | | /* Add the connection in the session server list, if not already done */ |
3008 | 0 | session_add_conn(sess, spop_conn->conn); |
3009 | |
|
3010 | 0 | if (eb_is_empty(&spop_conn->streams_by_id)) { |
3011 | 0 | if (!LIST_INLIST(&spop_conn->conn->sess_el)) { |
3012 | | /* Session insertion above has failed and connection is idle, remove it. */ |
3013 | 0 | CALL_MUX_NO_RET(spop_conn->conn->mux, destroy(spop_conn)); |
3014 | 0 | TRACE_DEVEL("leaving on error after killing outgoing connection", SPOP_EV_STRM_END|SPOP_EV_SPOP_CONN_ERR); |
3015 | 0 | return; |
3016 | 0 | } |
3017 | | |
3018 | | /* mark that the tasklet may lose its context to another thread and |
3019 | | * that the handler needs to check it under the idle conns lock. |
3020 | | */ |
3021 | 0 | HA_ATOMIC_OR(&spop_conn->wait_event.tasklet->state, TASK_F_USR1); |
3022 | 0 | xprt_set_idle(spop_conn->conn, spop_conn->conn->xprt, spop_conn->conn->xprt_ctx); |
3023 | | |
3024 | | /* Ensure session can keep a new idle connection. */ |
3025 | 0 | if (session_check_idle_conn(sess, spop_conn->conn) != 0) { |
3026 | 0 | CALL_MUX_NO_RET(spop_conn->conn->mux, destroy(spop_conn)); |
3027 | 0 | TRACE_DEVEL("leaving without reusable idle connection", SPOP_EV_STRM_END); |
3028 | 0 | return; |
3029 | 0 | } |
3030 | | |
3031 | | /* At this point, the connection is inserted into |
3032 | | * session list and marked as idle, so it may already |
3033 | | * have been purged from another thread. |
3034 | | */ |
3035 | 0 | TRACE_DEVEL("private connection marked as idle", SPOP_EV_STRM_END); |
3036 | 0 | return; |
3037 | 0 | } |
3038 | 0 | } |
3039 | 0 | else { |
3040 | 0 | if (eb_is_empty(&spop_conn->streams_by_id)) { |
3041 | | /* If the connection is owned by the session, first remove it |
3042 | | * from its list |
3043 | | */ |
3044 | 0 | if (spop_conn->conn->owner) { |
3045 | 0 | session_unown_conn(spop_conn->conn->owner, spop_conn->conn); |
3046 | 0 | spop_conn->conn->owner = NULL; |
3047 | 0 | } |
3048 | | |
3049 | | /* mark that the tasklet may lose its context to another thread and |
3050 | | * that the handler needs to check it under the idle conns lock. |
3051 | | */ |
3052 | 0 | HA_ATOMIC_OR(&spop_conn->wait_event.tasklet->state, TASK_F_USR1); |
3053 | 0 | xprt_set_idle(spop_conn->conn, spop_conn->conn->xprt, spop_conn->conn->xprt_ctx); |
3054 | |
|
3055 | 0 | if (!srv_add_to_idle_list(objt_server(spop_conn->conn->target), spop_conn->conn, 1)) { |
3056 | | /* The server doesn't want it, let's kill the connection right away */ |
3057 | 0 | CALL_MUX_NO_RET(spop_conn->conn->mux, destroy(spop_conn)); |
3058 | 0 | TRACE_DEVEL("leaving on error after killing outgoing connection", SPOP_EV_STRM_END|SPOP_EV_SPOP_CONN_ERR); |
3059 | 0 | return; |
3060 | 0 | } |
3061 | | /* At this point, the connection has been added to the |
3062 | | * server idle list, so another thread may already have |
3063 | | * hijacked it, so we can't do anything with it. |
3064 | | */ |
3065 | 0 | TRACE_DEVEL("reusable idle connection", SPOP_EV_STRM_END); |
3066 | 0 | return; |
3067 | 0 | } |
3068 | 0 | else if (!ceb_intree(&spop_conn->conn->hash_node.node) && |
3069 | 0 | spop_avail_streams(spop_conn->conn) > 0 && objt_server(spop_conn->conn->target) && |
3070 | 0 | !LIST_INLIST(&spop_conn->conn->sess_el)) { |
3071 | 0 | srv_add_to_avail_list(__objt_server(spop_conn->conn->target), spop_conn->conn); |
3072 | 0 | } |
3073 | 0 | } |
3074 | 0 | } |
3075 | | |
3076 | | /* We don't want to close right now unless we're removing the last |
3077 | | * stream and the connection is in error. |
3078 | | */ |
3079 | 0 | if (spop_conn_is_dead(spop_conn)) { |
3080 | | /* no more stream will come, kill it now */ |
3081 | 0 | TRACE_DEVEL("leaving, killing dead connection", SPOP_EV_STRM_END, spop_conn->conn); |
3082 | 0 | spop_release(spop_conn); |
3083 | 0 | } |
3084 | 0 | else if (spop_conn->task) { |
3085 | 0 | spop_conn_update_timeout(spop_conn); |
3086 | 0 | TRACE_DEVEL("leaving, refreshing connection's timeout", SPOP_EV_STRM_END, spop_conn->conn); |
3087 | 0 | } |
3088 | 0 | else |
3089 | 0 | TRACE_DEVEL("leaving", SPOP_EV_STRM_END, spop_conn->conn); |
3090 | 0 | } |
3091 | | |
3092 | | /* Performs a synchronous or asynchronous shutr(). */ |
3093 | | static void spop_do_shutr(struct spop_strm *spop_strm) |
3094 | 0 | { |
3095 | 0 | struct spop_conn *spop_conn = spop_strm->spop_conn; |
3096 | |
|
3097 | 0 | TRACE_ENTER(SPOP_EV_STRM_SHUT, spop_conn->conn, spop_strm); |
3098 | |
|
3099 | 0 | if (spop_strm->state == SPOP_SS_CLOSED) |
3100 | 0 | goto done; |
3101 | | |
3102 | | /* a connstream may require us to immediately kill the whole connection |
3103 | | * for example because of a "tcp-request content reject" rule that is |
3104 | | * normally used to limit abuse. |
3105 | | */ |
3106 | 0 | if (se_fl_test(spop_strm->sd, SE_FL_KILL_CONN) && |
3107 | 0 | !(spop_conn->flags & (SPOP_CF_DISCO_SENT|SPOP_CF_DISCO_FAILED))) { |
3108 | 0 | TRACE_STATE("stream wants to kill the connection", SPOP_EV_STRM_SHUT, spop_conn->conn, spop_strm); |
3109 | 0 | spop_conn->state = SPOP_CS_CLOSED; |
3110 | 0 | } |
3111 | |
|
3112 | 0 | spop_strm_close(spop_strm); |
3113 | |
|
3114 | 0 | if (!(spop_conn->wait_event.events & SUB_RETRY_SEND)) |
3115 | 0 | tasklet_wakeup(spop_conn->wait_event.tasklet); |
3116 | 0 | done: |
3117 | 0 | TRACE_LEAVE(SPOP_EV_STRM_SHUT, spop_conn->conn, spop_strm); |
3118 | 0 | return; |
3119 | 0 | } |
3120 | | |
3121 | | /* Performs a synchronous or asynchronous shutw(). */ |
3122 | | static void spop_do_shutw(struct spop_strm *spop_strm) |
3123 | 0 | { |
3124 | 0 | struct spop_conn *spop_conn = spop_strm->spop_conn; |
3125 | |
|
3126 | 0 | TRACE_ENTER(SPOP_EV_STRM_SHUT, spop_conn->conn, spop_strm); |
3127 | |
|
3128 | 0 | if (spop_strm->state == SPOP_SS_HLOC || spop_strm->state == SPOP_SS_CLOSED) |
3129 | 0 | goto done; |
3130 | | |
3131 | 0 | if (spop_strm->state == SPOP_SS_OPEN) |
3132 | 0 | spop_strm->state = SPOP_SS_HLOC; |
3133 | 0 | else if (spop_strm->state == SPOP_SS_HREM) |
3134 | 0 | spop_strm_close(spop_strm); |
3135 | 0 | else { |
3136 | | /* a connstream may require us to immediately kill the whole connection |
3137 | | * for example because of a "tcp-request content reject" rule that is |
3138 | | * normally used to limit abuse. |
3139 | | */ |
3140 | 0 | if (se_fl_test(spop_strm->sd, SE_FL_KILL_CONN)) { |
3141 | 0 | TRACE_STATE("stream wants to kill the connection", SPOP_EV_STRM_SHUT, spop_conn->conn, spop_strm); |
3142 | 0 | spop_conn->state = SPOP_CS_CLOSED; |
3143 | 0 | } |
3144 | 0 | } |
3145 | |
|
3146 | 0 | if (!(spop_conn->wait_event.events & SUB_RETRY_SEND)) |
3147 | 0 | tasklet_wakeup(spop_conn->wait_event.tasklet); |
3148 | 0 | done: |
3149 | 0 | TRACE_LEAVE(SPOP_EV_STRM_SHUT, spop_conn->conn, spop_strm); |
3150 | 0 | return; |
3151 | 0 | } |
3152 | | |
3153 | | |
3154 | | static void spop_shut(struct stconn *sc, enum se_shut_mode mode, struct se_abort_info *reason) |
3155 | 0 | { |
3156 | 0 | struct spop_strm *spop_strm = __sc_mux_strm(sc); |
3157 | |
|
3158 | 0 | TRACE_ENTER(SPOP_EV_STRM_SHUT, spop_strm->spop_conn->conn, spop_strm); |
3159 | 0 | if (mode & (SE_SHW_SILENT|SE_SHW_NORMAL)) |
3160 | 0 | spop_do_shutw(spop_strm); |
3161 | 0 | if (mode & SE_SHR_RESET) |
3162 | 0 | spop_do_shutr(spop_strm); |
3163 | 0 | TRACE_LEAVE(SPOP_EV_STRM_SHUT, spop_strm->spop_conn->conn, spop_strm); |
3164 | 0 | } |
3165 | | |
3166 | | /* Called from the upper layer, to subscribe <es> to events <event_type>. The |
3167 | | * event subscriber <es> is not allowed to change from a previous call as long |
3168 | | * as at least one event is still subscribed. The <event_type> must only be a |
3169 | | * combination of SUB_RETRY_RECV and SUB_RETRY_SEND. It always returns 0. |
3170 | | */ |
3171 | | static int spop_subscribe(struct stconn *sc, int event_type, struct wait_event *es) |
3172 | 0 | { |
3173 | 0 | struct spop_strm *spop_strm = __sc_mux_strm(sc); |
3174 | 0 | struct spop_conn *spop_conn = spop_strm->spop_conn; |
3175 | |
|
3176 | 0 | BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV)); |
3177 | 0 | BUG_ON(spop_strm->subs && spop_strm->subs != es); |
3178 | |
|
3179 | 0 | es->events |= event_type; |
3180 | 0 | spop_strm->subs = es; |
3181 | |
|
3182 | 0 | if (event_type & SUB_RETRY_RECV) |
3183 | 0 | TRACE_DEVEL("unsubscribe(recv)", SPOP_EV_STRM_RECV, spop_conn->conn, spop_strm); |
3184 | |
|
3185 | 0 | if (event_type & SUB_RETRY_SEND) { |
3186 | 0 | TRACE_DEVEL("unsubscribe(send)", SPOP_EV_STRM_SEND, spop_conn->conn, spop_strm); |
3187 | 0 | if (!LIST_INLIST(&spop_strm->list)) |
3188 | 0 | LIST_APPEND(&spop_conn->send_list, &spop_strm->list); |
3189 | 0 | } |
3190 | 0 | return 0; |
3191 | 0 | } |
3192 | | |
3193 | | /* Called from the upper layer, to unsubscribe <es> from events <event_type> |
3194 | | * (undo spop_subscribe). The <es> pointer is not allowed to differ from the one |
3195 | | * passed to the subscribe() call. It always returns zero. |
3196 | | */ |
3197 | | static int spop_unsubscribe(struct stconn *sc, int event_type, struct wait_event *es) |
3198 | 0 | { |
3199 | 0 | struct spop_strm *spop_strm = __sc_mux_strm(sc); |
3200 | 0 | struct spop_conn __maybe_unused *spop_conn = spop_strm->spop_conn; |
3201 | |
|
3202 | 0 | BUG_ON(event_type & ~(SUB_RETRY_SEND|SUB_RETRY_RECV)); |
3203 | 0 | BUG_ON(spop_strm->subs && spop_strm->subs != es); |
3204 | |
|
3205 | 0 | es->events &= ~event_type; |
3206 | 0 | if (!es->events) |
3207 | 0 | spop_strm->subs = NULL; |
3208 | |
|
3209 | 0 | if (event_type & SUB_RETRY_RECV) |
3210 | 0 | TRACE_DEVEL("subscribe(recv)", SPOP_EV_STRM_RECV, spop_conn->conn, spop_strm); |
3211 | |
|
3212 | 0 | if (event_type & SUB_RETRY_SEND) { |
3213 | 0 | TRACE_DEVEL("subscribe(send)", SPOP_EV_STRM_SEND, spop_conn->conn, spop_strm); |
3214 | 0 | spop_strm->flags &= ~SPOP_SF_NOTIFIED; |
3215 | 0 | LIST_DEL_INIT(&spop_strm->list); |
3216 | 0 | } |
3217 | 0 | return 0; |
3218 | 0 | } |
3219 | | |
3220 | | /* Try to send a NOTIFY frame. Returns the number of bytes sent. The caller |
3221 | | * must check the stream's status to detect any error which might have happened |
3222 | | * subsequently to a successful send. Data are automatically removed from the |
3223 | | * buffer. Content of the frame is assumed to be valid since produced from |
3224 | | * the internal code. |
3225 | | */ |
3226 | | static size_t spop_strm_send_notify(struct spop_strm *spop_strm, struct buffer *buf, size_t count) |
3227 | 0 | { |
3228 | 0 | struct spop_conn *spop_conn = spop_strm->spop_conn; |
3229 | 0 | struct buffer outbuf; |
3230 | 0 | struct buffer *mbuf; |
3231 | 0 | char *p, *end; |
3232 | 0 | size_t sz; |
3233 | 0 | int ret = 0; |
3234 | |
|
3235 | 0 | TRACE_ENTER(SPOP_EV_TX_FRAME|SPOP_EV_TX_NOTIFY, spop_conn->conn, spop_strm); |
3236 | |
|
3237 | 0 | mbuf = br_tail(spop_conn->mbuf); |
3238 | 0 | retry: |
3239 | 0 | if (!spop_get_buf(spop_conn, mbuf)) { |
3240 | 0 | spop_conn->flags |= SPOP_CF_MUX_MALLOC; |
3241 | 0 | spop_strm->flags |= SPOP_SF_BLK_MROOM; |
3242 | 0 | TRACE_STATE("waiting for fconn mbuf ring allocation", SPOP_EV_TX_FRAME|SPOP_EV_TX_NOTIFY|SPOP_EV_SPOP_STRM_BLK, spop_conn->conn, spop_strm); |
3243 | 0 | ret = 0; |
3244 | 0 | goto end; |
3245 | 0 | } |
3246 | | |
3247 | 0 | while (1) { |
3248 | 0 | outbuf = b_make(b_tail(mbuf), b_contig_space(mbuf), 0, 0); |
3249 | 0 | if (outbuf.size >= 11 || !b_space_wraps(mbuf)) |
3250 | 0 | break; |
3251 | 0 | realign_again: |
3252 | 0 | b_slow_realign(mbuf, trash.area, b_data(mbuf)); |
3253 | 0 | } |
3254 | | |
3255 | 0 | if (outbuf.size < 11) |
3256 | 0 | goto full; |
3257 | | |
3258 | | /* len: 4-bytes (fill later) type: (2)HAPROXY-NOTIFY, flags: 4-bytes (FIN=1) */ |
3259 | 0 | memcpy(outbuf.area, "\x00\x00\x00\x00\x03\x00\x00\x00\x01", 9); |
3260 | 0 | outbuf.data = 9; |
3261 | |
|
3262 | 0 | p = b_tail(&outbuf); |
3263 | 0 | end = b_orig(&outbuf) + b_size(&outbuf); |
3264 | |
|
3265 | 0 | if (encode_varint(spop_strm->id, &p, end) == -1) |
3266 | 0 | goto full; |
3267 | 0 | if (encode_varint(spop_strm->fid+1, &p, end) == -1) |
3268 | 0 | goto full; |
3269 | | |
3270 | 0 | sz = count - 1; /* Skip the frame type */ |
3271 | 0 | if (p + sz > end) |
3272 | 0 | goto full; |
3273 | 0 | memcpy(p, b_peek(buf,1), sz); |
3274 | 0 | p += sz; |
3275 | |
|
3276 | 0 | outbuf.data += p - b_tail(&outbuf); |
3277 | |
|
3278 | 0 | if (outbuf.data - 4 > spop_conn->max_frame_size) |
3279 | 0 | goto too_big; |
3280 | | |
3281 | | /* update the frame's size now */ |
3282 | 0 | TRACE_PROTO("SPOP HAPROXY NOTIFY frame xferred", SPOP_EV_TX_FRAME|SPOP_EV_TX_NOTIFY, spop_conn->conn, spop_strm, 0, (size_t[]){outbuf.data}); |
3283 | 0 | spop_set_frame_size(outbuf.area, outbuf.data - 4); |
3284 | 0 | spop_strm->fid++; |
3285 | 0 | b_add(mbuf, outbuf.data); |
3286 | 0 | b_del(buf, count); |
3287 | 0 | ret = count; |
3288 | |
|
3289 | 0 | end: |
3290 | 0 | TRACE_LEAVE(SPOP_EV_TX_FRAME|SPOP_EV_TX_NOTIFY, spop_conn->conn, spop_strm); |
3291 | 0 | return ret; |
3292 | 0 | full: |
3293 | | /* Too large to be encoded. For NOTIFY frame, it is an error */ |
3294 | 0 | if (!b_data(mbuf)) |
3295 | 0 | goto too_big; |
3296 | | |
3297 | 0 | if ((mbuf = br_tail_add(spop_conn->mbuf)) != NULL) |
3298 | 0 | goto retry; |
3299 | 0 | spop_conn->flags |= SPOP_CF_MUX_MFULL; |
3300 | 0 | spop_strm->flags |= SPOP_SF_BLK_MROOM; |
3301 | 0 | TRACE_STATE("mbuf ring full", SPOP_EV_TX_FRAME|SPOP_EV_TX_NOTIFY|SPOP_EV_SPOP_STRM_BLK, spop_conn->conn, spop_strm); |
3302 | 0 | ret = 0; |
3303 | 0 | goto end; |
3304 | | |
3305 | 0 | too_big: |
3306 | 0 | TRACE_ERROR("SPOP HAPROXY NOTIFY frame too large", SPOP_EV_TX_FRAME|SPOP_EV_TX_NOTIFY|SPOP_EV_SPOP_STRM_ERR, spop_conn->conn, spop_strm); |
3307 | 0 | spop_strm_error(spop_strm, SPOP_ERR_TOO_BIG); |
3308 | |
|
3309 | 0 | fail: |
3310 | 0 | TRACE_DEVEL("leaving on error", SPOP_EV_TX_FRAME|SPOP_EV_TX_NOTIFY|SPOP_EV_SPOP_STRM_ERR, spop_conn->conn, spop_strm); |
3311 | 0 | return 0; |
3312 | 0 | } |
3313 | | |
3314 | | /* Called from the upper layer, to receive data |
3315 | | * |
3316 | | * The caller is responsible for defragmenting <buf> if necessary. But <flags> |
3317 | | * must be tested to know the calling context. If CO_RFL_BUF_FLUSH is set, it |
3318 | | * means the caller wants to flush input data (from the mux buffer and the |
3319 | | * channel buffer) to be able to use kernel splicing or any kind of mux-to-mux |
3320 | | * xfer. If CO_RFL_KEEP_RECV is set, the mux must always subscribe for read |
3321 | | * events before giving back. CO_RFL_BUF_WET is set if <buf> is congested with |
3322 | | * data scheduled for leaving soon. CO_RFL_BUF_NOT_STUCK is set to instruct the |
3323 | | * mux it may optimize the data copy to <buf> if necessary. Otherwise, it should |
3324 | | * copy as much data as possible. |
3325 | | */ |
3326 | | static size_t spop_rcv_buf(struct stconn *sc, struct buffer *buf, size_t count, int flags) |
3327 | 0 | { |
3328 | 0 | struct spop_strm *spop_strm = __sc_mux_strm(sc); |
3329 | 0 | struct spop_conn *spop_conn = spop_strm->spop_conn; |
3330 | 0 | size_t ret = 0; |
3331 | |
|
3332 | 0 | TRACE_ENTER(SPOP_EV_STRM_RECV, spop_conn->conn, spop_strm, 0, (size_t[]){count}); |
3333 | |
|
3334 | 0 | ret = b_xfer(buf, &spop_strm->rxbuf, MIN(b_data(&spop_strm->rxbuf), count)); |
3335 | 0 | if (b_data(&spop_strm->rxbuf)) |
3336 | 0 | se_fl_set(spop_strm->sd, SE_FL_RCV_MORE | SE_FL_WANT_ROOM); |
3337 | 0 | else { |
3338 | 0 | se_fl_clr(spop_strm->sd, SE_FL_RCV_MORE | SE_FL_WANT_ROOM); |
3339 | 0 | spop_strm_propagate_term_flags(spop_conn, spop_strm); |
3340 | 0 | if (b_size(&spop_strm->rxbuf)) { |
3341 | 0 | b_free(&spop_strm->rxbuf); |
3342 | 0 | offer_buffers(NULL, 1); |
3343 | 0 | } |
3344 | 0 | } |
3345 | |
|
3346 | 0 | if (ret && spop_conn->dsi == spop_strm->id) { |
3347 | | /* demux is blocking on this stream's buffer */ |
3348 | 0 | spop_conn->flags &= ~SPOP_CF_DEM_SFULL; |
3349 | 0 | spop_conn_restart_reading(spop_conn, 1); |
3350 | 0 | } |
3351 | |
|
3352 | 0 | TRACE_LEAVE(SPOP_EV_STRM_RECV, spop_conn->conn, spop_strm, 0, (size_t[]){ret}); |
3353 | 0 | return ret; |
3354 | 0 | } |
3355 | | |
3356 | | |
3357 | | /* Called from the upper layer, to send data from buffer <buf> for no more than |
3358 | | * <count> bytes. Returns the number of bytes effectively sent. Some status |
3359 | | * flags may be updated on the stream connector. |
3360 | | */ |
3361 | | static size_t spop_snd_buf(struct stconn *sc, struct buffer *buf, size_t count, int flags) |
3362 | 0 | { |
3363 | 0 | struct spop_strm *spop_strm = __sc_mux_strm(sc); |
3364 | 0 | struct spop_conn *spop_conn = spop_strm->spop_conn; |
3365 | 0 | size_t total = 0; |
3366 | 0 | size_t ret; |
3367 | |
|
3368 | 0 | TRACE_ENTER(SPOP_EV_STRM_SEND, spop_conn->conn, spop_strm, 0, (size_t[]){count}); |
3369 | | |
3370 | | /* If we were not just woken because we wanted to send but couldn't, |
3371 | | * and there's somebody else that is waiting to send, do nothing, |
3372 | | * we will subscribe later and be put at the end of the list |
3373 | | */ |
3374 | 0 | if (!(spop_strm->flags & SPOP_SF_NOTIFIED) && !LIST_ISEMPTY(&spop_conn->send_list)) { |
3375 | 0 | if (LIST_INLIST(&spop_strm->list)) |
3376 | 0 | TRACE_DEVEL("stream already waiting, leaving", SPOP_EV_STRM_SEND|SPOP_EV_SPOP_STRM_BLK, spop_conn->conn, spop_strm); |
3377 | 0 | else { |
3378 | 0 | TRACE_DEVEL("other streams already waiting, going to the queue and leaving", SPOP_EV_STRM_SEND|SPOP_EV_SPOP_STRM_BLK, |
3379 | 0 | spop_conn->conn, spop_strm); |
3380 | 0 | spop_conn->flags |= SPOP_CF_WAIT_INLIST; |
3381 | 0 | } |
3382 | 0 | return 0; |
3383 | 0 | } |
3384 | 0 | spop_strm->flags &= ~SPOP_SF_NOTIFIED; |
3385 | |
|
3386 | 0 | if (spop_conn->state < SPOP_CS_RUNNING) { |
3387 | 0 | TRACE_DEVEL("connection not ready, leaving", SPOP_EV_STRM_SEND|SPOP_EV_SPOP_STRM_BLK, spop_conn->conn, spop_strm); |
3388 | 0 | return 0; |
3389 | 0 | } |
3390 | | |
3391 | 0 | if (spop_conn->state >= SPOP_CS_ERROR) { |
3392 | 0 | se_fl_set(spop_strm->sd, SE_FL_ERROR); |
3393 | 0 | se_report_term_evt(spop_strm->sd, se_tevt_type_snd_err); |
3394 | 0 | TRACE_DEVEL("connection is in error, leaving in error", SPOP_EV_STRM_SEND|SPOP_EV_SPOP_STRM_ERR|SPOP_EV_STRM_ERR, |
3395 | 0 | spop_conn->conn, spop_strm); |
3396 | 0 | return 0; |
3397 | 0 | } |
3398 | | |
3399 | 0 | if (spop_strm->id == 0) { |
3400 | 0 | int32_t id = spop_conn_get_next_sid(spop_conn); |
3401 | |
|
3402 | 0 | if (id < 0) { |
3403 | 0 | se_fl_set(spop_strm->sd, SE_FL_ERROR); |
3404 | 0 | TRACE_DEVEL("couldn't get a stream ID, leaving in error", SPOP_EV_STRM_SEND|SPOP_EV_SPOP_STRM_ERR|SPOP_EV_STRM_ERR, |
3405 | 0 | spop_conn->conn, spop_strm); |
3406 | 0 | return 0; |
3407 | 0 | } |
3408 | | |
3409 | 0 | eb32_delete(&spop_strm->by_id); |
3410 | 0 | spop_strm->by_id.key = spop_strm->id = id; |
3411 | 0 | spop_strm->state = SPOP_SS_OPEN; |
3412 | 0 | spop_conn->max_id = id; |
3413 | 0 | spop_conn->nb_reserved--; |
3414 | 0 | eb32_insert(&spop_conn->streams_by_id, &spop_strm->by_id); |
3415 | 0 | } |
3416 | | |
3417 | 0 | while (spop_strm->state < SPOP_SS_HLOC && !(spop_strm->flags & SPOP_SF_BLK_ANY) && count) { |
3418 | 0 | enum spop_frame_type type = *b_peek(buf, 0); |
3419 | |
|
3420 | 0 | switch (type) { |
3421 | 0 | case SPOP_FRM_T_HAPROXY_NOTIFY: |
3422 | 0 | ret = spop_strm_send_notify(spop_strm, buf, count); |
3423 | 0 | if (ret > 0) { |
3424 | 0 | total += ret; |
3425 | 0 | count -= ret; |
3426 | 0 | } |
3427 | 0 | break; |
3428 | 0 | default: |
3429 | 0 | TRACE_DEVEL("Unsupported frame type", SPOP_EV_STRM_SEND|SPOP_EV_STRM_ERR|SPOP_EV_SPOP_STRM_ERR, |
3430 | 0 | spop_conn->conn, spop_strm, 0, (size_t[]){type}); |
3431 | 0 | spop_strm_error(spop_strm, SPOP_ERR_INVALID); |
3432 | 0 | break; |
3433 | 0 | } |
3434 | 0 | } |
3435 | | |
3436 | 0 | if (spop_strm->state >= SPOP_SS_HLOC) { |
3437 | | /* trim any possibly pending data after we close */ |
3438 | 0 | b_del(buf, count); |
3439 | 0 | total += count; |
3440 | 0 | count = 0; |
3441 | 0 | } |
3442 | | /* RST are sent similarly to frame acks */ |
3443 | 0 | if (spop_strm->state == SPOP_SS_ERROR) { |
3444 | 0 | TRACE_DEVEL("reporting error to the app-layer stream", SPOP_EV_STRM_SEND|SPOP_EV_STRM_ERR|SPOP_EV_SPOP_STRM_ERR, spop_conn->conn, spop_strm); |
3445 | 0 | se_fl_set_error(spop_strm->sd); |
3446 | 0 | spop_strm_close(spop_strm); |
3447 | 0 | } |
3448 | |
|
3449 | 0 | if (total > 0) { |
3450 | 0 | if (!(spop_conn->wait_event.events & SUB_RETRY_SEND)) { |
3451 | 0 | TRACE_DEVEL("data queued, waking up spop_conn sender", SPOP_EV_STRM_SEND|SPOP_EV_SPOP_CONN_SEND, spop_conn->conn, spop_strm); |
3452 | 0 | if (spop_send(spop_conn)) |
3453 | 0 | tasklet_wakeup(spop_conn->wait_event.tasklet); |
3454 | 0 | } |
3455 | |
|
3456 | 0 | } |
3457 | |
|
3458 | 0 | if (total > 0) { |
3459 | | /* Ok we managed to send something, leave the send_list if we were still there */ |
3460 | 0 | spop_remove_from_list(spop_strm); |
3461 | 0 | TRACE_DEVEL("Removed from spop_strm list", SPOP_EV_STRM_SEND|SPOP_EV_SPOP_CONN_SEND, spop_conn->conn, spop_strm); |
3462 | 0 | } |
3463 | |
|
3464 | 0 | TRACE_LEAVE(SPOP_EV_STRM_SEND, spop_conn->conn, spop_strm, 0, (size_t[]){total}); |
3465 | 0 | return total; |
3466 | 0 | } |
3467 | | |
3468 | | /* appends some info about stream <spop_strm> to buffer <msg>, or does nothing |
3469 | | * if <spop_strm> is NULL. Returns non-zero if the stream is considered |
3470 | | * suspicious. May emit multiple lines, each new one being prefixed with <pfx>, |
3471 | | * if <pfx> is not NULL, otherwise a single line is used. |
3472 | | */ |
3473 | | static int spop_dump_spop_strm_info(struct buffer *msg, const struct spop_strm *spop_strm, const char *pfx) |
3474 | 0 | { |
3475 | 0 | int ret = 0; |
3476 | |
|
3477 | 0 | if (!spop_strm) |
3478 | 0 | return ret; |
3479 | | |
3480 | 0 | chunk_appendf(msg, " .id=%d .flg=0x%04x .rxbuf=%u@%p+%u/%u .sc=%p", |
3481 | 0 | spop_strm->id, spop_strm->flags, |
3482 | 0 | (unsigned int)b_data(&spop_strm->rxbuf), b_orig(&spop_strm->rxbuf), |
3483 | 0 | (unsigned int)b_head_ofs(&spop_strm->rxbuf), (unsigned int)b_size(&spop_strm->rxbuf), |
3484 | 0 | spop_strm_sc(spop_strm)); |
3485 | |
|
3486 | 0 | if (pfx && spop_strm->subs) |
3487 | 0 | chunk_appendf(msg, "\n%s", pfx); |
3488 | |
|
3489 | 0 | chunk_appendf(msg, " .sd.flg=0x%08x .sd.evts=%s", se_fl_get(spop_strm->sd), tevt_evts2str(spop_strm->sd->term_evts_log)); |
3490 | 0 | if (!se_fl_test(spop_strm->sd, SE_FL_ORPHAN)) |
3491 | 0 | chunk_appendf(msg, " .sc.flg=0x%08x .sc.app=%p .sc.evts=%s", |
3492 | 0 | spop_strm_sc(spop_strm)->flags, spop_strm_sc(spop_strm)->app, tevt_evts2str(spop_strm_sc(spop_strm)->term_evts_log)); |
3493 | |
|
3494 | 0 | if (pfx && spop_strm->subs) |
3495 | 0 | chunk_appendf(msg, "\n%s", pfx); |
3496 | |
|
3497 | 0 | chunk_appendf(msg, " .subs=%p", spop_strm->subs); |
3498 | 0 | if (spop_strm->subs) { |
3499 | 0 | chunk_appendf(msg, "(ev=%d tl=%p", spop_strm->subs->events, spop_strm->subs->tasklet); |
3500 | 0 | chunk_appendf(msg, " tl.calls=%d tl.ctx=%p tl.fct=", |
3501 | 0 | spop_strm->subs->tasklet->calls, |
3502 | 0 | spop_strm->subs->tasklet->context); |
3503 | 0 | if (spop_strm->subs->tasklet->calls >= 1000000) |
3504 | 0 | ret = 1; |
3505 | 0 | resolve_sym_name(msg, NULL, spop_strm->subs->tasklet->process); |
3506 | 0 | chunk_appendf(msg, ")"); |
3507 | 0 | } |
3508 | 0 | return ret; |
3509 | 0 | } |
3510 | | |
3511 | | /* appends some info about connection <spop_conn> to buffer <msg>, or does |
3512 | | * nothing if <spop_conn> is NULL. Returns non-zero if the connection is |
3513 | | * considered suspicious. May emit multiple lines, each new one being prefixed |
3514 | | * with <pfx>, if <pfx> is not NULL, otherwise a single line is used. |
3515 | | */ |
3516 | | static int spop_dump_spop_conn_info(struct buffer *msg, struct spop_conn *spop_conn, const char *pfx) |
3517 | 0 | { |
3518 | 0 | const struct buffer *hmbuf, *tmbuf; |
3519 | 0 | struct spop_strm *spop_strm = NULL; |
3520 | 0 | struct eb32_node *node; |
3521 | 0 | int send_cnt = 0; |
3522 | 0 | int tree_cnt = 0; |
3523 | 0 | int orph_cnt = 0; |
3524 | 0 | int ret = 0; |
3525 | |
|
3526 | 0 | if (!spop_conn) |
3527 | 0 | return ret; |
3528 | | |
3529 | 0 | list_for_each_entry(spop_strm, &spop_conn->send_list, list) |
3530 | 0 | send_cnt++; |
3531 | |
|
3532 | 0 | spop_strm = NULL; |
3533 | 0 | node = eb32_first(&spop_conn->streams_by_id); |
3534 | 0 | while (node) { |
3535 | 0 | spop_strm = container_of(node, struct spop_strm, by_id); |
3536 | 0 | tree_cnt++; |
3537 | 0 | if (!spop_strm_sc(spop_strm)) |
3538 | 0 | orph_cnt++; |
3539 | 0 | node = eb32_next(node); |
3540 | 0 | } |
3541 | |
|
3542 | 0 | hmbuf = br_head(spop_conn->mbuf); |
3543 | 0 | tmbuf = br_tail(spop_conn->mbuf); |
3544 | 0 | chunk_appendf(msg, " spop_conn.st0=%d .maxid=%d .flg=0x%04x .nbst=%u .nbcs=%u .evts=%s", |
3545 | 0 | spop_conn->state, spop_conn->max_id, spop_conn->flags, |
3546 | 0 | spop_conn->nb_streams, spop_conn->nb_sc, |
3547 | 0 | tevt_evts2str(spop_conn->term_evts_log)); |
3548 | |
|
3549 | 0 | if (pfx) |
3550 | 0 | chunk_appendf(msg, "\n%s", pfx); |
3551 | |
|
3552 | 0 | chunk_appendf(msg, ".send_cnt=%d .tree_cnt=%d .orph_cnt=%d .sub=%d .dsi=%d .dbuf=%u@%p+%u/%u", |
3553 | 0 | send_cnt, tree_cnt, orph_cnt, spop_conn->wait_event.events, spop_conn->dsi, |
3554 | 0 | (unsigned int)b_data(&spop_conn->dbuf), b_orig(&spop_conn->dbuf), |
3555 | 0 | (unsigned int)b_head_ofs(&spop_conn->dbuf), (unsigned int)b_size(&spop_conn->dbuf)); |
3556 | | |
3557 | |
|
3558 | 0 | if (pfx) |
3559 | 0 | chunk_appendf(msg, "\n%s", pfx); |
3560 | |
|
3561 | 0 | chunk_appendf(msg, ".mbuf=[%u..%u|%u],h=[%u@%p+%u/%u],t=[%u@%p+%u/%u]", |
3562 | 0 | br_head_idx(spop_conn->mbuf), br_tail_idx(spop_conn->mbuf), br_size(spop_conn->mbuf), |
3563 | 0 | (unsigned int)b_data(hmbuf), b_orig(hmbuf), |
3564 | 0 | (unsigned int)b_head_ofs(hmbuf), (unsigned int)b_size(hmbuf), |
3565 | 0 | (unsigned int)b_data(tmbuf), b_orig(tmbuf), |
3566 | 0 | (unsigned int)b_head_ofs(tmbuf), (unsigned int)b_size(tmbuf)); |
3567 | |
|
3568 | 0 | chunk_appendf(msg, " .task=%p", spop_conn->task); |
3569 | 0 | if (spop_conn->task) { |
3570 | 0 | chunk_appendf(msg, " .exp=%s", |
3571 | 0 | spop_conn->task->expire ? tick_is_expired(spop_conn->task->expire, now_ms) ? "<PAST>" : |
3572 | 0 | human_time(TICKS_TO_MS(spop_conn->task->expire - now_ms), TICKS_TO_MS(1000)) : "<NEVER>"); |
3573 | 0 | } |
3574 | |
|
3575 | 0 | return ret; |
3576 | 0 | } |
3577 | | |
3578 | | /* for debugging with CLI's "show fd" command */ |
3579 | | static int spop_show_fd(struct buffer *msg, struct connection *conn) |
3580 | 0 | { |
3581 | 0 | struct spop_conn *spop_conn = conn->ctx; |
3582 | 0 | struct spop_strm *spop_strm = NULL; |
3583 | 0 | struct eb32_node *node; |
3584 | 0 | int ret = 0; |
3585 | |
|
3586 | 0 | if (!spop_conn) |
3587 | 0 | return ret; |
3588 | | |
3589 | 0 | ret |= spop_dump_spop_conn_info(msg, spop_conn, NULL); |
3590 | |
|
3591 | 0 | node = eb32_first(&spop_conn->streams_by_id); |
3592 | 0 | if (node) { |
3593 | 0 | spop_strm = container_of(node, struct spop_strm, by_id); |
3594 | 0 | chunk_appendf(msg, " last_spop_strm=%p", spop_strm); |
3595 | 0 | ret |= spop_dump_spop_strm_info(msg, spop_strm, NULL); |
3596 | 0 | } |
3597 | |
|
3598 | 0 | return ret; |
3599 | 0 | } |
3600 | | |
3601 | | /* for debugging with CLI's "show sess" command. May emit multiple lines, each |
3602 | | * new one being prefixed with <pfx>, if <pfx> is not NULL, otherwise a single |
3603 | | * line is used. Each field starts with a space so it's safe to print it after |
3604 | | * existing fields. |
3605 | | */ |
3606 | | static int spop_show_sd(struct buffer *msg, struct sedesc *sd, const char *pfx) |
3607 | 0 | { |
3608 | 0 | struct spop_strm *spop_strm = sd->se; |
3609 | 0 | int ret = 0; |
3610 | |
|
3611 | 0 | if (!spop_strm) |
3612 | 0 | return ret; |
3613 | | |
3614 | 0 | chunk_appendf(msg, " spop_strm=%p", spop_strm); |
3615 | 0 | ret |= spop_dump_spop_strm_info(msg, spop_strm, pfx); |
3616 | 0 | if (pfx) |
3617 | 0 | chunk_appendf(msg, "\n%s", pfx); |
3618 | 0 | chunk_appendf(msg, " spop_conn=%p", spop_strm->spop_conn); |
3619 | 0 | ret |= spop_dump_spop_conn_info(msg, spop_strm->spop_conn, pfx); |
3620 | 0 | return ret; |
3621 | 0 | } |
3622 | | |
3623 | | /* Migrate the the connection to the current thread. |
3624 | | * Return 0 if successful, non-zero otherwise. |
3625 | | * Expected to be called with the old thread lock held. |
3626 | | */ |
3627 | | static int spop_takeover(struct connection *conn, int orig_tid, int release) |
3628 | 0 | { |
3629 | 0 | struct spop_conn *spop_conn = conn->ctx; |
3630 | 0 | struct task *task; |
3631 | 0 | struct task *new_task = NULL; |
3632 | 0 | struct tasklet *new_tasklet = NULL; |
3633 | | |
3634 | | /* If the connection is attached to a buffer_wait (extremely rare), |
3635 | | * it will be woken up at any instant by its own thread and we can't |
3636 | | * undo it anyway, so let's give up on this one. It's not interesting |
3637 | | * at least for reuse anyway since it's not usable right now. |
3638 | | * |
3639 | | * TODO if takeover is used to free conn (release == 1), buf_wait may |
3640 | | * prevent this which is not desirable. |
3641 | | */ |
3642 | 0 | if (LIST_INLIST(&spop_conn->buf_wait.list)) |
3643 | 0 | goto fail; |
3644 | | |
3645 | | /* Pre-allocate tasks so that we don't have to roll back after the xprt |
3646 | | * has been migrated. |
3647 | | */ |
3648 | 0 | if (!release) { |
3649 | |
|
3650 | 0 | new_task = task_new_here(); |
3651 | 0 | new_tasklet = tasklet_new(); |
3652 | 0 | if (!new_task || !new_tasklet) |
3653 | 0 | goto fail; |
3654 | 0 | } |
3655 | | |
3656 | 0 | if (fd_takeover(conn->handle.fd, conn) != 0) |
3657 | 0 | goto fail; |
3658 | | |
3659 | 0 | if (conn->xprt->takeover && conn->xprt->takeover(conn, conn->xprt_ctx, orig_tid, release) != 0) { |
3660 | | /* We failed to takeover the xprt, even if the connection may |
3661 | | * still be valid, flag it as error'd, as we have already |
3662 | | * taken over the fd, and wake the tasklet, so that it will |
3663 | | * destroy it. |
3664 | | */ |
3665 | 0 | conn->flags |= CO_FL_ERROR; |
3666 | 0 | tasklet_wakeup_on(spop_conn->wait_event.tasklet, orig_tid); |
3667 | 0 | goto fail; |
3668 | 0 | } |
3669 | | |
3670 | 0 | if (spop_conn->wait_event.events) |
3671 | 0 | spop_conn->conn->xprt->unsubscribe(spop_conn->conn, spop_conn->conn->xprt_ctx, |
3672 | 0 | spop_conn->wait_event.events, &spop_conn->wait_event); |
3673 | |
|
3674 | 0 | task = spop_conn->task; |
3675 | 0 | if (task) { |
3676 | | /* only assign a task if there was already one, otherwise |
3677 | | * the preallocated new task will be released. |
3678 | | */ |
3679 | 0 | task->context = NULL; |
3680 | 0 | spop_conn->task = NULL; |
3681 | 0 | __ha_barrier_store(); |
3682 | 0 | task_kill(task); |
3683 | |
|
3684 | 0 | spop_conn->task = new_task; |
3685 | 0 | new_task = NULL; |
3686 | 0 | if (!release) { |
3687 | 0 | spop_conn->task->process = spop_timeout_task; |
3688 | 0 | spop_conn->task->context = spop_conn; |
3689 | 0 | } |
3690 | 0 | } |
3691 | | |
3692 | | /* To let the tasklet know it should free itself, and do nothing else, |
3693 | | * set its context to NULL; |
3694 | | */ |
3695 | 0 | spop_conn->wait_event.tasklet->context = NULL; |
3696 | 0 | tasklet_wakeup_on(spop_conn->wait_event.tasklet, orig_tid); |
3697 | |
|
3698 | 0 | spop_conn->wait_event.tasklet = new_tasklet; |
3699 | 0 | if (!release) { |
3700 | 0 | spop_conn->wait_event.tasklet->process = spop_io_cb; |
3701 | 0 | spop_conn->wait_event.tasklet->context = spop_conn; |
3702 | 0 | spop_conn->conn->xprt->subscribe(spop_conn->conn, spop_conn->conn->xprt_ctx, |
3703 | 0 | SUB_RETRY_RECV, &spop_conn->wait_event); |
3704 | 0 | } |
3705 | |
|
3706 | 0 | if (new_task) |
3707 | 0 | __task_free(new_task); |
3708 | 0 | return 0; |
3709 | 0 | fail: |
3710 | 0 | if (new_task) |
3711 | 0 | __task_free(new_task); |
3712 | 0 | tasklet_free(new_tasklet); |
3713 | 0 | return -1; |
3714 | 0 | } |
3715 | | |
3716 | | static const struct mux_ops mux_spop_ops = { |
3717 | | .init = spop_init, |
3718 | | .wake = spop_wake, |
3719 | | .attach = spop_attach, |
3720 | | .get_first_sc = spop_get_first_sc, |
3721 | | .detach = spop_detach, |
3722 | | .destroy = spop_destroy, |
3723 | | .avail_streams = spop_avail_streams, |
3724 | | .used_streams = spop_used_streams, |
3725 | | .rcv_buf = spop_rcv_buf, |
3726 | | .snd_buf = spop_snd_buf, |
3727 | | .subscribe = spop_subscribe, |
3728 | | .unsubscribe = spop_unsubscribe, |
3729 | | .shut = spop_shut, |
3730 | | .ctl = spop_ctl, |
3731 | | .sctl = spop_sctl, |
3732 | | .show_fd = spop_show_fd, |
3733 | | .show_sd = spop_show_sd, |
3734 | | .takeover = spop_takeover, |
3735 | | .flags = MX_FL_HOL_RISK|MX_FL_NO_UPG, |
3736 | | .name = "SPOP", |
3737 | | }; |
3738 | | |
3739 | | static struct mux_proto_list mux_proto_spop = |
3740 | | { .mux_proto = IST("spop"), .mode = PROTO_MODE_SPOP, .side = PROTO_SIDE_BE, .mux = &mux_spop_ops }; |
3741 | | |
3742 | | static struct mux_proto_list mux_proto_default_spop = |
3743 | | { .mux_proto = IST(""), .mode = PROTO_MODE_SPOP, .side = PROTO_SIDE_BE, .mux = &mux_spop_ops }; |
3744 | | |
3745 | | INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_spop); |
3746 | | INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_default_spop); |
3747 | | |
3748 | | /* |
3749 | | * Local variables: |
3750 | | * c-indent-level: 8 |
3751 | | * c-basic-offset: 8 |
3752 | | * End: |
3753 | | */ |