Coverage Report

Created: 2026-10-13 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */