From: Eric Salama Date: Fri, 10 Nov 2017 10:02:23 +0000 (+0100) Subject: CONTRIB: spoa_example: allow to compile outside HAProxy. X-Git-Tag: v1.8.0~73 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=8a9c6c2154166579b93b7eabb4e0591ef37ca883;p=thirdparty%2Fhaproxy.git CONTRIB: spoa_example: allow to compile outside HAProxy. Don't include haproxy's includes anymore and use a local copy instead. --- diff --git a/contrib/spoa_example/Makefile b/contrib/spoa_example/Makefile index 3105ef0f9d..d04a01e11a 100644 --- a/contrib/spoa_example/Makefile +++ b/contrib/spoa_example/Makefile @@ -6,8 +6,8 @@ CC = gcc LD = $(CC) CFLAGS = -g -O2 -Wall -Werror -pthread -LDFLAGS = -lpthread -levent -levent_pthreads -INCS += -I../../include -I../../ebtree +LDFLAGS = -lpthread -levent -levent_pthreads +INCS += -I../../ebtree -I./include LIBS = OBJS = spoa.o diff --git a/contrib/spoa_example/include/mini-clist.h b/contrib/spoa_example/include/mini-clist.h new file mode 100644 index 0000000000..56fb28de5a --- /dev/null +++ b/contrib/spoa_example/include/mini-clist.h @@ -0,0 +1,122 @@ +#ifndef _COMMON_MINI_CLIST_H +#define _COMMON_MINI_CLIST_H + +/* these are circular or bidirectionnal lists only. Each list pointer points to + * another list pointer in a structure, and not the structure itself. The + * pointer to the next element MUST be the first one so that the list is easily + * cast as a single linked list or pointer. + */ +struct list { + struct list *n; /* next */ + struct list *p; /* prev */ +}; + +/* a back-ref is a pointer to a target list entry. It is used to detect when an + * element being deleted is currently being tracked by another user. The best + * example is a user dumping the session table. The table does not fit in the + * output buffer so we have to set a mark on a session and go on later. But if + * that marked session gets deleted, we don't want the user's pointer to go in + * the wild. So we can simply link this user's request to the list of this + * session's users, and put a pointer to the list element in ref, that will be + * used as the mark for next iteration. + */ +struct bref { + struct list users; + struct list *ref; /* pointer to the target's list entry */ +}; + +/* a word list is a generic list with a pointer to a string in each element. */ +struct wordlist { + struct list list; + char *s; +}; + +/* this is the same as above with an additional pointer to a condition. */ +struct cond_wordlist { + struct list list; + void *cond; + char *s; +}; + +/* First undefine some macros which happen to also be defined on OpenBSD, + * in sys/queue.h, used by sys/event.h + */ +#undef LIST_HEAD +#undef LIST_INIT +#undef LIST_NEXT + +/* ILH = Initialized List Head : used to prevent gcc from moving an empty + * list to BSS. Some older version tend to trim all the array and cause + * corruption. + */ +#define ILH { .n = (struct list *)1, .p = (struct list *)2 } + +#define LIST_HEAD(a) ((void *)(&(a))) + +#define LIST_INIT(l) ((l)->n = (l)->p = (l)) + +#define LIST_HEAD_INIT(l) { &l, &l } + +/* adds an element at the beginning of a list ; returns the element */ +#define LIST_ADD(lh, el) ({ (el)->n = (lh)->n; (el)->n->p = (lh)->n = (el); (el)->p = (lh); (el); }) + +/* adds an element at the end of a list ; returns the element */ +#define LIST_ADDQ(lh, el) ({ (el)->p = (lh)->p; (el)->p->n = (lh)->p = (el); (el)->n = (lh); (el); }) + +/* removes an element from a list and returns it */ +#define LIST_DEL(el) ({ typeof(el) __ret = (el); (el)->n->p = (el)->p; (el)->p->n = (el)->n; (__ret); }) + +/* returns a pointer of type to a structure containing a list head called + * at address . Note that can be the result of a function or macro + * since it's used only once. + * Example: LIST_ELEM(cur_node->args.next, struct node *, args) + */ +#define LIST_ELEM(lh, pt, el) ((pt)(((void *)(lh)) - ((void *)&((pt)NULL)->el))) + +/* checks if the list head is empty or not */ +#define LIST_ISEMPTY(lh) ((lh)->n == (lh)) + +/* returns a pointer of type to a structure following the element + * which contains list head , which is known as element in + * struct pt. + * Example: LIST_NEXT(args, struct node *, list) + */ +#define LIST_NEXT(lh, pt, el) (LIST_ELEM((lh)->n, pt, el)) + + +/* returns a pointer of type to a structure preceeding the element + * which contains list head , which is known as element in + * struct pt. + */ +#undef LIST_PREV +#define LIST_PREV(lh, pt, el) (LIST_ELEM((lh)->p, pt, el)) + +/* + * Simpler FOREACH_ITEM macro inspired from Linux sources. + * Iterates through a list of items of type "typeof(*item)" which are + * linked via a "struct list" member named . A pointer to the head of + * the list is passed in . No temporary variable is needed. Note + * that must not be modified during the loop. + * Example: list_for_each_entry(cur_acl, known_acl, list) { ... }; + */ +#define list_for_each_entry(item, list_head, member) \ + for (item = LIST_ELEM((list_head)->n, typeof(item), member); \ + &item->member != (list_head); \ + item = LIST_ELEM(item->member.n, typeof(item), member)) + +/* + * Simpler FOREACH_ITEM_SAFE macro inspired from Linux sources. + * Iterates through a list of items of type "typeof(*item)" which are + * linked via a "struct list" member named . A pointer to the head of + * the list is passed in . A temporary variable of same type + * as is needed so that may safely be deleted if needed. + * Example: list_for_each_entry_safe(cur_acl, tmp, known_acl, list) { ... }; + */ +#define list_for_each_entry_safe(item, back, list_head, member) \ + for (item = LIST_ELEM((list_head)->n, typeof(item), member), \ + back = LIST_ELEM(item->member.n, typeof(item), member); \ + &item->member != (list_head); \ + item = back, back = LIST_ELEM(back->member.n, typeof(back), member)) + + +#endif /* _COMMON_MINI_CLIST_H */ diff --git a/contrib/spoa_example/include/mini-sample.h b/contrib/spoa_example/include/mini-sample.h new file mode 100644 index 0000000000..96012c7983 --- /dev/null +++ b/contrib/spoa_example/include/mini-sample.h @@ -0,0 +1,50 @@ +#ifndef _MINI_SAMPLE_H +#define _MINI_SAMPLE_H + +#include +#include +#include + +/* input and output sample types */ +enum { + SMP_T_ANY = 0, /* any type */ + SMP_T_BOOL, /* boolean */ + SMP_T_SINT, /* signed 64bits integer type */ + SMP_T_ADDR, /* ipv4 or ipv6, only used for input type compatibility */ + SMP_T_IPV4, /* ipv4 type */ + SMP_T_IPV6, /* ipv6 type */ + SMP_T_STR, /* char string type */ + SMP_T_BIN, /* buffer type */ + SMP_T_METH, /* contain method */ + SMP_TYPES /* number of types, must always be last */ +}; + +/* describes a chunk of string */ +struct chunk { + char *str; /* beginning of the string itself. Might not be 0-terminated */ + int size; /* total size of the buffer, 0 if the *str is read-only */ + int len; /* current size of the string from first to last char. <0 = uninit. */ +}; + +union sample_value { + long long int sint; /* used for signed 64bits integers */ + struct in_addr ipv4; /* used for ipv4 addresses */ + struct in6_addr ipv6; /* used for ipv6 addresses */ + struct chunk str; /* used for char strings or buffers */ + //struct meth meth; /* used for http method */ +}; + +/* Used to store sample constant */ +struct sample_data { + int type; /* SMP_T_* */ + union sample_value u; /* sample data */ +}; + +/* a sample is a typed data extracted from a stream. It has a type, contents, + * validity constraints, a context for use in iterative calls. + */ +struct sample { + struct sample_data data; +}; +#endif + diff --git a/contrib/spoa_example/include/spoe_types.h b/contrib/spoa_example/include/spoe_types.h new file mode 100644 index 0000000000..b5abf39919 --- /dev/null +++ b/contrib/spoa_example/include/spoe_types.h @@ -0,0 +1,230 @@ +/* + * include/spoe_types.h + * Macros, variables and structures for the SPOE filter. + * + * Copyright (C) 2017 HAProxy Technologies, Christopher Faulet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _SPOE_TYPES_H +#define _SPOE_TYPES_H + +#include + +// Taken from HAProxy's defaults.h +/* Maximum host name length */ +#ifndef MAX_HOSTNAME_LEN +#if MAXHOSTNAMELEN +#define MAX_HOSTNAME_LEN MAXHOSTNAMELEN +#else +#define MAX_HOSTNAME_LEN 64 +#endif // MAXHOSTNAMELEN +#endif // MAX_HOSTNAME_LEN + +/* Flags set on the SPOE agent */ +#define SPOE_FL_CONT_ON_ERR 0x00000001 /* Do not stop events processing when an error occurred */ +#define SPOE_FL_PIPELINING 0x00000002 /* Set when SPOE agent supports pipelining (set by default) */ +#define SPOE_FL_ASYNC 0x00000004 /* Set when SPOE agent supports async (set by default) */ +#define SPOE_FL_SND_FRAGMENTATION 0x00000008 /* Set when SPOE agent supports sending fragmented payload */ +#define SPOE_FL_RCV_FRAGMENTATION 0x00000010 /* Set when SPOE agent supports receiving fragmented payload */ + +/* Flags set on the SPOE context */ +#define SPOE_CTX_FL_CLI_CONNECTED 0x00000001 /* Set after that on-client-session event was processed */ +#define SPOE_CTX_FL_SRV_CONNECTED 0x00000002 /* Set after that on-server-session event was processed */ +#define SPOE_CTX_FL_REQ_PROCESS 0x00000004 /* Set when SPOE is processing the request */ +#define SPOE_CTX_FL_RSP_PROCESS 0x00000008 /* Set when SPOE is processing the response */ +#define SPOE_CTX_FL_FRAGMENTED 0x00000010 /* Set when a fragmented frame is processing */ + +#define SPOE_CTX_FL_PROCESS (SPOE_CTX_FL_REQ_PROCESS|SPOE_CTX_FL_RSP_PROCESS) + +/* Flags set on the SPOE applet */ +#define SPOE_APPCTX_FL_PIPELINING 0x00000001 /* Set if pipelining is supported */ +#define SPOE_APPCTX_FL_ASYNC 0x00000002 /* Set if asynchronus frames is supported */ +#define SPOE_APPCTX_FL_FRAGMENTATION 0x00000004 /* Set if fragmentation is supported */ +#define SPOE_APPCTX_FL_PERSIST 0x00000008 /* Set if the applet is persistent */ + +#define SPOE_APPCTX_ERR_NONE 0x00000000 /* no error yet, leave it to zero */ +#define SPOE_APPCTX_ERR_TOUT 0x00000001 /* SPOE applet timeout */ + +/* Flags set on the SPOE frame */ +#define SPOE_FRM_FL_FIN 0x00000001 +#define SPOE_FRM_FL_ABRT 0x00000002 + +/* All possible states for a SPOE context */ +enum spoe_ctx_state { + SPOE_CTX_ST_NONE = 0, + SPOE_CTX_ST_READY, + SPOE_CTX_ST_ENCODING_MSGS, + SPOE_CTX_ST_SENDING_MSGS, + SPOE_CTX_ST_WAITING_ACK, + SPOE_CTX_ST_DONE, + SPOE_CTX_ST_ERROR, +}; + +/* All possible states for a SPOE applet */ +enum spoe_appctx_state { + SPOE_APPCTX_ST_CONNECT = 0, + SPOE_APPCTX_ST_CONNECTING, + SPOE_APPCTX_ST_IDLE, + SPOE_APPCTX_ST_PROCESSING, + SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY, + SPOE_APPCTX_ST_WAITING_SYNC_ACK, + SPOE_APPCTX_ST_DISCONNECT, + SPOE_APPCTX_ST_DISCONNECTING, + SPOE_APPCTX_ST_EXIT, + SPOE_APPCTX_ST_END, +}; + +/* All supported SPOE actions */ +enum spoe_action_type { + SPOE_ACT_T_SET_VAR = 1, + SPOE_ACT_T_UNSET_VAR, + SPOE_ACT_TYPES, +}; + +/* All supported SPOE events */ +enum spoe_event { + SPOE_EV_NONE = 0, + + /* Request events */ + SPOE_EV_ON_CLIENT_SESS = 1, + SPOE_EV_ON_TCP_REQ_FE, + SPOE_EV_ON_TCP_REQ_BE, + SPOE_EV_ON_HTTP_REQ_FE, + SPOE_EV_ON_HTTP_REQ_BE, + + /* Response events */ + SPOE_EV_ON_SERVER_SESS, + SPOE_EV_ON_TCP_RSP, + SPOE_EV_ON_HTTP_RSP, + + SPOE_EV_EVENTS +}; + +/* Errors triggered by streams */ +enum spoe_context_error { + SPOE_CTX_ERR_NONE = 0, + SPOE_CTX_ERR_TOUT, + SPOE_CTX_ERR_RES, + SPOE_CTX_ERR_TOO_BIG, + SPOE_CTX_ERR_FRAG_FRAME_ABRT, + SPOE_CTX_ERR_UNKNOWN = 255, + SPOE_CTX_ERRS, +}; + +/* Errors triggerd by SPOE applet */ +enum spoe_frame_error { + SPOE_FRM_ERR_NONE = 0, + SPOE_FRM_ERR_IO, + SPOE_FRM_ERR_TOUT, + SPOE_FRM_ERR_TOO_BIG, + SPOE_FRM_ERR_INVALID, + SPOE_FRM_ERR_NO_VSN, + SPOE_FRM_ERR_NO_FRAME_SIZE, + SPOE_FRM_ERR_NO_CAP, + SPOE_FRM_ERR_BAD_VSN, + SPOE_FRM_ERR_BAD_FRAME_SIZE, + SPOE_FRM_ERR_FRAG_NOT_SUPPORTED, + SPOE_FRM_ERR_INTERLACED_FRAMES, + SPOE_FRM_ERR_FRAMEID_NOTFOUND, + SPOE_FRM_ERR_RES, + SPOE_FRM_ERR_UNKNOWN = 99, + SPOE_FRM_ERRS, +}; + +/* Scopes used for variables set by agents. It is a way to be agnotic to vars + * scope. */ +enum spoe_vars_scope { + SPOE_SCOPE_PROC = 0, /* <=> SCOPE_PROC */ + SPOE_SCOPE_SESS, /* <=> SCOPE_SESS */ + SPOE_SCOPE_TXN, /* <=> SCOPE_TXN */ + SPOE_SCOPE_REQ, /* <=> SCOPE_REQ */ + SPOE_SCOPE_RES, /* <=> SCOPE_RES */ +}; + + +/* Describe an argument that will be linked to a message. It is a sample fetch, + * with an optional name. */ +struct spoe_arg { + char *name; /* Name of the argument, may be NULL */ + unsigned int name_len; /* The name length, 0 if NULL */ + struct sample_expr *expr; /* Sample expression */ + struct list list; /* Used to chain SPOE args */ +}; + +/* Used during the config parsing only because, when a SPOE agent section is + * parsed, messages can be undefined. */ +struct spoe_msg_placeholder { + char *id; /* SPOE message placeholder id */ + struct list list; /* Use to chain SPOE message placeholders */ +}; + +/* Describe a message that will be sent in a NOTIFY frame. A message has a name, + * an argument list (see above) and it is linked to a specific event. */ +struct spoe_message { + char *id; /* SPOE message id */ + unsigned int id_len; /* The message id length */ + struct spoe_agent *agent; /* SPOE agent owning this SPOE message */ + struct { + char *file; /* file where the SPOE message appears */ + int line; /* line where the SPOE message appears */ + } conf; /* config information */ + unsigned int nargs; /* # of arguments */ + struct list args; /* Arguments added when the SPOE messages is sent */ + struct list list; /* Used to chain SPOE messages */ + + enum spoe_event event; /* SPOE_EV_* */ +}; + +enum spoe_frame_type { + SPOE_FRM_T_UNSET = 0, + + /* Frames sent by HAProxy */ + SPOE_FRM_T_HAPROXY_HELLO = 1, + SPOE_FRM_T_HAPROXY_DISCON, + SPOE_FRM_T_HAPROXY_NOTIFY, + + /* Frames sent by the agents */ + SPOE_FRM_T_AGENT_HELLO = 101, + SPOE_FRM_T_AGENT_DISCON, + SPOE_FRM_T_AGENT_ACK +}; + +/* All supported data types */ +enum spoe_data_type { + SPOE_DATA_T_NULL = 0, + SPOE_DATA_T_BOOL, + SPOE_DATA_T_INT32, + SPOE_DATA_T_UINT32, + SPOE_DATA_T_INT64, + SPOE_DATA_T_UINT64, + SPOE_DATA_T_IPV4, + SPOE_DATA_T_IPV6, + SPOE_DATA_T_STR, + SPOE_DATA_T_BIN, + SPOE_DATA_TYPES +}; + +/* Masks to get data type or flags value */ +#define SPOE_DATA_T_MASK 0x0F +#define SPOE_DATA_FL_MASK 0xF0 + +/* Flags to set Boolean values */ +#define SPOE_DATA_FL_FALSE 0x00 +#define SPOE_DATA_FL_TRUE 0x10 + + +#endif /* _TYPES_SPOE_H */ diff --git a/contrib/spoa_example/include/spop_functions.h b/contrib/spoa_example/include/spop_functions.h new file mode 100644 index 0000000000..17011d0485 --- /dev/null +++ b/contrib/spoa_example/include/spop_functions.h @@ -0,0 +1,413 @@ +#ifndef _SPOP_FUNCTIONS_H +#define _SPOP_FUNCTIONS_H + +#include +#include +#include +#include + + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + + +/* Encode the integer into a varint (variable-length integer). The encoded + * value is copied in <*buf>. Here is the encoding format: + * + * 0 <= X < 240 : 1 byte (7.875 bits) [ XXXX XXXX ] + * 240 <= X < 2288 : 2 bytes (11 bits) [ 1111 XXXX ] [ 0XXX XXXX ] + * 2288 <= X < 264432 : 3 bytes (18 bits) [ 1111 XXXX ] [ 1XXX XXXX ] [ 0XXX XXXX ] + * 264432 <= X < 33818864 : 4 bytes (25 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*2 [ 0XXX XXXX ] + * 33818864 <= X < 4328786160 : 5 bytes (32 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*3 [ 0XXX XXXX ] + * ... + * + * On success, it returns the number of written bytes and <*buf> is moved after + * the encoded value. Otherwise, it returns -1. */ +static inline int +encode_varint(uint64_t i, char **buf, char *end) +{ + unsigned char *p = (unsigned char *)*buf; + int r; + + if (p >= (unsigned char *)end) + return -1; + + if (i < 240) { + *p++ = i; + *buf = (char *)p; + return 1; + } + + *p++ = (unsigned char)i | 240; + i = (i - 240) >> 4; + while (i >= 128) { + if (p >= (unsigned char *)end) + return -1; + *p++ = (unsigned char)i | 128; + i = (i - 128) >> 7; + } + + if (p >= (unsigned char *)end) + return -1; + *p++ = (unsigned char)i; + + r = ((char *)p - *buf); + *buf = (char *)p; + return r; +} + +/* Decode a varint from <*buf> and save the decoded value in <*i>. See + * 'spoe_encode_varint' for details about varint. + * On success, it returns the number of read bytes and <*buf> is moved after the + * varint. Otherwise, it returns -1. */ +static inline int +decode_varint(char **buf, char *end, uint64_t *i) +{ + unsigned char *p = (unsigned char *)*buf; + int r; + + if (p >= (unsigned char *)end) + return -1; + + *i = *p++; + if (*i < 240) { + *buf = (char *)p; + return 1; + } + + r = 4; + do { + if (p >= (unsigned char *)end) + return -1; + *i += (uint64_t)*p << r; + r += 7; + } while (*p++ >= 128); + + r = ((char *)p - *buf); + *buf = (char *)p; + return r; +} + +/* Encode a buffer. Its length is encoded as a varint, followed by a copy + * of . It must have enough space in <*buf> to encode the buffer, else an + * error is triggered. + * On success, it returns and <*buf> is moved after the encoded value. If + * an error occurred, it returns -1. */ +static inline int +spoe_encode_buffer(const char *str, size_t len, char **buf, char *end) +{ + char *p = *buf; + int ret; + + if (p >= end) + return -1; + + if (!len) { + *p++ = 0; + *buf = p; + return 0; + } + + ret = encode_varint(len, &p, end); + if (ret == -1 || p + len > end) + return -1; + + memcpy(p, str, len); + *buf = p + len; + return len; +} + +/* Encode a buffer, possibly partially. It does the same thing than + * 'spoe_encode_buffer', but if there is not enough space, it does not fail. + * On success, it returns the number of copied bytes and <*buf> is moved after + * the encoded value. If an error occured, it returns -1. */ +static inline int +spoe_encode_frag_buffer(const char *str, size_t len, char **buf, char *end) +{ + char *p = *buf; + int ret; + + if (p >= end) + return -1; + + if (!len) { + *p++ = 0; + *buf = p; + return 0; + } + + ret = encode_varint(len, &p, end); + if (ret == -1 || p >= end) + return -1; + + ret = (p+len < end) ? len : (end - p); + memcpy(p, str, ret); + *buf = p + ret; + return ret; +} + +/* Decode a buffer. The buffer length is decoded and saved in <*len>. <*str> + * points on the first byte of the buffer. + * On success, it returns the buffer length and <*buf> is moved after the + * encoded buffer. Otherwise, it returns -1. */ +static inline int +spoe_decode_buffer(char **buf, char *end, char **str, uint64_t *len) +{ + char *p = *buf; + uint64_t sz; + int ret; + + *str = NULL; + *len = 0; + + ret = decode_varint(&p, end, &sz); + if (ret == -1 || p + sz > end) + return -1; + + *str = p; + *len = sz; + *buf = p + sz; + return sz; +} + +/* Encode a typed data using value in . On success, it returns the number + * of copied bytes and <*buf> is moved after the encoded value. If an error + * occured, it returns -1. + * + * If the value is too big to be encoded, depending on its type, then encoding + * failed or the value is partially encoded. Only strings and binaries can be + * partially encoded. In this case, the offset <*off> is updated to known how + * many bytes has been encoded. If <*off> is zero at the end, it means that all + * data has been encoded. */ +static inline int +spoe_encode_data(struct sample *smp, unsigned int *off, char **buf, char *end) +{ + char *p = *buf; + int ret; + + if (p >= end) + return -1; + + if (smp == NULL) { + *p++ = SPOE_DATA_T_NULL; + goto end; + } + + switch (smp->data.type) { + case SMP_T_BOOL: + *p = SPOE_DATA_T_BOOL; + *p++ |= ((!smp->data.u.sint) ? SPOE_DATA_FL_FALSE : SPOE_DATA_FL_TRUE); + break; + + case SMP_T_SINT: + *p++ = SPOE_DATA_T_INT64; + if (encode_varint(smp->data.u.sint, &p, end) == -1) + return -1; + break; + + case SMP_T_IPV4: + if (p + 5 > end) + return -1; + *p++ = SPOE_DATA_T_IPV4; + memcpy(p, &smp->data.u.ipv4, 4); + p += 4; + break; + + case SMP_T_IPV6: + if (p + 17 > end) + return -1; + *p++ = SPOE_DATA_T_IPV6; + memcpy(p, &smp->data.u.ipv6, 16); + p += 16; + break; + + case SMP_T_STR: + case SMP_T_BIN: { + struct chunk *chk = &smp->data.u.str; + + /* Here, we need to know if the sample has already been + * partially encoded. If yes, we only need to encode the + * remaining, <*off> reprensenting the number of bytes + * already encoded. */ + if (!*off) { + /* First evaluation of the sample : encode the + * type (string or binary), the buffer length + * (as a varint) and at least 1 byte of the + * buffer. */ + struct chunk *chk = &smp->data.u.str; + + *p++ = (smp->data.type == SMP_T_STR) + ? SPOE_DATA_T_STR + : SPOE_DATA_T_BIN; + ret = spoe_encode_frag_buffer(chk->str, chk->len, &p, end); + if (ret == -1) + return -1; + } + else { + /* The sample has been fragmented, encode remaining data */ + ret = MIN(chk->len - *off, end - p); + memcpy(p, chk->str + *off, ret); + p += ret; + } + /* Now update <*off> */ + if (ret + *off != chk->len) + *off += ret; + else + *off = 0; + break; + } + /* + case SMP_T_METH: { + char *m; + size_t len; + + *p++ = SPOE_DATA_T_STR; + switch (smp->data.u.meth.meth) { + case HTTP_METH_OPTIONS: m = "OPTIONS"; len = 7; break; + case HTTP_METH_GET : m = "GET"; len = 3; break; + case HTTP_METH_HEAD : m = "HEAD"; len = 4; break; + case HTTP_METH_POST : m = "POST"; len = 4; break; + case HTTP_METH_PUT : m = "PUT"; len = 3; break; + case HTTP_METH_DELETE : m = "DELETE"; len = 6; break; + case HTTP_METH_TRACE : m = "TRACE"; len = 5; break; + case HTTP_METH_CONNECT: m = "CONNECT"; len = 7; break; + + default : + m = smp->data.u.meth.str.str; + len = smp->data.u.meth.str.len; + } + if (spoe_encode_buffer(m, len, &p, end) == -1) + return -1; + break; + } + */ + + default: + *p++ = SPOE_DATA_T_NULL; + break; + } + + end: + ret = (p - *buf); + *buf = p; + return ret; +} + +/* Skip a typed data. If an error occurred, -1 is returned, otherwise the number + * of skipped bytes is returned and the <*buf> is moved after skipped data. + * + * A types data is composed of a type (1 byte) and corresponding data: + * - boolean: non additional data (0 bytes) + * - integers: a variable-length integer (see decode_varint) + * - ipv4: 4 bytes + * - ipv6: 16 bytes + * - binary and string: a buffer prefixed by its size, a variable-length + * integer (see spoe_decode_buffer) */ +static inline int +spoe_skip_data(char **buf, char *end) +{ + char *str, *p = *buf; + int type, ret; + uint64_t v, sz; + + if (p >= end) + return -1; + + type = *p++; + switch (type & SPOE_DATA_T_MASK) { + case SPOE_DATA_T_BOOL: + break; + case SPOE_DATA_T_INT32: + case SPOE_DATA_T_INT64: + case SPOE_DATA_T_UINT32: + case SPOE_DATA_T_UINT64: + if (decode_varint(&p, end, &v) == -1) + return -1; + break; + case SPOE_DATA_T_IPV4: + if (p+4 > end) + return -1; + p += 4; + break; + case SPOE_DATA_T_IPV6: + if (p+16 > end) + return -1; + p += 16; + break; + case SPOE_DATA_T_STR: + case SPOE_DATA_T_BIN: + /* All the buffer must be skipped */ + if (spoe_decode_buffer(&p, end, &str, &sz) == -1) + return -1; + break; + } + + ret = (p - *buf); + *buf = p; + return ret; +} + +/* Decode a typed data and fill . If an error occurred, -1 is returned, + * otherwise the number of read bytes is returned and <*buf> is moved after the + * decoded data. See spoe_skip_data for details. */ +static inline int +spoe_decode_data(char **buf, char *end, struct sample *smp) +{ + char *str, *p = *buf; + int type, r = 0; + uint64_t sz; + + if (p >= end) + return -1; + + type = *p++; + switch (type & SPOE_DATA_T_MASK) { + case SPOE_DATA_T_BOOL: + smp->data.u.sint = ((type & SPOE_DATA_FL_MASK) == SPOE_DATA_FL_TRUE); + smp->data.type = SMP_T_BOOL; + break; + case SPOE_DATA_T_INT32: + case SPOE_DATA_T_INT64: + case SPOE_DATA_T_UINT32: + case SPOE_DATA_T_UINT64: + if (decode_varint(&p, end, (uint64_t *)&smp->data.u.sint) == -1) + return -1; + smp->data.type = SMP_T_SINT; + break; + case SPOE_DATA_T_IPV4: + if (p+4 > end) + return -1; + smp->data.type = SMP_T_IPV4; + memcpy(&smp->data.u.ipv4, p, 4); + p += 4; + break; + case SPOE_DATA_T_IPV6: + if (p+16 > end) + return -1; + memcpy(&smp->data.u.ipv6, p, 16); + smp->data.type = SMP_T_IPV6; + p += 16; + break; + case SPOE_DATA_T_STR: + case SPOE_DATA_T_BIN: + /* All the buffer must be decoded */ + if (spoe_decode_buffer(&p, end, &str, &sz) == -1) + return -1; + smp->data.u.str.str = str; + smp->data.u.str.len = sz; + smp->data.type = (type == SPOE_DATA_T_STR) ? SMP_T_STR : SMP_T_BIN; + break; + } + + r = (p - *buf); + *buf = p; + return r; +} + + +#endif diff --git a/contrib/spoa_example/spoa.c b/contrib/spoa_example/spoa.c index 351313adea..ee2e3de549 100644 --- a/contrib/spoa_example/spoa.c +++ b/contrib/spoa_example/spoa.c @@ -21,7 +21,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -33,10 +35,9 @@ #include #include -#include -#include - -#include +#include +#include +#include #define DEFAULT_PORT 12345 #define CONNECTION_BACKLOG 10