1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-30 00:00:16 +01:00

remove: lws_sequencer

lws_sequencer and lws_abstract were both false starts trying to do the
functionality of secure streams.

Since Secure Streams does a better job for both and there are no known
out-of-tree users of them, let's remove them and focus on Secure Streams.
This commit is contained in:
Andy Green 2021-10-10 08:45:30 +01:00
parent 057d03997f
commit f5edf9d75a
33 changed files with 5 additions and 3919 deletions

View file

@ -154,7 +154,6 @@ if (NOT LWS_WITH_NETWORK)
set(LWS_WITH_HTTP_STREAM_COMPRESSION 0)
set(LWS_WITH_HTTP_BROTLI 0)
set(LWS_WITH_POLL 0)
set(LWS_WITH_SEQUENCER 0)
set(LWS_ROLE_DBUS 0)
set(LWS_WITH_LWS_DSH 0)
set(LWS_WITH_THREADPOOL 0)

View file

@ -296,7 +296,6 @@ option(LWS_WITH_LEJP_CONF "With LEJP configuration parser as used by lwsws" OFF)
option(LWS_WITH_ZLIB "Include zlib support (required for extensions)" OFF)
option(LWS_WITH_BUNDLED_ZLIB "Use bundled zlib version (Windows only)" ${LWS_WITH_BUNDLED_ZLIB_DEFAULT})
option(LWS_WITH_MINIZ "Use miniz instead of zlib" OFF)
option(LWS_WITH_SEQUENCER "lws_seq_t support" OFF)
option(LWS_WITH_EXTERNAL_POLL "Support external POLL integration using callback messages (not recommended)" OFF)
option(LWS_WITH_LWS_DSH "Support lws_dsh_t Disordered Shared Heap" OFF)
option(LWS_CLIENT_HTTP_PROXYING "Support external http proxies for client connections" ON)

View file

@ -1,149 +0,0 @@
# `struct lws_sequencer` introduction
Often a single network action like a client GET is just part of a
larger series of actions, perhaps involving different connections.
Since lws operates inside an event loop, if the outer sequencing
doesn't, it can be awkward to synchronize these steps with what's
happening on the network with a particular connection on the event
loop thread.
![lws_sequencer](/doc-assets/lws_sequencer.svg)
`struct lws_sequencer` provides a generic way to stage multi-step
operations from inside the event loop. Because it participates
in the event loop similar to a wsi, it always operates from the
service thread context and can access structures that share the
service thread without locking. It can also provide its own
higher-level timeout handling.
Naturally you can have many of them running in the same event
loop operating independently.
Sequencers themselves bind to a pt (per-thread) service thread,
by default there's only one of these and it's the same as saying
they bind to an `lws_context`. The sequencer callback may create
wsi which in turn are bound to a vhost, but the sequencer itself
is above all that.
## Sequencer timeouts
The sequencer additionally maintains its own second-resolution timeout
checked by lws for the step being sequenced... this is independent of
any lws wsi timeouts which tend to be set and reset for very short-term
timeout protection inside one transaction.
The sequencer timeout operates separately and above any wsi timeout, and
is typically only reset by the sequencer callback when it receives an
event indicating a step completed or failed, or it sets up the next sequence
step.
If the sequencer timeout expires, then the sequencer receives a queued
`LWSSEQ_TIMED_OUT` message informing it, and it can take corrective action
or schedule a retry of the step. This message is queued and sent normally
under the service thread context and in order of receipt.
Unlike lws timeouts which force the wsi to close, the sequencer timeout
only sends the message. This allows the timeout to be used to, eg, wait
out a retry cooloff period and then start the retry when the
`LWSSEQ_TIMED_OUT` is received, according to the state of the sequencer.
## Creating an `struct lws_sequencer`
```
typedef struct lws_seq_info {
struct lws_context *context; /* lws_context for seq */
int tsi; /* thread service idx */
size_t user_size; /* size of user alloc */
void **puser; /* place ptr to user */
lws_seq_event_cb cb; /* seq callback */
const char *name; /* seq name */
const lws_retry_bo_t *retry; /* retry policy */
} lws_seq_info_t;
```
```
struct lws_sequencer *
lws_sequencer_create(lws_seq_info_t *info);
```
When created, in lws the sequencer objects are bound to a 'per-thread',
which is by default the same as to say bound to the `lws_context`. You
can tag them with an opaque user data pointer, and they are also bound to
a user-specified callback which handles sequencer events
```
typedef int (*lws_seq_event_cb)(struct lws_sequencer *seq, void *user_data,
lws_seq_events_t event, void *data);
```
`struct lws_sequencer` objects are private to lws and opaque to the user. A small
set of apis lets you perform operations on the pointer returned by the
create api.
## Queueing events on a sequencer
Each sequencer object can be passed "events", which are held on a per-sequencer
queue and handled strictly in the order they arrived on subsequent event loops.
`LWSSEQ_CREATED` and `LWSSEQ_DESTROYED` events are produced by lws reflecting
the sequencer's lifecycle, but otherwise the event indexes have a user-defined
meaning and are queued on the sequencer by user code for eventual consumption
by user code in the sequencer callback.
Pending events are removed from the sequencer queues and sent to the sequencer
callback from inside the event loop at a rate of one per event loop wait.
## Destroying sequencers
`struct lws_sequencer` objects are cleaned up during context destruction if they are
still around.
Normally the sequencer callback receives a queued message that
informs it that it's either failed at the current step, or succeeded and that
was the last step, and requests that it should be destroyed by returning
`LWSSEQ_RET_DESTROY` from the sequencer callback.
## Lifecycle considerations
Sequencers may spawn additional assets like client wsi as part of the sequenced
actions... the lifecycle of the sequencer and the assets overlap but do not
necessarily depend on each other... that is a wsi created by the sequencer may
outlive the sequencer.
It's important therefore to detach assets from the sequencer and the sequencer
from the assets when each step is over and the asset is "out of scope" for the
sequencer. It doesn't necessarily mean closing the assets, just making sure
pointers are invalidated. For example, if a client wsi held a pointer to the
sequencer as its `.user_data`, when the wsi is out of scope for the sequencer
it can set it to NULL, eg, `lws_set_wsi_user(wsi, NULL);`.
Under some conditions wsi may want to hang around a bit to see if there is a
subsequent client wsi transaction they can be reused on. They will clean
themselves up when they time out.
## Watching wsi lifecycle from a sequencer
When a sequencer is creating a wsi as part of its sequence, it will be very
interested in lifecycle events. At client wsi creation time, the sequencer
callback can set info->seq to itself in order to receive lifecycle messages
about its wsi.
|message|meaning|
|---|---|
|`LWSSEQ_WSI_CONNECTED`|The wsi has become connected|
|`LWSSEQ_WSI_CONN_FAIL`|The wsi has failed to connect|
|`LWSSEQ_WSI_CONN_CLOSE`|The wsi had been connected, but has now closed|
By receiving these, the sequencer can understand when it should attempt
reconnections or that it cannot progress the sequence.
When dealing with wsi that were created by the sequencer, they may close at
any time, eg, be closed by the remote peer or an intermediary. The
`LWSSEQ_WSI_CONN_CLOSE` message may have been queued but since they are
strictly handled in the order they arrived, before it was
handled an earlier message may want to cause some api to be called on
the now-free()-d wsi. To detect this situation safely, there is a
sequencer api `lws_sequencer_check_wsi()` which peeks the message
buffer and returns nonzero if it later contains an `LWSSEQ_WSI_CONN_CLOSE`
already.

View file

@ -54,7 +54,6 @@ extern "C" {
/* place for one-shot opaque forward references */
typedef struct lws_context * lws_ctx_t;
struct lws_sequencer;
struct lws_dsh;
/*
@ -664,7 +663,6 @@ struct lws;
#include <libwebsockets/lws-lwsac.h>
#include <libwebsockets/lws-fts.h>
#include <libwebsockets/lws-diskcache.h>
#include <libwebsockets/lws-sequencer.h>
#include <libwebsockets/lws-secure-streams.h>
#include <libwebsockets/lws-secure-streams-serialization.h>
#include <libwebsockets/lws-secure-streams-policy.h>
@ -672,11 +670,6 @@ struct lws;
#include <libwebsockets/lws-secure-streams-transport-proxy.h>
#include <libwebsockets/lws-jrpc.h>
#if !defined(LWS_PLAT_FREERTOS)
#include <libwebsockets/abstract/abstract.h>
#include <libwebsockets/lws-test-sequencer.h>
#endif
#include <libwebsockets/lws-async-dns.h>
#if defined(LWS_WITH_TLS)

View file

@ -1,138 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*
* These are used to optionally pass an array of index = C string, binary array,
* or ulong tokens to the abstract transport or protocol. For example if it's
* raw socket transport, then the DNS address to connect to and the port are
* passed using these when the client created and bound to the transport.
*/
typedef struct lws_token_map {
union {
const char *value;
uint8_t *bvalue;
unsigned long lvalue;
} u;
short name_index; /* 0 here indicates end of array */
short length_or_zero;
} lws_token_map_t;
/*
* The indvidual protocols and transports define their own name_index-es which
* are meaningful to them. Define index 0 globally as the end of an array of
* them, and provide bases so user protocol and transport ones don't overlap.
*/
enum {
LTMI_END_OF_ARRAY,
LTMI_PROTOCOL_BASE = 2048,
LTMI_TRANSPORT_BASE = 4096
};
struct lws_abs_transport;
struct lws_abs_protocol;
typedef struct lws_abs lws_abs_t;
LWS_VISIBLE LWS_EXTERN const lws_token_map_t *
lws_abs_get_token(const lws_token_map_t *token_map, short name_index);
/*
* the combination of a protocol, transport, and token maps for each
*/
typedef void lws_abs_transport_inst_t;
typedef void lws_abs_protocol_inst_t;
/**
* lws_abstract_alloc() - allocate and configure an lws_abs_t
*
* \param vhost: the struct lws_vhost to bind to
* \param user: opaque user pointer
* \param abstract_path: "protocol.transport" names
* \param ap_tokens: tokens for protocol options
* \param at_tokens: tokens for transport
* \param seq: optional sequencer we should bind to, or NULL
* \param opaque_user_data: data given in sequencer callback, if any
*
* Returns an allocated lws_abs_t pointer set up with the other arguments.
*
* Doesn't create a connection instance, just allocates the lws_abs_t and
* sets it up with the arguments.
*
* Returns NULL is there's any problem.
*/
LWS_VISIBLE LWS_EXTERN lws_abs_t *
lws_abstract_alloc(struct lws_vhost *vhost, void *user,
const char *abstract_path, const lws_token_map_t *ap_tokens,
const lws_token_map_t *at_tokens, struct lws_sequencer *seq,
void *opaque_user_data);
/**
* lws_abstract_free() - free an allocated lws_abs_t
*
* \param pabs: pointer to the lws_abs_t * to free
*
* Frees and sets the pointer to NULL.
*/
LWS_VISIBLE LWS_EXTERN void
lws_abstract_free(lws_abs_t **pabs);
/**
* lws_abs_bind_and_create_instance - use an abstract protocol and transport
*
* \param abs: the lws_abs_t describing the combination desired
*
* This instantiates an abstract protocol and abstract transport bound together.
* A single heap allocation is made for the combination and the protocol and
* transport creation ops are called on it. The ap_tokens and at_tokens
* are consulted by the creation ops to decide the details of the protocol and
* transport for the instance.
*/
LWS_VISIBLE LWS_EXTERN lws_abs_t *
lws_abs_bind_and_create_instance(const lws_abs_t *ai);
/**
* lws_abs_destroy_instance() - destroys an instance
*
* \param ai: pointer to the ai pointer to destroy
*
* This is for destroying an instance created by
* lws_abs_bind_and_create_instance() above.
*
* Calls the protocol and transport destroy operations on the instance, then
* frees the combined allocation in one step. The pointer ai is set to NULL.
*/
LWS_VISIBLE LWS_EXTERN void
lws_abs_destroy_instance(lws_abs_t **ai);
/*
* bring in all the protocols and transports definitions
*/
#include <libwebsockets/abstract/protocols.h>
#include <libwebsockets/abstract/transports.h>

View file

@ -1,88 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*
* Information about how this protocol handles multiple use of connections.
*
* .flags of 0 indicates each connection must start with a fresh transport.
*
* Flags can be used to indicate the protocol itself supports different
* kinds of multiple use. However the actual use or not of these may depend on
* negotiation with the remote peer.
*
* LWS_AP_FLAG_PIPELINE_TRANSACTIONS: other instances can be queued on one
* with an existing connection and get a
* chance to "hot take over" the existing
* transport in turn, like h1 keepalive
* pipelining
*
* LWS_AP_FLAG_MUXABLE_STREAM: an existing connection can absorb more child
* connections and mux them as separate child
* streams ongoing, like h2
*/
enum {
LWS_AP_FLAG_PIPELINE_TRANSACTIONS = (1 << 0),
LWS_AP_FLAG_MUXABLE_STREAM = (1 << 1),
};
typedef struct lws_abs_protocol {
const char *name;
int alloc;
int flags;
int (*create)(const struct lws_abs *ai);
void (*destroy)(lws_abs_protocol_inst_t **d);
int (*compare)(lws_abs_t *abs1, lws_abs_t *abs2);
/* events the transport invokes (handled by abstract protocol) */
int (*accept)(lws_abs_protocol_inst_t *d);
int (*rx)(lws_abs_protocol_inst_t *d, const uint8_t *b, size_t l);
int (*writeable)(lws_abs_protocol_inst_t *d, size_t budget);
int (*closed)(lws_abs_protocol_inst_t *d);
int (*heartbeat)(lws_abs_protocol_inst_t *d);
/* as parent, we get a notification a new child / queue entry
* bound to us... this is the parent lws_abs_t as arg */
int (*child_bind)(lws_abs_t *abs);
} lws_abs_protocol_t;
/**
* lws_abs_protocol_get_by_name() - returns a pointer to the named protocol ops
*
* \param name: the name of the abstract protocol
*
* Returns a pointer to the named protocol ops struct if available, otherwise
* NULL.
*/
LWS_VISIBLE LWS_EXTERN const lws_abs_protocol_t *
lws_abs_protocol_get_by_name(const char *name);
/*
* bring in public api pieces from protocols
*/
#include <libwebsockets/abstract/protocols/smtp.h>

View file

@ -1,115 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/** \defgroup smtp SMTP related functions
* ##SMTP related functions
* \ingroup lwsapi
*
* These apis let you communicate with a local SMTP server to send email from
* lws. It handles all the SMTP sequencing and protocol actions.
*
* Your system should have postfix, sendmail or another MTA listening on port
* 25 and able to send email using the "mail" commandline app. Usually distro
* MTAs are configured for this by default.
*
* You can either use the abstract protocol layer directly, or instead use the
* provided smtp sequencer... this takes care of creating the protocol
* connections, and provides and email queue and retry management.
*/
//@{
#if defined(LWS_WITH_SMTP)
enum {
LTMI_PSMTP_V_HELO = LTMI_PROTOCOL_BASE, /* u.value */
LTMI_PSMTP_V_LWS_SMTP_EMAIL_T, /* u.value */
};
enum {
LWS_SMTP_DISPOSITION_SENT,
LWS_SMTP_DISPOSITION_FAILED,
LWS_SMTP_DISPOSITION_FAILED_DESTROY
};
typedef struct lws_smtp_sequencer_args {
const char helo[32];
struct lws_vhost *vhost;
time_t retry_interval;
time_t delivery_timeout;
size_t email_queue_max;
size_t max_content_size;
} lws_smtp_sequencer_args_t;
typedef struct lws_smtp_sequencer lws_smtp_sequencer_t;
typedef struct lws_smtp_email lws_smtp_email_t;
LWS_VISIBLE LWS_EXTERN lws_smtp_sequencer_t *
lws_smtp_sequencer_create(const lws_smtp_sequencer_args_t *args);
LWS_VISIBLE LWS_EXTERN void
lws_smtp_sequencer_destroy(lws_smtp_sequencer_t *s);
typedef int (*lws_smtp_cb_t)(void *e, void *d, int disp, const void *b, size_t l);
typedef struct lws_smtp_email lws_smtp_email_t;
/**
* lws_smtpc_add_email() - Allocates and queues an email object
*
* \param s: smtp sequencer to queue on
* \param payload: the email payload string, with headers and terminating .
* \param payload_len: size in bytes of the payload string
* \param sender: the sender name and email
* \param recipient: the recipient name and email
* \param data: opaque user data returned in the done callback
* \param done: callback called when the email send succeeded or failed
*
* Allocates an email object and copies the payload, sender and recipient into
* it and initializes it. Returns NULL if OOM, otherwise the allocated email
* object.
*
* Because it copies the arguments into an allocated buffer, the original
* arguments can be safely destroyed after calling this.
*
* The done() callback must free the email object. It doesn't have to free any
* individual members.
*/
LWS_VISIBLE LWS_EXTERN int
lws_smtpc_add_email(lws_smtp_sequencer_t *s, const char *payload,
size_t payload_len, const char *sender,
const char *recipient, void *data, lws_smtp_cb_t done);
/**
* lws_smtpc_free_email() - Add email to the list of ones being sent
*
* \param e: email to queue for sending on \p c
*
* Adds an email to the linked-list of emails to send
*/
LWS_VISIBLE LWS_EXTERN int
lws_smtpc_free_email(lws_smtp_email_t *e);
#endif
//@}

View file

@ -1,65 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
/*
* Abstract transport ops
*/
typedef struct lws_abs_transport {
const char *name;
int alloc;
int (*create)(lws_abs_t *abs);
void (*destroy)(lws_abs_transport_inst_t **d);
/* check if the transport settings for these connections are the same */
int (*compare)(lws_abs_t *abs1, lws_abs_t *abs2);
/* events the abstract protocol invokes (handled by transport) */
int (*tx)(lws_abs_transport_inst_t *d, uint8_t *buf, size_t len);
int (*client_conn)(const lws_abs_t *abs);
int (*close)(lws_abs_transport_inst_t *d);
int (*ask_for_writeable)(lws_abs_transport_inst_t *d);
int (*set_timeout)(lws_abs_transport_inst_t *d, int reason, int secs);
int (*state)(lws_abs_transport_inst_t *d);
} lws_abs_transport_t;
/**
* lws_abs_protocol_get_by_name() - returns a pointer to the named protocol ops
*
* \param name: the name of the abstract protocol
*
* Returns a pointer to the named protocol ops struct if available, otherwise
* NULL.
*/
LWS_VISIBLE LWS_EXTERN const lws_abs_transport_t *
lws_abs_transport_get_by_name(const char *name);
/*
* bring in public api pieces from transports
*/
#include <libwebsockets/abstract/transports/raw-skt.h>
#include <libwebsockets/abstract/transports/unit-test.h>

View file

@ -1,29 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
enum {
LTMI_PEER_V_DNS_ADDRESS = LTMI_TRANSPORT_BASE, /* u.value */
LTMI_PEER_LV_PORT, /* u.lvalue */
LTMI_PEER_LV_TLS_FLAGS, /* u.lvalue */
};

View file

@ -1,81 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is an abstract transport useful for unit testing abstract protocols.
*
* Instead of passing data anywhere, you give the transport a list of packets
* to deliver and packets you expect back from the abstract protocol it's
* bound to.
*/
enum {
LWS_AUT_EXPECT_TEST_END = (1 << 0),
LWS_AUT_EXPECT_LOCAL_CLOSE = (1 << 1),
LWS_AUT_EXPECT_DO_REMOTE_CLOSE = (1 << 2),
LWS_AUT_EXPECT_TX /* expect this as tx from protocol */ = (1 << 3),
LWS_AUT_EXPECT_RX /* present this as rx to protocol */ = (1 << 4),
LWS_AUT_EXPECT_SHOULD_FAIL = (1 << 5),
LWS_AUT_EXPECT_SHOULD_TIMEOUT = (1 << 6),
};
typedef enum {
LPE_CONTINUE,
LPE_SUCCEEDED,
LPE_FAILED,
LPE_FAILED_UNEXPECTED_TIMEOUT,
LPE_FAILED_UNEXPECTED_PASS,
LPE_FAILED_UNEXPECTED_CLOSE,
LPE_SKIPPED,
LPE_CLOSING
} lws_unit_test_packet_disposition;
typedef int (*lws_unit_test_packet_test_cb)(const void *cb_user, int disposition);
typedef int (*lws_unit_test_packet_cb)(lws_abs_t *instance);
/* each step in the unit test */
typedef struct lws_unit_test_packet {
void *buffer;
lws_unit_test_packet_cb pre;
size_t len;
uint32_t flags;
} lws_unit_test_packet_t;
/* each unit test */
typedef struct lws_unit_test {
const char * name; /* NULL indicates end of test array */
lws_unit_test_packet_t * expect_array;
int max_secs;
} lws_unit_test_t;
enum {
LTMI_PEER_V_EXPECT_TEST = LTMI_TRANSPORT_BASE, /* u.value */
LTMI_PEER_V_EXPECT_RESULT_CB, /* u.value */
LTMI_PEER_V_EXPECT_RESULT_CB_ARG, /* u.value */
};
LWS_VISIBLE LWS_EXTERN const char *
lws_unit_test_result_name(int in);

View file

@ -171,11 +171,6 @@ struct lws_client_connect_info {
* tokens
*/
struct lws_sequencer *seq;
/**< NULL, or an lws_seq_t that wants to be given messages about
* this wsi's lifecycle as it connects, errors or closes.
*/
void *opaque_user_data;
/**< This data has no meaning to lws but is applied to the client wsi
* and can be retrieved by user code with lws_get_opaque_user_data().

View file

@ -201,7 +201,7 @@ struct lws_context_standalone;
LWS_VISIBLE LWS_EXTERN int
lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
void *opaque_user_data, struct lws_sspc_handle **ppss,
struct lws_sequencer *seq_owner, const char **ppayload_fmt);
void *reserved, const char **ppayload_fmt);
/**
* lws_sspc_destroy() - Destroy secure stream
@ -256,19 +256,6 @@ lws_sspc_request_tx_len(struct lws_sspc_handle *h, unsigned long len);
LWS_VISIBLE LWS_EXTERN lws_ss_state_return_t
lws_sspc_client_connect(struct lws_sspc_handle *h);
/**
* lws_sspc_get_sequencer() - Return parent sequencer pointer if any
*
* \param h: secure streams handle
*
* Returns NULL if the secure stream is not associated with a sequencer.
* Otherwise returns a pointer to the owning sequencer. You can use this to
* identify which sequencer to direct messages to, from the secure stream
* callback.
*/
LWS_VISIBLE LWS_EXTERN struct lws_sequencer *
lws_sspc_get_sequencer(struct lws_sspc_handle *h);
/**
* lws_sspc_proxy_create() - Start a unix domain socket proxy for Secure Streams
*

View file

@ -309,7 +309,7 @@ typedef struct lws_ss_info {
LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
void *opaque_user_data, struct lws_ss_handle **ppss,
struct lws_sequencer *seq_owner, const char **ppayload_fmt);
void *reserved, const char **ppayload_fmt);
/**
* lws_ss_destroy() - Destroy secure stream
@ -367,19 +367,6 @@ lws_ss_request_tx_len(struct lws_ss_handle *pss, unsigned long len);
LWS_VISIBLE LWS_EXTERN lws_ss_state_return_t LWS_WARN_UNUSED_RESULT
lws_ss_client_connect(struct lws_ss_handle *h);
/**
* lws_ss_get_sequencer() - Return parent sequencer pointer if any
*
* \param h: secure streams handle
*
* Returns NULL if the secure stream is not associated with a sequencer.
* Otherwise returns a pointer to the owning sequencer. You can use this to
* identify which sequencer to direct messages to, from the secure stream
* callback.
*/
LWS_VISIBLE LWS_EXTERN struct lws_sequencer *
lws_ss_get_sequencer(struct lws_ss_handle *h);
/**
* lws_ss_proxy_create() - Start a unix domain socket proxy for Secure Streams
*

View file

@ -1,243 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* lws_sequencer is intended to help implement sequences that:
*
* - outlive a single connection lifetime,
* - are not associated with a particular protocol,
* - are not associated with a particular vhost,
* - must receive and issue events inside the event loop
*
* lws_sequencer-s are bound to a pt (per-thread) which for the default case of
* one service thread is the same as binding to an lws_context.
*/
/*
* retry backoff table... retry n happens after .retry_ms_table[n] ms, with
* the last entry used if n is greater than the number of entries.
*
* The first .conceal_count retries are concealed, but after that the failures
* are reported.
*/
typedef enum {
LWSSEQ_CREATED, /* sequencer created */
LWSSEQ_DESTROYED, /* sequencer destroyed */
LWSSEQ_TIMED_OUT, /* sequencer timeout */
LWSSEQ_HEARTBEAT, /* 1Hz callback */
LWSSEQ_WSI_CONNECTED, /* wsi we bound to us has connected */
LWSSEQ_WSI_CONN_FAIL, /* wsi we bound to us has failed to connect */
LWSSEQ_WSI_CONN_CLOSE, /* wsi we bound to us has closed */
LWSSEQ_SS_STATE_BASE, /* secure streams owned by a sequencer provide
* automatic messages about state changes on
* the sequencer, passing the oridinal in the
* event argument field. The message index is
* LWSSEQ_SS_STATE_BASE + the enum from
* lws_ss_constate_t */
LWSSEQ_USER_BASE = 100 /* define your events from here */
} lws_seq_events_t;
typedef enum lws_seq_cb_return {
LWSSEQ_RET_CONTINUE,
LWSSEQ_RET_DESTROY
} lws_seq_cb_return_t;
/*
* handler for this sequencer. Return 0 if OK else nonzero to destroy the
* sequencer. LWSSEQ_DESTROYED will be called back to the handler so it can
* close / destroy any private assets associated with the sequence.
*
* The callback may return either LWSSEQ_RET_CONTINUE for the sequencer to
* resume or LWSSEQ_RET_DESTROY to indicate the sequence is finished.
*
* Event indexes consist of some generic ones but mainly user-defined ones
* starting from LWSSEQ_USER_BASE.
*/
typedef lws_seq_cb_return_t (*lws_seq_event_cb)(struct lws_sequencer *seq,
void *user, int event, void *data, void *aux);
typedef struct lws_seq_info {
struct lws_context *context; /* lws_context for seq */
int tsi; /* thread service idx */
size_t user_size; /* size of user alloc */
void **puser; /* place ptr to user */
lws_seq_event_cb cb; /* seq callback */
const char *name; /* seq name */
const lws_retry_bo_t *retry; /* retry policy */
uint8_t wakesuspend:1; /* important enough to
* wake system */
} lws_seq_info_t;
/**
* lws_seq_create() - create and bind sequencer to a pt
*
* \param info: information about sequencer to create
*
* This binds an abstract sequencer to a per-thread (by default, the single
* event loop of an lws_context). After the event loop starts, the sequencer
* will receive an LWSSEQ_CREATED event on its callback from the event loop
* context, where it can begin its sequence flow.
*
* Lws itself will only call the callback subsequently with LWSSEQ_DESTROYED
* when the sequencer is being destroyed.
*
* pt locking is used to protect the related data structures.
*/
LWS_VISIBLE LWS_EXTERN struct lws_sequencer *
lws_seq_create(lws_seq_info_t *info);
/**
* lws_seq_destroy() - destroy the sequencer
*
* \param seq: pointer to the the opaque sequencer pointer returned by
* lws_seq_create()
*
* This proceeds to destroy the sequencer, calling LWSSEQ_DESTROYED and then
* freeing the sequencer object itself. The pointed-to seq pointer will be
* set to NULL.
*/
LWS_VISIBLE LWS_EXTERN void
lws_seq_destroy(struct lws_sequencer **seq);
/**
* lws_seq_queue_event() - queue an event on the given sequencer
*
* \param seq: the opaque sequencer pointer returned by lws_seq_create()
* \param e: the event index to queue
* \param data: associated opaque (to lws) data to provide the callback
* \param aux: second opaque data to provide the callback
*
* This queues the event on a given sequencer. Queued events are delivered one
* per sequencer each subsequent time around the event loop, so the cb is called
* from the event loop thread context.
*
* Notice that because the events are delivered in order from the event loop,
* the scope of objects pointed to by \p data or \p aux may exceed the lifetime
* of the thing containing the pointed-to data. So it's usually better to pass
* values here.
*/
LWS_VISIBLE LWS_EXTERN int
lws_seq_queue_event(struct lws_sequencer *seq, lws_seq_events_t e, void *data,
void *aux);
/**
* lws_seq_check_wsi() - check if wsi still extant
*
* \param seq: the sequencer interested in the wsi
* \param wsi: the wsi we want to confirm hasn't closed yet
*
* Check if wsi still extant, by peeking in the message queue for a
* LWSSEQ_WSI_CONN_CLOSE message about wsi. (Doesn't need to do the same for
* CONN_FAIL since that will never have produced any messages prior to that).
*
* Use this to avoid trying to perform operations on wsi that have already
* closed but we didn't get to that message yet.
*
* Returns 0 if not closed yet or 1 if it has closed but we didn't process the
* close message yet.
*/
LWS_VISIBLE LWS_EXTERN int
lws_seq_check_wsi(struct lws_sequencer *seq, struct lws *wsi);
#define LWSSEQTO_NONE 0
/**
* lws_seq_timeout_us() - set a timeout by which the sequence must have
* completed by a different event or inform the
* sequencer
*
* \param seq: The sequencer to set the timeout on
* \param us: How many us in the future to fire the timeout
* LWS_SET_TIMER_USEC_CANCEL = cancel any existing timeout
*
* This api allows the sequencer to ask to be informed if it has not completed
* or disabled its timeout after secs seconds. Lws will send a LWSSEQ_TIMED_OUT
* event to the sequencer if the timeout expires.
*
* Typically the sequencer sets the timeout when starting a step, then waits to
* hear a queued event informing it the step completed or failed. The timeout
* provides a way to deal with the case the step neither completed nor failed
* within the timeout period.
*
* Lws wsi timeouts are not really suitable for this since they are focused on
* short-term protocol timeout protection and may be set and reset many times
* in one transaction. Wsi timeouts also enforce closure of the wsi when they
* trigger, sequencer timeouts have no side effect except to queue the
* LWSSEQ_TIMED_OUT message and leave it to the sequencer to decide how to
* react appropriately.
*/
LWS_VISIBLE LWS_EXTERN int
lws_seq_timeout_us(struct lws_sequencer *seq, lws_usec_t us);
/**
* lws_seq_from_user(): get the lws_seq_t pointer from the user ptr
*
* \param u: the sequencer user allocation returned by lws_seq_create() or
* provided in the sequencer callback
*
* This gets the lws_seq_t * from the sequencer user allocation pointer.
* Actually these are allocated at the same time in one step, with the user
* allocation immediately after the lws_seq_t, so lws can compute where
* the lws_seq_t is from having the user allocation pointer. Since the
* size of the lws_seq_t is unknown to user code, this helper does it for
* you.
*/
LWS_VISIBLE LWS_EXTERN struct lws_sequencer *
lws_seq_from_user(void *u);
/**
* lws_seq_us_since_creation(): elapsed seconds since sequencer created
*
* \param seq: pointer to the lws_seq_t
*
* Returns the number of us elapsed since the lws_seq_t was
* created. This is useful to calculate sequencer timeouts for the current
* step considering a global sequencer lifetime limit.
*/
LWS_VISIBLE LWS_EXTERN lws_usec_t
lws_seq_us_since_creation(struct lws_sequencer *seq);
/**
* lws_seq_name(): get the name of this sequencer
*
* \param seq: pointer to the lws_seq_t
*
* Returns the name given when the sequencer was created. This is useful to
* annotate logging when then are multiple sequencers in play.
*/
LWS_VISIBLE LWS_EXTERN const char *
lws_seq_name(struct lws_sequencer *seq);
/**
* lws_seq_get_context(): get the lws_context sequencer was created on
*
* \param seq: pointer to the lws_seq_t
*
* Returns the lws_context. Saves you having to store it if you have a seq
* pointer handy.
*/
LWS_VISIBLE LWS_EXTERN struct lws_context *
lws_seq_get_context(struct lws_sequencer *seq);

View file

@ -139,9 +139,6 @@ endif()
if (LWS_WITH_NETWORK)
add_subdir_include_dirs(core-net)
if (LWS_WITH_ABSTRACT)
add_subdir_include_dirs(abstract)
endif()
add_subdir_include_dirs(roles)
endif()

View file

@ -1,57 +0,0 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
list(APPEND SOURCES
abstract/abstract.c
)
if (LWS_WITH_SEQUENCER)
list(APPEND SOURCES
abstract/test-sequencer.c)
endif()
list(APPEND SOURCES
abstract/transports/unit-test.c)
#if (LWS_WITH_SMTP)
# list(APPEND SOURCES
# abstract/protocols/smtp/smtp.c
# abstract/protocols/smtp/smtp-sequencer.c
# )
#endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View file

@ -1,170 +0,0 @@
# Abstract protocols and transports
## Overview
Until now protocol implementations in lws have been done directly
to the network-related apis inside lws.
In an effort to separate out completely network implementation
details from protocol specification, lws now supports
"abstract protocols" and "abstract transports".
![lws_abstract overview](/doc-assets/abstract-overview.svg)
The concept is that the implementation is split into two separate
chunks of code hidden behind "ops" structs... the "abstract protocol"
implementation is responsible for the logical protocol operation
and reads and writes only memory buffers.
The "abstract transport" implementation is responsible for sending
and receiving buffers on some kind of transport, and again is hidden
behind a standardized ops struct.
In the system, both the abstract protocols and transports are
found by their name.
An actual "connection" is created by calling a generic api
`lws_abs_bind_and_create_instance()` to instantiate the
combination of a protocol and a transport.
This makes it possible to confidently offer the same protocol on
completely different transports, eg, like serial, or to wire
up the protocol implementation to a test jig sending canned
test vectors and confirming the response at buffer level, without
any network. The abstract protocol itself has no relationship
to the transport at all and is completely unchanged by changes
to the transport.
In addition, generic tokens to control settings in both the
protocol and the transport are passed in at instantiation-time,
eg, controlling the IP address targeted by the transport.
lws SMTP client support has been rewritten to use the new scheme,
and lws provides a raw socket transport built-in.
## Public API
The public api for defining abstract protocols and transports is
found at
- [abstract.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/abstract/abstract.h)
- [protocols.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/abstract/protocols.h)
- [transports.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/abstract/transports.h)
### `lws_abs_t`
The main structure that defines the abstraction is `lws_abs_t`,
this is a name and then pointers to the protocol and transport,
optional tokens to control both the protocol and transport,
and pointers to private allocations for both the
protocol and transport when instantiated.
The transport is selected using
```
LWS_VISIBLE LWS_EXTERN const lws_abs_transport_t *
lws_abs_transport_get_by_name(const char *name);
```
and similarly the protocol by
```
LWS_VISIBLE LWS_EXTERN const lws_abs_protocol_t *
lws_abs_protocol_get_by_name(const char *name);
```
At the moment only "`raw-skt`" is defined as an lws built-in, athough
you can also create your own mock transport the same way for creating
test jigs.
|transport op|meaning|
|---|---|
|`tx()`|transmit a buffer|
|`client_conn()`|start a connection to a peer|
|`close()`|request to close the connection to a peer|
|`ask_for_writeable()`|request a `writeable()` callback when tx can be used|
|`set_timeout()`|set a timeout that will close the connection if reached|
|`state()`|check if the connection is established and can carry traffic|
These are called by the protocol to get things done and make queries
through the abstract transport.
|protocol op|meaning|
|---|---|
|`accept()`|The peer has accepted the transport connection|
|`rx()`|The peer has sent us some payload|
|`writeable()`|The connection to the peer can take more tx|
|`closed()`|The connection to the peer has closed|
|`heartbeat()`|Called periodically even when no network events|
These are called by the transport to inform the protocol of events
and traffic.
### Instantiation
The user fills an lws_abs_t and passes a pointer to it to
`lws_abs_bind_and_create_instance()` to create an instantiation
of the protocol + transport.
### `lws_token_map_t`
The abstract protocol has no idea about a network or network addresses
or ports or whatever... it may not even be hooked up to one.
If the transport it is bound to wants things like that, they are passed
in using an array of `lws_token_map_t` at instantiation time.
For example this is passed to the raw socket protocol in the smtp client
minimal example to control where it would connect to:
```
static const lws_token_map_t smtp_abs_tokens[] = {
{
.u = { .value = "127.0.0.1" },
.name_index = LTMI_PEER_DNS_ADDRESS,
}, {
.u = { .lvalue = 25l },
.name_index = LTMI_PEER_PORT,
}};
```
## Steps for adding new abstract protocols
- add the public header in `./include/libwebsockets/abstract/protocols/`
- add a directory under `./lib/abstract/protocols/`
- add your protocol sources in the new directory
- in CMakeLists.txt:
- add an `LWS_WITH_xxx` for your protocol
- search for "using any abstract protocol" and add your `LWS_WITH_xxx` to
the if so it also sets `LWS_WITH_ABSTRACT` if any set
- add a clause to append your source to SOURCES if `LWS_WITH_xxx` enabled
- add your `lws_abs_protocol` to the list `available_abs_protocols` in
`./lib/abstract/abstract.c`
## Steps for adding new abstract transports
- add the public header in `./include/libwebsockets/abstract/transports/`
- add your transport sources under `./lib/abstract/transports/`
- in CMakeLists.txt append your transport sources to SOURCES if `LWS_WITH_ABSTRACT`
and any other cmake conditionals
- add an extern for your transport `lws_protocols` in `./lib/core-net/private.h`
- add your transport `lws_protocols` to `available_abstract_protocols` in
`./lib/core-net/vhost.c`
- add your `lws_abs_transport` to the list `available_abs_transports` in
`./lib/abstract/abstract.c`
# Protocol testing
## unit tests
lws features an abstract transport designed to facilitate unit testing. This
contains an lws_sequencer that performs the steps of tests involving sending the
protocol test vector buffers and confirming the response of the protocol matches
the test vectors.
## test-sequencer
test-sequencer is a helper that sequences running an array of unit tests and
collects the statistics and gives a PASS / FAIL result.
See the SMTP client api test for an example of how to use.

View file

@ -1,355 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.h>
#include <private-lib-abstract.h>
extern const lws_abs_transport_t lws_abs_transport_cli_raw_skt,
lws_abs_transport_cli_unit_test;
#if defined(LWS_WITH_SMTP)
extern const lws_abs_protocol_t lws_abs_protocol_smtp;
#endif
#if defined(LWS_WITH_MQTT)
extern const lws_abs_protocol_t lws_abs_protocol_mqttc;
#endif
static const lws_abs_transport_t * const available_abs_transports[] = {
&lws_abs_transport_cli_raw_skt,
&lws_abs_transport_cli_unit_test,
};
#if defined(LWS_WITH_ABSTRACT)
static const lws_abs_protocol_t * const available_abs_protocols[] = {
#if defined(LWS_WITH_SMTP)
&lws_abs_protocol_smtp,
#endif
#if defined(LWS_WITH_MQTT)
&lws_abs_protocol_mqttc,
#endif
};
#endif
const lws_abs_transport_t *
lws_abs_transport_get_by_name(const char *name)
{
int n;
for (n = 0; n < (int)LWS_ARRAY_SIZE(available_abs_transports); n++)
if (!strcmp(name, available_abs_transports[n]->name))
return available_abs_transports[n];
lwsl_err("%s: cannot find '%s'\n", __func__, name);
return NULL;
}
const lws_abs_protocol_t *
lws_abs_protocol_get_by_name(const char *name)
{
#if defined(LWS_WITH_ABSTRACT)
int n;
for (n = 0; n < (int)LWS_ARRAY_SIZE(available_abs_protocols); n++)
if (!strcmp(name, available_abs_protocols[n]->name))
return available_abs_protocols[n];
#endif
lwsl_err("%s: cannot find '%s'\n", __func__, name);
return NULL;
}
const lws_token_map_t *
lws_abs_get_token(const lws_token_map_t *token_map, short name_index)
{
if (!token_map)
return NULL;
do {
if (token_map->name_index == name_index)
return token_map;
token_map++;
} while (token_map->name_index);
return NULL;
}
static int
lws_abstract_compare_connection(lws_abs_t *abs1, lws_abs_t *abs2)
{
/* it has to be using the same protocol */
if (abs1->ap != abs2->ap)
return 1;
/* protocol has to allow some kind of binding */
if (!abs1->ap->flags)
return 1;
/* it has to be using the same transport */
if (abs1->at != abs2->at)
return 1;
/*
* The transport must feel the endpoint and conditions in use match the
* requested endpoint and conditions... and the transport type must be
* willing to allow it
*/
if (abs1->at->compare(abs1, abs2))
return 1;
/*
* The protocol must feel they are in compatible modes if any
* (and the protocol type must be willing to allow it)
*/
if (abs1->ap->compare(abs1, abs2))
return 1;
/*
* If no objection by now, we can say there's already a comparable
* connection and both the protocol and transport feel we can make
* use of it.
*/
return 0;
}
static int
find_compatible(struct lws_dll2 *d, void *user)
{
lws_abs_t *ai1 = (lws_abs_t *)user,
*ai2 = lws_container_of(d, lws_abs_t, abstract_instances);
if (!lws_abstract_compare_connection(ai1, ai2)) {
/* we can bind to it */
lws_dll2_add_tail(&ai1->bound, &ai2->children_owner);
return 1;
}
return 0;
}
lws_abs_t *
lws_abs_bind_and_create_instance(const lws_abs_t *abs)
{
size_t size = sizeof(lws_abs_t) + abs->ap->alloc + abs->at->alloc;
lws_abs_t *ai;
int n;
/*
* since we know we will allocate the lws_abs_t, the protocol's
* instance allocation, and the transport's instance allocation,
* we merge it into a single heap allocation
*/
ai = lws_malloc(size, "abs inst");
if (!ai)
return NULL;
*ai = *abs;
ai->ati = NULL;
ai->api = (char *)ai + sizeof(lws_abs_t);
if (!ai->ap->flags) /* protocol only understands single connections */
goto fresh;
lws_vhost_lock(ai->vh); /* ----------------------------------- vh { */
/*
* Let's have a look for any already-connected transport we can use
*/
n = lws_dll2_foreach_safe(&ai->vh->abstract_instances_owner, ai,
find_compatible);
lws_vhost_unlock(ai->vh); /* } vh --------------------------------- */
if (n)
goto vh_list_add;
/* there's no existing connection doing what we want */
fresh:
ai->ati = (char *)ai->api + abs->ap->alloc;
if (ai->at->create(ai)) {
ai->ati = NULL;
goto bail;
}
vh_list_add:
/* add us to the vhost's dll2 of instances */
lws_dll2_clear(&ai->abstract_instances);
lws_dll2_add_head(&ai->abstract_instances,
&ai->vh->abstract_instances_owner);
if (ai->ap->create(ai)) {
ai->api = NULL;
goto bail;
}
if (ai->bound.owner) { /* we are a piggybacker */
lws_abs_t *ai2 = lws_container_of(ai->bound.owner, lws_abs_t,
children_owner);
/*
* Provide an 'event' in the parent context to start handling
* the bind if it's otherwise idle. We give the parent abs
* because we don't know if we're "next" or whatever. Just that
* a child joined him and he should look into his child
* situation in case he was waiting for one to appear.
*/
if (ai2->ap->child_bind(ai2)) {
lwsl_info("%s: anticpated child bind fail\n", __func__);
lws_dll2_remove(&ai->bound);
goto bail;
}
}
return ai;
bail:
lws_abs_destroy_instance(&ai);
return NULL;
}
/*
* We get called to clean up each child that was still bound to a parent
* at the time the parent is getting destroyed.
*/
static void
__lws_abs_destroy_instance2(lws_abs_t **ai)
{
lws_abs_t *a = *ai;
if (a->api)
a->ap->destroy(&a->api);
if (a->ati)
a->at->destroy(&a->ati);
lws_dll2_remove(&a->abstract_instances);
*ai = NULL;
free(a);
}
static int
__reap_children(struct lws_dll2 *d, void *user)
{
lws_abs_t *ac = lws_container_of(d, lws_abs_t, bound);
lws_dll2_foreach_safe(&ac->children_owner, NULL, __reap_children);
/* then destroy ourselves */
__lws_abs_destroy_instance2(&ac);
return 0;
}
void
lws_abs_destroy_instance(lws_abs_t **ai)
{
lws_abs_t *a = *ai;
/* destroy child instances that are bound to us first... */
lws_vhost_lock(a->vh); /* ----------------------------------- vh { */
lws_dll2_foreach_safe(&a->children_owner, NULL, __reap_children);
/* ...then destroy ourselves */
__lws_abs_destroy_instance2(ai);
lws_vhost_unlock(a->vh); /* } vh --------------------------------- */
}
lws_abs_t *
lws_abstract_alloc(struct lws_vhost *vhost, void *user,
const char *abstract_path, const lws_token_map_t *ap_tokens,
const lws_token_map_t *at_tokens, struct lws_sequencer *seq,
void *opaque_user_data)
{
lws_abs_t *abs = lws_zalloc(sizeof(*abs), __func__);
struct lws_tokenize ts;
lws_tokenize_elem e;
char tmp[30];
if (!abs)
return NULL;
lws_tokenize_init(&ts, abstract_path, LWS_TOKENIZE_F_MINUS_NONTERM);
e = lws_tokenize(&ts);
if (e != LWS_TOKZE_TOKEN)
goto abs_path_problem;
if (lws_tokenize_cstr(&ts, tmp, sizeof(tmp)))
goto abs_path_problem;
abs->ap = lws_abs_protocol_get_by_name(tmp);
if (!abs->ap)
goto abs_path_problem;
e = lws_tokenize(&ts);
if (e != LWS_TOKZE_DELIMITER)
goto abs_path_problem;
e = lws_tokenize(&ts);
if (e != LWS_TOKZE_TOKEN)
goto abs_path_problem;
if (lws_tokenize_cstr(&ts, tmp, sizeof(tmp)))
goto abs_path_problem;
abs->at = lws_abs_transport_get_by_name(tmp);
if (!abs->at)
goto abs_path_problem;
abs->vh = vhost;
abs->ap_tokens = ap_tokens;
abs->at_tokens = at_tokens;
abs->seq = seq;
abs->opaque_user_data = opaque_user_data;
lwsl_info("%s: allocated %s\n", __func__, abstract_path);
return abs;
abs_path_problem:
lwsl_err("%s: bad abs path '%s'\n", __func__, abstract_path);
lws_free_set_NULL(abs);
return NULL;
}
void
lws_abstract_free(lws_abs_t **pabs)
{
if (*pabs)
lws_free_set_NULL(*pabs);
}

View file

@ -1,55 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if !defined(__PRIVATE_LIB_ABSTRACT_H__)
#define __PRIVATE_LIB_ABSTRACT_H__
typedef struct lws_token_map lws_token_map_t;
typedef void lws_abs_transport_inst_t;
typedef void lws_abs_protocol_inst_t;
typedef struct lws_abs {
void *user;
struct lws_vhost *vh;
const struct lws_abs_protocol *ap;
const lws_token_map_t *ap_tokens;
const struct lws_abs_transport *at;
const lws_token_map_t *at_tokens;
struct lws_sequencer *seq;
void *opaque_user_data;
/* vh lock */
struct lws_dll2_owner children_owner; /* our children / queue */
/* vh lock */
struct lws_dll2 bound; /* parent or encapsulator */
/* vh lock */
struct lws_dll2 abstract_instances;
lws_abs_transport_inst_t *ati;
lws_abs_protocol_inst_t *api;
} lws_abs_t;
#endif

View file

@ -1,24 +0,0 @@
#define LWS_SMTP_MAX_EMAIL_LEN 32
/*
* These are allocated on to the heap with an over-allocation to hold the
* payload at the end
*/
typedef struct lws_smtp_email {
struct lws_dll2 list;
void *data;
char from[LWS_SMTP_MAX_EMAIL_LEN];
char to[LWS_SMTP_MAX_EMAIL_LEN];
time_t added;
time_t last_try;
lws_smtp_cb_t done;
int tries;
/* email payload follows */
} lws_smtp_email_t;

View file

@ -1,323 +0,0 @@
/*
* Abstract SMTP support for libwebsockets - SMTP sequencer
*
* Copyright (C) 2016-2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This sequencer sits above the abstract protocol, and manages queueing,
* retrying mail transmission, and retry limits.
*
* Having the sequencer means that, eg, we can manage retries after complete
* connection failure.
*
* Connections to the smtp server are serialized
*/
#include "private-lib-core.h"
#include "private-lib-abstract-protocols-smtp.h"
typedef enum {
LSMTPSS_DISCONNECTED,
LSMTPSS_CONNECTING,
LSMTPSS_CONNECTED,
LSMTPSS_BUSY,
} smtpss_connstate_t;
typedef struct lws_smtp_sequencer {
struct lws_dll2_owner emails_owner; /* email queue */
lws_abs_t *abs, *instance;
lws_smtp_sequencer_args_t args;
struct lws_sequencer *seq;
smtpss_connstate_t connstate;
time_t email_connect_started;
/* holds the HELO for the smtp protocol to consume */
lws_token_map_t apt[3];
} lws_smtp_sequencer_t;
/* sequencer messages specific to this sequencer */
enum {
SEQ_MSG_CLIENT_FAILED = LWSSEQ_USER_BASE,
SEQ_MSG_CLIENT_DONE,
};
/*
* We're going to bind to the raw-skt transport, so tell that what we want it
* to connect to
*/
static const lws_token_map_t smtp_rs_transport_tokens[] = {
{
.u = { .value = "127.0.0.1" },
.name_index = LTMI_PEER_V_DNS_ADDRESS,
}, {
.u = { .lvalue = 25 },
.name_index = LTMI_PEER_LV_PORT,
}, {
}
};
static void
lws_smtpc_kick_internal(lws_smtp_sequencer_t *s)
{
lws_smtp_email_t *e;
lws_dll2_t *d;
char buf[64];
int n;
lws_dll2_t *pd2;
pd2 = lws_dll2_get_head(&s->emails_owner);
if (!pd2)
return;
e = lws_container_of(pd2, lws_smtp_email_t, list);
if (!e)
return;
/* Is there something to do? If so, we need a connection... */
if (s->connstate == LSMTPSS_DISCONNECTED) {
s->apt[0].u.value = s->args.helo;
s->apt[0].name_index = LTMI_PSMTP_V_HELO;
s->apt[1].u.value = (void *)e;
s->apt[1].name_index = LTMI_PSMTP_V_LWS_SMTP_EMAIL_T;
/*
* create and connect the smtp protocol + transport
*/
s->abs = lws_abstract_alloc(s->args.vhost, NULL, "smtp.raw_skt",
s->apt, smtp_rs_transport_tokens,
s->seq, NULL);
if (!s->abs)
return;
s->instance = lws_abs_bind_and_create_instance(s->abs);
if (!s->instance) {
lws_abstract_free(&s->abs);
lwsl_err("%s: failed to create SMTP client\n", __func__);
goto bail1;
}
s->connstate = LSMTPSS_CONNECTING;
lws_seq_timeout_us(s->seq, 10 * LWS_USEC_PER_SEC);
return;
}
/* ask the transport if we have a connection to the server ongoing */
if (s->abs->at->state(s->abs->ati)) {
/*
* there's a connection, it could be still trying to connect
* or established
*/
s->abs->at->ask_for_writeable(s->abs->ati);
return;
}
/* there's no existing connection */
lws_smtpc_state_transition(c, LGSSMTP_CONNECTING);
if (s->abs->at->client_conn(s->abs)) {
lwsl_err("%s: failed to connect\n", __func__);
return;
}
e->tries++;
e->last_try = lws_now_secs();
}
/*
* The callback we get from the smtp protocol... we use this to drive
* decisions about destroy email, retry and fail.
*
* Sequencer will handle it via the event loop.
*/
static int
email_result(void *e, void *d, int disp, void *b, size_t l)
{
lws_smtp_sequencer_t *s = (lws_smtp_sequencer_t *)d;
lws_sequencer_event(s->seq, LWSSEQ_USER_BASE + disp, e);
return 0;
}
static int
cleanup(struct lws_dll2 *d, void *user)
{
lws_smtp_email_t *e;
e = lws_container_of(d, lws_smtp_email_t, list);
if (e->done)
e->done(e, "destroying", 10);
lws_dll2_remove(d);
lws_free(e);
return 0;
}
static lws_seq_cb_return_t
smtp_sequencer_cb(struct lws_sequencer *seq, void *user, int event, void *data)
{
struct lws_smtp_sequencer_t *s = (struct lws_smtp_sequencer_t *)user;
switch ((int)event) {
case LWSSEQ_CREATED: /* our sequencer just got started */
lwsl_notice("%s: %s: created\n", __func__,
lws_sequencer_name(seq));
s->connstate = LSMTPSS_DISCONNECTED;
s->state = 0; /* first thing we'll do is the first url */
goto step;
case LWSSEQ_DESTROYED:
lws_dll2_foreach_safe(&s->pending_owner, NULL, cleanup);
break;
case LWSSEQ_TIMED_OUT:
lwsl_notice("%s: LWSSEQ_TIMED_OUT\n", __func__);
break;
case LWSSEQ_USER_BASE + LWS_SMTP_DISPOSITION_SENT:
lws_smtpc_free_email(data);
break;
case LWSSEQ_WSI_CONNECTED:
s->connstate = LSMTPSS_CONNECTED;
lws_smtpc_kick_internal(s);
break;
case LWSSEQ_WSI_CONN_FAIL:
case LWSSEQ_WSI_CONN_CLOSE:
s->connstate = LSMTPSS_DISCONNECTED;
lws_smtpc_kick_internal(s);
break;
case SEQ_MSG_SENT:
break;
default:
break;
}
return LWSSEQ_RET_CONTINUE;
}
/*
* Creates an lws_sequencer to manage the test sequence
*/
lws_smtp_sequencer_t *
lws_smtp_sequencer_create(const lws_smtp_sequencer_args_t *args)
{
lws_smtp_sequencer_t *s;
struct lws_sequencer *seq;
/*
* Create a sequencer in the event loop to manage the SMTP queue
*/
seq = lws_sequencer_create(args->vhost->context, 0,
sizeof(lws_smtp_sequencer_t), (void **)&s,
smtp_sequencer_cb, "smtp-seq");
if (!seq) {
lwsl_err("%s: unable to create sequencer\n", __func__);
return NULL;
}
s->abs = *args->abs;
s->args = *args;
s->seq = seq;
/* set defaults in our copy of the args */
if (!s->args.helo[0])
strcpy(s->args.helo, "default-helo");
if (!s->args.email_queue_max)
s->args.email_queue_max = 8;
if (!s->args.retry_interval)
s->args.retry_interval = 15 * 60;
if (!s->args.delivery_timeout)
s->args.delivery_timeout = 12 * 60 * 60;
return s;
}
void
lws_smtp_sequencer_destroy(lws_smtp_sequencer_t *s)
{
/* sequencer destruction destroys all assets */
lws_sequencer_destroy(&s->seq);
}
int
lws_smtpc_add_email(lws_smtp_sequencer_t *s, const char *payload,
size_t payload_len, const char *sender,
const char *recipient, void *data, lws_smtp_cb_t done)
{
lws_smtp_email_t *e;
if (s->emails_owner.count > s->args.email_queue_max) {
lwsl_err("%s: email queue at limit of %d\n", __func__,
(int)s->args.email_queue_max);
return 1;
}
if (!done)
return 1;
e = malloc(sizeof(*e) + payload_len + 1);
if (!e)
return 1;
memset(e, 0, sizeof(*e));
e->data = data;
e->done = done;
lws_strncpy(e->from, sender, sizeof(e->from));
lws_strncpy(e->to, recipient, sizeof(e->to));
memcpy((char *)&e[1], payload, payload_len + 1);
e->added = lws_now_secs();
e->last_try = 0;
e->tries = 0;
lws_dll2_clear(&e->list);
lws_dll2_add_tail(&e->list, &s->emails_owner);
lws_smtpc_kick_internal(s);
return 0;
}

View file

@ -1,382 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "private-lib-abstract.h"
/** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */
typedef enum lwsgs_smtp_states {
LGSSMTP_IDLE, /**< awaiting new email */
LGSSMTP_CONNECTING, /**< opening tcp connection to MTA */
LGSSMTP_CONNECTED, /**< tcp connection to MTA is connected */
/* (server sends greeting) */
LGSSMTP_SENT_HELO, /**< sent the HELO */
LGSSMTP_SENT_FROM, /**< sent FROM */
LGSSMTP_SENT_TO, /**< sent TO */
LGSSMTP_SENT_DATA, /**< sent DATA request */
LGSSMTP_SENT_BODY, /**< sent the email body */
/*
* (server sends, eg, "250 Ok: queued as 12345")
* at this point we can return to LGSSMTP_SENT_HELO and send a
* new email, or continue below to QUIT, or just wait
*/
LGSSMTP_SENT_QUIT, /**< sent the session quit */
/* (server sends, eg, "221 Bye" and closes the connection) */
} lwsgs_smtp_states_t;
/** abstract protocol instance data */
typedef struct lws_smtp_client_protocol {
const struct lws_abs *abs;
lwsgs_smtp_states_t estate;
lws_smtp_email_t *e; /* the email we are trying to send */
const char *helo;
unsigned char send_pending:1;
} lws_smtpcp_t;
static const short retcodes[] = {
0, /* idle */
0, /* connecting */
220, /* connected */
250, /* helo */
250, /* from */
250, /* to */
354, /* data */
250, /* body */
221, /* quit */
};
static void
lws_smtpc_state_transition(lws_smtpcp_t *c, lwsgs_smtp_states_t s)
{
lwsl_debug("%s: cli %p: state %d -> %d\n", __func__, c, c->estate, s);
c->estate = s;
}
static lws_smtp_email_t *
lws_smtpc_get_email(lws_smtpcp_t *c)
{
const lws_token_map_t *tm;
/* ... the email we want to send */
tm = lws_abs_get_token(c->abs->ap_tokens, LTMI_PSMTP_V_LWS_SMTP_EMAIL_T);
if (!tm) {
assert(0);
return NULL;
}
return (lws_smtp_email_t *)tm->u.value;
}
/*
* Called when something happened so that we know now the final disposition of
* the email send attempt, for good or ill.
*
* Inform the owner via the done callback and set up the next queued one if any.
*
* Returns nonzero if we queued a new one
*/
static int
lws_smtpc_email_disposition(lws_smtpcp_t *c, int disp, const void *buf,
size_t len)
{
lws_smtpcp_t *ch;
lws_abs_t *ach;
lws_dll2_t *d;
lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO);
/* lifetime of the email object is handled by done callback */
c->e->done(c->e, c->e->data, disp, buf, len);
c->e = NULL;
/* this may not be the time to try to send anything else... */
if (disp == LWS_SMTP_DISPOSITION_FAILED_DESTROY)
return 0;
/* ... otherwise... do we have another queued? */
d = lws_dll2_get_tail(&c->abs->children_owner);
if (!d)
return 0;
ach = lws_container_of(d, lws_abs_t, bound);
ch = (lws_smtpcp_t *)ach->api;
c->e = lws_smtpc_get_email(ch);
/* since we took it on, remove it from the queue */
lws_dll2_remove(d);
return 1;
}
/*
* we became connected
*/
static int
lws_smtpc_abs_accept(lws_abs_protocol_inst_t *api)
{
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
/* we have become connected in the tcp sense */
lws_smtpc_state_transition(c, LGSSMTP_CONNECTED);
/*
* From the accept(), the next thing that should happen is the SMTP
* server sends its greeting like "220 smtp2.example.com ESMTP Postfix",
* we'll hear about it in the rx callback, or time out
*/
c->abs->at->set_timeout(c->abs->ati,
PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, 3);
return 0;
}
static int
lws_smtpc_abs_rx(lws_abs_protocol_inst_t *api, const uint8_t *buf, size_t len)
{
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
char dotstar[96], at[5];
int n;
c->abs->at->set_timeout(c->abs->ati, NO_PENDING_TIMEOUT, 0);
lws_strncpy(at, (const char *)buf, sizeof(at));
n = atoi(at);
switch (c->estate) {
case LGSSMTP_CONNECTED:
if (n != 220) {
/*
* The server did not properly greet us... we can't
* even get started, so fail the transport connection
* (and anything queued on it)
*/
lws_strnncpy(dotstar, (const char *)buf, len, sizeof(dotstar));
lwsl_err("%s: server: %s\n", __func__, dotstar);
return 1;
}
break;
case LGSSMTP_SENT_BODY:
/*
* We finished one way or another... let's prepare to send a
* new one... or wait until server hangs up on us
*/
if (!lws_smtpc_email_disposition(c,
n == 250 ? LWS_SMTP_DISPOSITION_SENT :
LWS_SMTP_DISPOSITION_FAILED,
"destroyed", 0))
return 0; /* become idle */
break; /* ask to send */
case LGSSMTP_SENT_QUIT:
lwsl_debug("%s: done\n", __func__);
lws_smtpc_state_transition(c, LGSSMTP_IDLE);
return 1;
default:
if (n != retcodes[c->estate]) {
lws_strnncpy(dotstar, buf, len, sizeof(dotstar));
lwsl_notice("%s: bad response: %d (state %d) %s\n",
__func__, n, c->estate, dotstar);
lws_smtpc_email_disposition(c,
LWS_SMTP_DISPOSITION_FAILED, buf, len);
return 0;
}
break;
}
c->send_pending = 1;
c->abs->at->ask_for_writeable(c->abs->ati);
return 0;
}
static int
lws_smtpc_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget)
{
char b[256 + LWS_PRE], *p = b + LWS_PRE;
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
int n;
if (!c->send_pending || !c->e)
return 0;
c->send_pending = 0;
lwsl_debug("%s: writing response for state %d\n", __func__, c->estate);
switch (c->estate) {
case LGSSMTP_CONNECTED:
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "HELO %s\n", c->helo);
lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO);
break;
case LGSSMTP_SENT_HELO:
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "MAIL FROM: <%s>\n",
c->e->from);
lws_smtpc_state_transition(c, LGSSMTP_SENT_FROM);
break;
case LGSSMTP_SENT_FROM:
n = lws_snprintf(p, sizeof(b) - LWS_PRE,
"RCPT TO: <%s>\n", c->e->to);
lws_smtpc_state_transition(c, LGSSMTP_SENT_TO);
break;
case LGSSMTP_SENT_TO:
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "DATA\n");
lws_smtpc_state_transition(c, LGSSMTP_SENT_DATA);
break;
case LGSSMTP_SENT_DATA:
p = (char *)&c->e[1];
n = strlen(p);
lws_smtpc_state_transition(c, LGSSMTP_SENT_BODY);
break;
case LGSSMTP_SENT_BODY:
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "quit\n");
lws_smtpc_state_transition(c, LGSSMTP_SENT_QUIT);
break;
case LGSSMTP_SENT_QUIT:
return 0;
default:
return 0;
}
//puts(p);
c->abs->at->tx(c->abs->ati, (uint8_t *)p, n);
return 0;
}
static int
lws_smtpc_abs_closed(lws_abs_protocol_inst_t *api)
{
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
if (c)
lws_smtpc_state_transition(c, LGSSMTP_IDLE);
return 0;
}
/*
* Creating for initial transport and for piggybacking on another transport
* both get created here the same. But piggybackers have ai->bound attached.
*/
static int
lws_smtpc_create(const lws_abs_t *ai)
{
lws_smtpcp_t *c = (lws_smtpcp_t *)ai->api;
memset(c, 0, sizeof(*c));
c->abs = ai;
c->e = lws_smtpc_get_email(c);
lws_smtpc_state_transition(c, lws_dll2_is_detached(&ai->bound) ?
LGSSMTP_CONNECTING : LGSSMTP_IDLE);
/* If we are initiating the transport, we will get an accept() next...
*
* If we are piggybacking, the parent will get a .child_bind() after
* this to give it a chance to act on us joining (eg, it was completely
* idle and we joined).
*/
return 0;
}
static void
lws_smtpc_destroy(lws_abs_protocol_inst_t **_c)
{
lws_smtpcp_t *c = (lws_smtpcp_t *)*_c;
if (!c)
return;
/* so if we are still holding on to c->e, we have failed to send it */
if (c->e)
lws_smtpc_email_disposition(c,
LWS_SMTP_DISPOSITION_FAILED_DESTROY, "destroyed", 0);
*_c = NULL;
}
static int
lws_smtpc_compare(lws_abs_t *abs1, lws_abs_t *abs2)
{
return 0;
}
static int
lws_smtpc_child_bind(lws_abs_t *abs)
{
return 0;
}
/* events the transport invokes (handled by abstract protocol) */
const lws_abs_protocol_t lws_abs_protocol_smtp = {
.name = "smtp",
.alloc = sizeof(lws_smtpcp_t),
.flags = LWSABSPR_FLAG_PIPELINE,
.create = lws_smtpc_create,
.destroy = lws_smtpc_destroy,
.compare = lws_smtpc_compare,
.accept = lws_smtpc_abs_accept,
.rx = lws_smtpc_abs_rx,
.writeable = lws_smtpc_abs_writeable,
.closed = lws_smtpc_abs_closed,
.heartbeat = NULL,
.child_bind = lws_smtpc_child_bind,
};

View file

@ -1,274 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* A helper for running multiple unit tests against abstract protocols.
*
* An lws_seq_t is used to base its actions in the event loop and manage
* the sequencing of multiple tests. A new abstract connection is instantiated
* for each test using te
*/
#include <private-lib-core.h>
struct lws_seq_test_sequencer {
lws_abs_t original_abs;
lws_test_sequencer_args_t args;
struct lws_context *context;
struct lws_vhost *vhost;
struct lws_sequencer *unit_test_seq;
/* holds the per-test token for the unit-test transport to consume */
lws_token_map_t uttt[4];
lws_abs_t *instance;
int state;
};
/* sequencer messages specific to this sequencer */
enum {
SEQ_MSG_PASS = LWSSEQ_USER_BASE,
SEQ_MSG_FAIL,
SEQ_MSG_FAIL_TIMEOUT,
};
/*
* We get called back when the unit test transport has decided if the test
* passed or failed. We get the priv, and report to the sequencer message queue
* what the result was.
*/
static int
unit_test_result_cb(const void *cb_user, int disposition)
{
const struct lws_seq_test_sequencer *s =
(const struct lws_seq_test_sequencer *)cb_user;
int r;
lwsl_debug("%s: disp %d\n", __func__, disposition);
switch (disposition) {
case LPE_FAILED_UNEXPECTED_PASS:
case LPE_FAILED_UNEXPECTED_CLOSE:
case LPE_FAILED:
r = SEQ_MSG_FAIL;
break;
case LPE_FAILED_UNEXPECTED_TIMEOUT:
r = SEQ_MSG_FAIL_TIMEOUT;
break;
case LPE_SUCCEEDED:
r = SEQ_MSG_PASS;
break;
default:
assert(0);
return -1;
}
lws_seq_queue_event(s->unit_test_seq, r, NULL, NULL);
((struct lws_seq_test_sequencer *)s)->instance = NULL;
return 0;
}
/*
* We receive the unit test result callback's messages via the message queue.
*
* We log the results and always move on to the next test until there are no
* more tests.
*/
static lws_seq_cb_return_t
test_sequencer_cb(struct lws_sequencer *seq, void *user, int event, void *data,
void *aux)
{
struct lws_seq_test_sequencer *s =
(struct lws_seq_test_sequencer *)user;
lws_unit_test_packet_t *exp = (lws_unit_test_packet_t *)
s->args.tests[s->state].expect_array;
lws_abs_t test_abs;
switch ((int)event) {
case LWSSEQ_CREATED: /* our sequencer just got started */
lwsl_notice("%s: %s: created\n", __func__,
lws_seq_name(seq));
s->state = 0; /* first thing we'll do is the first url */
goto step;
case LWSSEQ_DESTROYED:
/*
* We are going down... if we have a child unit test sequencer
* still around inform and destroy it
*/
if (s->instance) {
s->instance->at->close(s->instance);
s->instance = NULL;
}
break;
case SEQ_MSG_FAIL_TIMEOUT: /* current step timed out */
if (exp->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT) {
lwsl_user("%s: test %d got expected timeout\n",
__func__, s->state);
goto pass;
}
lwsl_user("%s: seq timed out at step %d\n", __func__, s->state);
s->args.results[s->state] = LPE_FAILED_UNEXPECTED_TIMEOUT;
goto done; /* always move on to the next test */
case SEQ_MSG_FAIL:
if (exp->flags & LWS_AUT_EXPECT_SHOULD_FAIL) {
/*
* in this case, we expected to fail like this, it's OK
*/
lwsl_user("%s: test %d failed as expected\n",
__func__, s->state);
goto pass; /* always move on to the next test */
}
lwsl_user("%s: seq failed at step %d\n", __func__, s->state);
s->args.results[s->state] = LPE_FAILED;
goto done; /* always move on to the next test */
case SEQ_MSG_PASS:
if (exp->flags & (LWS_AUT_EXPECT_SHOULD_FAIL |
LWS_AUT_EXPECT_SHOULD_TIMEOUT)) {
/*
* In these specific cases, done would be a failure,
* we expected to timeout or fail
*/
lwsl_user("%s: seq failed at step %d\n", __func__,
s->state);
s->args.results[s->state] = LPE_FAILED_UNEXPECTED_PASS;
goto done; /* always move on to the next test */
}
lwsl_info("%s: seq done test %d\n", __func__, s->state);
pass:
(*s->args.count_passes)++;
s->args.results[s->state] = LPE_SUCCEEDED;
done:
lws_seq_timeout_us(lws_seq_from_user(s), LWSSEQTO_NONE);
s->state++;
step:
if (!s->args.tests[s->state].name) {
/* the sequence has completed */
lwsl_user("%s: sequence completed OK\n", __func__);
if (s->args.cb)
s->args.cb(s->args.cb_user);
return LWSSEQ_RET_DESTROY;
}
lwsl_info("%s: starting test %d\n", __func__, s->state);
if (s->state >= s->args.results_max) {
lwsl_err("%s: results array is too small\n", __func__);
return LWSSEQ_RET_DESTROY;
}
test_abs = s->original_abs;
s->uttt[0].name_index = LTMI_PEER_V_EXPECT_TEST;
s->uttt[0].u.value = (void *)&s->args.tests[s->state];
s->uttt[1].name_index = LTMI_PEER_V_EXPECT_RESULT_CB;
s->uttt[1].u.value = (void *)unit_test_result_cb;
s->uttt[2].name_index = LTMI_PEER_V_EXPECT_RESULT_CB_ARG;
s->uttt[2].u.value = (void *)s;
/* give the unit test transport the test tokens */
test_abs.at_tokens = s->uttt;
s->instance = lws_abs_bind_and_create_instance(&test_abs);
if (!s->instance) {
lwsl_notice("%s: failed to create step %d unit test\n",
__func__, s->state);
return LWSSEQ_RET_DESTROY;
}
(*s->args.count_tests)++;
break;
default:
break;
}
return LWSSEQ_RET_CONTINUE;
}
/*
* Creates an lws_sequencer to manage the test sequence
*/
int
lws_abs_unit_test_sequencer(const lws_test_sequencer_args_t *args)
{
struct lws_seq_test_sequencer *s;
struct lws_sequencer *seq;
lws_seq_info_t i;
memset(&i, 0, sizeof(i));
i.context = args->abs->vh->context;
i.user_size = sizeof(struct lws_seq_test_sequencer);
i.puser = (void **)&s;
i.cb = test_sequencer_cb;
i.name = "test-seq";
/*
* Create a sequencer in the event loop to manage the tests
*/
seq = lws_seq_create(&i);
if (!seq) {
lwsl_err("%s: unable to create sequencer\n", __func__);
return 1;
}
/*
* Take a copy of the original lws_abs_t we were passed so we can use
* it as the basis of the lws_abs_t we create the individual tests with
*/
s->original_abs = *args->abs;
s->args = *args;
s->context = args->abs->vh->context;
s->vhost = args->abs->vh;
s->unit_test_seq = seq;
*s->args.count_tests = 0;
*s->args.count_passes = 0;
return 0;
}

View file

@ -1,408 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "private-lib-abstract.h"
typedef struct lws_abstxp_raw_skt_priv {
struct lws_abs *abs;
struct lws *wsi;
lws_dll2_t same_abs_transport_list;
uint8_t established:1;
uint8_t connecting:1;
} abs_raw_skt_priv_t;
struct vhd {
lws_dll2_owner_t owner;
};
static int
heartbeat_cb(struct lws_dll2 *d, void *user)
{
abs_raw_skt_priv_t *priv = lws_container_of(d, abs_raw_skt_priv_t,
same_abs_transport_list);
if (priv->abs->ap->heartbeat)
priv->abs->ap->heartbeat(priv->abs->api);
return 0;
}
static int
callback_abs_client_raw_skt(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)user;
struct vhd *vhd = (struct vhd *)
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
lws_get_protocol(wsi));
switch (reason) {
case LWS_CALLBACK_PROTOCOL_INIT:
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
lws_get_protocol(wsi), sizeof(struct vhd));
if (!vhd)
return 1;
lws_timed_callback_vh_protocol(lws_get_vhost(wsi),
lws_get_protocol(wsi),
LWS_CALLBACK_USER, 1);
break;
case LWS_CALLBACK_USER:
/*
* This comes at 1Hz without a wsi context, so there is no
* valid priv. We need to track the live abstract objects that
* are using our abstract protocol, and pass the heartbeat
* through to the ones that care.
*/
if (!vhd)
break;
lws_dll2_foreach_safe(&vhd->owner, NULL, heartbeat_cb);
lws_timed_callback_vh_protocol(lws_get_vhost(wsi),
lws_get_protocol(wsi),
LWS_CALLBACK_USER, 1);
break;
case LWS_CALLBACK_RAW_CONNECTED:
lwsl_debug("LWS_CALLBACK_RAW_CONNECTED\n");
priv->connecting = 0;
priv->established = 1;
if (priv->abs->ap->accept)
priv->abs->ap->accept(priv->abs->api);
if (wsi->seq)
/*
* we are bound to a sequencer who wants to know about
* our lifecycle events
*/
lws_seq_queue_event(wsi->seq, LWSSEQ_WSI_CONNECTED,
wsi, NULL);
break;
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
lwsl_user("CONNECTION_ERROR\n");
if (in)
lwsl_user(" %s\n", (const char *)in);
if (wsi->seq)
/*
* we are bound to a sequencer who wants to know about
* our lifecycle events
*/
lws_seq_queue_event(wsi->seq, LWSSEQ_WSI_CONN_FAIL,
wsi, NULL);
goto close_path;
/* fallthru */
case LWS_CALLBACK_RAW_CLOSE:
if (!user)
break;
if (wsi->seq)
/*
* we are bound to a sequencer who wants to know about
* our lifecycle events
*/
lws_seq_queue_event(wsi->seq, LWSSEQ_WSI_CONN_CLOSE,
wsi, NULL);
close_path:
lwsl_debug("LWS_CALLBACK_RAW_CLOSE\n");
priv->established = 0;
priv->connecting = 0;
if (priv->abs && priv->abs->ap->closed)
priv->abs->ap->closed(priv->abs->api);
lws_set_wsi_user(wsi, NULL);
break;
case LWS_CALLBACK_RAW_RX:
lwsl_debug("LWS_CALLBACK_RAW_RX (%d)\n", (int)len);
return !!priv->abs->ap->rx(priv->abs->api, in, len);
case LWS_CALLBACK_RAW_WRITEABLE:
lwsl_debug("LWS_CALLBACK_RAW_WRITEABLE\n");
priv->abs->ap->writeable(priv->abs->api,
lws_get_peer_write_allowance(priv->wsi));
break;
case LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL:
lws_dll2_add_tail(&priv->same_abs_transport_list, &vhd->owner);
break;
case LWS_CALLBACK_RAW_SKT_DROP_PROTOCOL:
lws_dll2_remove(&priv->same_abs_transport_list);
break;
default:
break;
}
return 0;
}
static int
lws_atcrs_close(lws_abs_transport_inst_t *ati)
{
abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)ati;
struct lws *wsi = priv->wsi;
if (!priv->wsi)
return 0;
if (!lws_raw_transaction_completed(priv->wsi))
return 0;
priv->wsi = NULL;
lws_set_timeout(wsi, 1, LWS_TO_KILL_SYNC);
/* priv is destroyed in the CLOSE callback */
return 0;
}
const struct lws_protocols protocol_abs_client_raw_skt = {
"lws-abs-cli-raw-skt", callback_abs_client_raw_skt,
0, 1024, 1024, NULL, 0
};
static int
lws_atcrs_tx(lws_abs_transport_inst_t *ati, uint8_t *buf, size_t len)
{
abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)ati;
if (!priv->wsi) {
lwsl_err("%s: NULL priv->wsi\n", __func__);
return 1;
}
lwsl_debug("%s: priv %p, wsi %p, ro %p\n", __func__,
priv, priv->wsi, priv->wsi->role_ops);
if (lws_write(priv->wsi, buf, len, LWS_WRITE_RAW) < 0)
lws_atcrs_close(ati);
return 0;
}
#if defined(LWS_WITH_CLIENT)
static int
lws_atcrs_client_conn(const lws_abs_t *abs)
{
abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)abs->ati;
struct lws_client_connect_info i;
const lws_token_map_t *tm;
if (priv->connecting)
return 0;
if (priv->established) {
lws_set_timeout(priv->wsi, PENDING_TIMEOUT_CLIENT_CONN_IDLE, 5);
return 0;
}
memset(&i, 0, sizeof(i));
/* address and port are passed-in using the abstract transport tokens */
tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_V_DNS_ADDRESS);
if (!tm) {
lwsl_notice("%s: raw_skt needs LTMI_PEER_V_DNS_ADDRESS\n",
__func__);
return 1;
}
i.address = tm->u.value;
tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_LV_PORT);
if (!tm) {
lwsl_notice("%s: raw_skt needs LTMI_PEER_LV_PORT\n", __func__);
return 1;
}
i.port = tm->u.lvalue;
/* optional */
i.ssl_connection = 0;
tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_LV_TLS_FLAGS);
if (tm)
i.ssl_connection = tm->u.lvalue;
lwsl_debug("%s: raw_skt priv %p connecting to %s:%u %p\n",
__func__, priv, i.address, i.port, abs->vh->context);
i.path = "";
i.method = "RAW";
i.vhost = abs->vh;
i.userdata = priv;
i.host = i.address;
i.pwsi = &priv->wsi;
i.origin = i.address;
i.context = abs->vh->context;
i.local_protocol_name = "lws-abs-cli-raw-skt";
i.seq = abs->seq;
i.opaque_user_data = abs->opaque_user_data;
/*
* the protocol itself has some natural attributes we should pass on
*/
if (abs->ap->flags & LWS_AP_FLAG_PIPELINE_TRANSACTIONS)
i.ssl_connection |= LCCSCF_PIPELINE;
if (abs->ap->flags & LWS_AP_FLAG_MUXABLE_STREAM)
i.ssl_connection |= LCCSCF_MUXABLE_STREAM;
priv->wsi = lws_client_connect_via_info(&i);
if (!priv->wsi)
return 1;
priv->connecting = 1;
return 0;
}
#endif
static int
lws_atcrs_ask_for_writeable(lws_abs_transport_inst_t *ati)
{
abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)ati;
if (!priv->wsi || !priv->established)
return 1;
lws_callback_on_writable(priv->wsi);
return 0;
}
static int
lws_atcrs_create(struct lws_abs *ai)
{
abs_raw_skt_priv_t *at = (abs_raw_skt_priv_t *)ai->ati;
memset(at, 0, sizeof(*at));
at->abs = ai;
return 0;
}
static void
lws_atcrs_destroy(lws_abs_transport_inst_t **pati)
{
/*
* For ourselves, we don't free anything because the abstract layer
* combined our allocation with that of the abs instance, and it will
* free the whole thing after this.
*/
*pati = NULL;
}
static int
lws_atcrs_set_timeout(lws_abs_transport_inst_t *ati, int reason, int secs)
{
abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)ati;
lws_set_timeout(priv->wsi, reason, secs);
return 0;
}
static int
lws_atcrs_state(lws_abs_transport_inst_t *ati)
{
abs_raw_skt_priv_t *priv = (abs_raw_skt_priv_t *)ati;
if (!priv || !priv->wsi || (!priv->established && !priv->connecting))
return 0;
return 1;
}
static int
lws_atcrs_compare(lws_abs_t *abs1, lws_abs_t *abs2)
{
const lws_token_map_t *tm1, *tm2;
tm1 = lws_abs_get_token(abs1->at_tokens, LTMI_PEER_V_DNS_ADDRESS);
tm2 = lws_abs_get_token(abs2->at_tokens, LTMI_PEER_V_DNS_ADDRESS);
/* Address token is mandatory and must match */
if (!tm1 || !tm2 || strcmp(tm1->u.value, tm2->u.value))
return 1;
/* Port token is mandatory and must match */
tm1 = lws_abs_get_token(abs1->at_tokens, LTMI_PEER_LV_PORT);
tm2 = lws_abs_get_token(abs2->at_tokens, LTMI_PEER_LV_PORT);
if (!tm1 || !tm2 || tm1->u.lvalue != tm2->u.lvalue)
return 1;
/* TLS is optional... */
tm1 = lws_abs_get_token(abs1->at_tokens, LTMI_PEER_LV_TLS_FLAGS);
tm2 = lws_abs_get_token(abs2->at_tokens, LTMI_PEER_LV_TLS_FLAGS);
/* ... but both must have the same situation with it given or not... */
if (!!tm1 != !!tm2)
return 1;
/* if not using TLS, then that's enough to call it */
if (!tm1)
return 0;
/* ...and if there are tls flags, both must have the same tls flags */
if (tm1->u.lvalue != tm2->u.lvalue)
return 1;
/* ... and both must use the same client tls ctx / vhost */
return abs1->vh != abs2->vh;
}
const lws_abs_transport_t lws_abs_transport_cli_raw_skt = {
.name = "raw_skt",
.alloc = sizeof(abs_raw_skt_priv_t),
.create = lws_atcrs_create,
.destroy = lws_atcrs_destroy,
.compare = lws_atcrs_compare,
.tx = lws_atcrs_tx,
#if !defined(LWS_WITH_CLIENT)
.client_conn = NULL,
#else
.client_conn = lws_atcrs_client_conn,
#endif
.close = lws_atcrs_close,
.ask_for_writeable = lws_atcrs_ask_for_writeable,
.set_timeout = lws_atcrs_set_timeout,
.state = lws_atcrs_state,
};

View file

@ -1,540 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* An abstract transport that is useful for unit testing an abstract protocol.
* It doesn't actually connect to anything, but checks the protocol's response
* to provided canned packets from an array of test vectors.
*/
#include "private-lib-core.h"
#include "private-lib-abstract.h"
/* this is the transport priv instantiated at abs->ati */
typedef struct lws_abstxp_unit_test_priv {
char note[128];
struct lws_abs *abs;
struct lws_sequencer *seq;
lws_unit_test_t *current_test;
lws_unit_test_packet_t *expect;
lws_unit_test_packet_test_cb result_cb;
const void *result_cb_arg;
lws_unit_test_packet_disposition disposition;
/* synthesized protocol timeout */
time_t timeout;
uint8_t established:1;
uint8_t connecting:1;
} abs_unit_test_priv_t;
typedef struct seq_priv {
lws_abs_t *ai;
} seq_priv_t;
enum {
UTSEQ_MSG_WRITEABLE = LWSSEQ_USER_BASE,
UTSEQ_MSG_CLOSING,
UTSEQ_MSG_TIMEOUT,
UTSEQ_MSG_CONNECTING,
UTSEQ_MSG_POST_TX_KICK,
UTSEQ_MSG_DISPOSITION_KNOWN
};
/*
* A definitive result has appeared for the current test
*/
static lws_unit_test_packet_disposition
lws_unit_test_packet_dispose(abs_unit_test_priv_t *priv,
lws_unit_test_packet_disposition disp,
const char *note)
{
assert(priv->disposition == LPE_CONTINUE);
lwsl_info("%s: %d\n", __func__, disp);
if (note)
lws_strncpy(priv->note, note, sizeof(priv->note));
priv->disposition = disp;
lws_seq_queue_event(priv->seq, UTSEQ_MSG_DISPOSITION_KNOWN,
NULL, NULL);
return disp;
}
/*
* start on the next step of the test
*/
lws_unit_test_packet_disposition
process_expect(abs_unit_test_priv_t *priv)
{
assert(priv->disposition == LPE_CONTINUE);
while (priv->expect->flags & LWS_AUT_EXPECT_RX &&
priv->disposition == LPE_CONTINUE) {
int f = priv->expect->flags & LWS_AUT_EXPECT_LOCAL_CLOSE, s;
if (priv->expect->pre)
priv->expect->pre(priv->abs);
lwsl_info("%s: rx()\n", __func__);
lwsl_hexdump_debug(priv->expect->buffer, priv->expect->len);
s = priv->abs->ap->rx(priv->abs->api, priv->expect->buffer,
priv->expect->len);
if (!!f != !!s) {
lwsl_notice("%s: expected rx return %d, got %d\n",
__func__, !!f, s);
return lws_unit_test_packet_dispose(priv, LPE_FAILED,
"rx unexpected return");
}
if (priv->expect->flags & LWS_AUT_EXPECT_TEST_END) {
lws_unit_test_packet_dispose(priv, LPE_SUCCEEDED, NULL);
break;
}
priv->expect++;
}
return LPE_CONTINUE;
}
static lws_seq_cb_return_t
unit_test_sequencer_cb(struct lws_sequencer *seq, void *user, int event,
void *data, void *aux)
{
seq_priv_t *s = (seq_priv_t *)user;
abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)s->ai->ati;
time_t now;
switch ((int)event) {
case LWSSEQ_CREATED: /* our sequencer just got started */
lwsl_notice("%s: %s: created\n", __func__,
lws_seq_name(seq));
if (s->ai->at->client_conn(s->ai)) {
lwsl_notice("%s: %s: abstract client conn failed\n",
__func__, lws_seq_name(seq));
return LWSSEQ_RET_DESTROY;
}
break;
case LWSSEQ_DESTROYED:
/*
* This sequencer is about to be destroyed. If we have any
* other assets in play, detach them from us.
*/
if (priv->abs)
lws_abs_destroy_instance(&priv->abs);
break;
case LWSSEQ_HEARTBEAT:
/* synthesize a wsi-style timeout */
if (!priv->timeout)
goto ph;
time(&now);
if (now <= priv->timeout)
goto ph;
if (priv->expect->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT) {
lwsl_user("%s: test got expected timeout\n",
__func__);
lws_unit_test_packet_dispose(priv,
LPE_FAILED_UNEXPECTED_TIMEOUT, NULL);
return LWSSEQ_RET_DESTROY;
}
lwsl_user("%s: seq timed out\n", __func__);
ph:
if (priv->abs->ap->heartbeat)
priv->abs->ap->heartbeat(priv->abs->api);
break;
case UTSEQ_MSG_DISPOSITION_KNOWN:
lwsl_info("%s: %s: DISPOSITION_KNOWN %s: %s\n", __func__,
priv->abs->ap->name,
priv->current_test->name,
priv->disposition == LPE_SUCCEEDED ? "OK" : "FAIL");
/*
* if the test has a callback, call it back to let it
* know the result
*/
if (priv->result_cb)
priv->result_cb(priv->result_cb_arg, priv->disposition);
return LWSSEQ_RET_DESTROY;
case UTSEQ_MSG_CONNECTING:
lwsl_debug("UTSEQ_MSG_CONNECTING\n");
if (priv->abs->ap->accept)
priv->abs->ap->accept(priv->abs->api);
priv->established = 1;
/* fallthru */
case UTSEQ_MSG_POST_TX_KICK:
if (priv->disposition)
break;
if (process_expect(priv) != LPE_CONTINUE) {
lwsl_notice("%s: UTSEQ_MSG_POST_TX_KICK failed\n",
__func__);
return LWSSEQ_RET_DESTROY;
}
break;
case UTSEQ_MSG_WRITEABLE:
/*
* inform the protocol our transport is writeable now
*/
priv->abs->ap->writeable(priv->abs->api, 1024);
break;
case UTSEQ_MSG_CLOSING:
if (!(priv->expect->flags & LWS_AUT_EXPECT_LOCAL_CLOSE)) {
lwsl_user("%s: got unexpected close\n", __func__);
lws_unit_test_packet_dispose(priv,
LPE_FAILED_UNEXPECTED_CLOSE, NULL);
goto done;
}
/* tell the abstract protocol we are closing on them */
if (priv->abs && priv->abs->ap->closed)
priv->abs->ap->closed(priv->abs->api);
goto done;
case UTSEQ_MSG_TIMEOUT: /* current step timed out */
s->ai->at->close(s->ai->ati);
if (!(priv->expect->flags & LWS_AUT_EXPECT_SHOULD_TIMEOUT)) {
lwsl_user("%s: got unexpected timeout\n", __func__);
lws_unit_test_packet_dispose(priv,
LPE_FAILED_UNEXPECTED_TIMEOUT, NULL);
return LWSSEQ_RET_DESTROY;
}
goto done;
done:
lws_seq_timeout_us(lws_seq_from_user(s),
LWSSEQTO_NONE);
priv->expect++;
if (!priv->expect->buffer) {
/* the sequence has completed */
lwsl_user("%s: sequence completed OK\n", __func__);
return LWSSEQ_RET_DESTROY;
}
break;
default:
break;
}
return LWSSEQ_RET_CONTINUE;
}
static int
lws_atcut_close(lws_abs_transport_inst_t *ati)
{
abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
lwsl_notice("%s\n", __func__);
lws_seq_queue_event(priv->seq, UTSEQ_MSG_CLOSING, NULL, NULL);
return 0;
}
static int
lws_atcut_tx(lws_abs_transport_inst_t *ati, uint8_t *buf, size_t len)
{
abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
assert(priv->disposition == LPE_CONTINUE);
lwsl_info("%s: received tx\n", __func__);
if (priv->expect->pre)
priv->expect->pre(priv->abs);
if (!(priv->expect->flags & LWS_AUT_EXPECT_TX)) {
lwsl_notice("%s: unexpected tx\n", __func__);
lwsl_hexdump_notice(buf, len);
lws_unit_test_packet_dispose(priv, LPE_FAILED, "unexpected tx");
return 1;
}
if (len != priv->expect->len) {
lwsl_notice("%s: unexpected tx len %zu, expected %zu\n",
__func__, len, priv->expect->len);
lws_unit_test_packet_dispose(priv, LPE_FAILED,
"tx len mismatch");
return 1;
}
if (memcmp(buf, priv->expect->buffer, len)) {
lwsl_notice("%s: tx mismatch (exp / actual)\n", __func__);
lwsl_hexdump_debug(priv->expect->buffer, len);
lwsl_hexdump_debug(buf, len);
lws_unit_test_packet_dispose(priv, LPE_FAILED,
"tx data mismatch");
return 1;
}
if (priv->expect->flags & LWS_AUT_EXPECT_TEST_END) {
lws_unit_test_packet_dispose(priv, LPE_SUCCEEDED, NULL);
return 1;
}
priv->expect++;
lws_seq_queue_event(priv->seq, UTSEQ_MSG_POST_TX_KICK, NULL, NULL);
return 0;
}
#if defined(LWS_WITH_CLIENT)
static int
lws_atcut_client_conn(const lws_abs_t *abs)
{
abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)abs->ati;
const lws_token_map_t *tm;
if (priv->established) {
lwsl_err("%s: already established\n", __func__);
return 1;
}
/* set up the test start pieces... the array of test expects... */
tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_V_EXPECT_TEST);
if (!tm) {
lwsl_notice("%s: unit_test needs LTMI_PEER_V_EXPECT_TEST\n",
__func__);
return 1;
}
priv->current_test = (lws_unit_test_t *)tm->u.value;
/* ... and the callback to deliver the result to */
tm = lws_abs_get_token(abs->at_tokens, LTMI_PEER_V_EXPECT_RESULT_CB);
if (tm)
priv->result_cb = (lws_unit_test_packet_test_cb)tm->u.value;
else
priv->result_cb = NULL;
/* ... and the arg to deliver it with */
tm = lws_abs_get_token(abs->at_tokens,
LTMI_PEER_V_EXPECT_RESULT_CB_ARG);
if (tm)
priv->result_cb_arg = tm->u.value;
priv->expect = priv->current_test->expect_array;
priv->disposition = LPE_CONTINUE;
priv->note[0] = '\0';
lws_seq_timeout_us(priv->seq, priv->current_test->max_secs *
LWS_US_PER_SEC);
lwsl_notice("%s: %s: test '%s': start\n", __func__, abs->ap->name,
priv->current_test->name);
lws_seq_queue_event(priv->seq, UTSEQ_MSG_CONNECTING, NULL, NULL);
return 0;
}
#endif
static int
lws_atcut_ask_for_writeable(lws_abs_transport_inst_t *ati)
{
abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
if (!priv->established)
return 1;
/*
* Queue a writeable event... this won't be handled by teh sequencer
* until we have returned to the event loop, just like a real
* callback_on_writable()
*/
lws_seq_queue_event(priv->seq, UTSEQ_MSG_WRITEABLE, NULL, NULL);
return 0;
}
/*
* An abstract protocol + transport has been instantiated
*/
static int
lws_atcut_create(lws_abs_t *ai)
{
abs_unit_test_priv_t *priv;
struct lws_sequencer *seq;
lws_seq_info_t i;
seq_priv_t *s;
memset(&i, 0, sizeof(i));
i.context = ai->vh->context;
i.user_size = sizeof(*s);
i.puser = (void **)&s;
i.cb = unit_test_sequencer_cb;
i.name = "unit-test-seq";
/*
* Create the sequencer for the steps in a single unit test
*/
seq = lws_seq_create(&i);
if (!seq) {
lwsl_err("%s: unable to create sequencer\n", __func__);
return 1;
}
priv = ai->ati;
memset(s, 0, sizeof(*s));
memset(priv, 0, sizeof(*priv));
/* the sequencer priv just points to the lws_abs_t */
s->ai = ai;
priv->abs = ai;
priv->seq = seq;
return 0;
}
static void
lws_atcut_destroy(lws_abs_transport_inst_t **pati)
{
/*
* We don't free anything because the abstract layer combined our
* allocation with that of the instance, and it will free the whole
* thing after this.
*/
*pati = NULL;
}
static int
lws_atcut_set_timeout(lws_abs_transport_inst_t *ati, int reason, int secs)
{
abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
time_t now;
time(&now);
if (secs)
priv->timeout = now + secs;
else
priv->timeout = 0;
return 0;
}
static int
lws_atcut_state(lws_abs_transport_inst_t *ati)
{
abs_unit_test_priv_t *priv = (abs_unit_test_priv_t *)ati;
if (!priv || (!priv->established && !priv->connecting))
return 0;
return 1;
}
static const char *dnames[] = {
"INCOMPLETE",
"PASS",
"FAIL",
"FAIL(TIMEOUT)",
"FAIL(UNEXPECTED PASS)",
"FAIL(UNEXPECTED CLOSE)",
"SKIPPED"
"?",
"?"
};
const char *
lws_unit_test_result_name(int in)
{
if (in < 0 || in > (int)LWS_ARRAY_SIZE(dnames))
return "unknown";
return dnames[in];
}
static int
lws_atcut_compare(lws_abs_t *abs1, lws_abs_t *abs2)
{
return 0;
}
const lws_abs_transport_t lws_abs_transport_cli_unit_test = {
.name = "unit_test",
.alloc = sizeof(abs_unit_test_priv_t),
.create = lws_atcut_create,
.destroy = lws_atcut_destroy,
.compare = lws_atcut_compare,
.tx = lws_atcut_tx,
#if !defined(LWS_WITH_CLIENT)
.client_conn = NULL,
#else
.client_conn = lws_atcut_client_conn,
#endif
.close = lws_atcut_close,
.ask_for_writeable = lws_atcut_ask_for_writeable,
.set_timeout = lws_atcut_set_timeout,
.state = lws_atcut_state,
};

View file

@ -56,11 +56,6 @@ if (LWS_WITH_LWS_DSH)
core-net/lws-dsh.c)
endif()
if (LWS_WITH_SEQUENCER)
list(APPEND SOURCES
core-net/sequencer.c)
endif()
if (LWS_WITH_CLIENT)
list(APPEND SOURCES
core-net/client/client.c

View file

@ -234,7 +234,6 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i)
else
wsi->keep_warm_secs = 5;
wsi->seq = i->seq;
wsi->flags = i->ssl_connection;
wsi->c_pri = i->priority;

View file

@ -284,7 +284,6 @@ struct lws_context_per_thread {
pthread_t self;
#endif
struct lws_dll2_owner dll_buflist_owner; /* guys with pending rxflow */
struct lws_dll2_owner seq_owner; /* list of lws_sequencer-s */
lws_dll2_owner_t attach_owner; /* pending lws_attach */
#if defined(LWS_WITH_SECURE_STREAMS)
@ -298,9 +297,6 @@ struct lws_context_per_thread {
struct lws_dll2_owner pt_sul_owner[LWS_COUNT_PT_SUL_OWNERS];
#if defined (LWS_WITH_SEQUENCER)
lws_sorted_usec_list_t sul_seq_heartbeat;
#endif
#if (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)) && defined(LWS_WITH_SERVER)
lws_sorted_usec_list_t sul_ah_lifecheck;
#endif
@ -701,7 +697,6 @@ struct lws {
struct lws *child_list; /* points to first child */
struct lws *sibling_list; /* subsequent children at same level */
const struct lws_role_ops *role_ops;
struct lws_sequencer *seq; /* associated sequencer if any */
const lws_retry_bo_t *retry_policy;
lws_log_cx_t *log_cx;

View file

@ -1,335 +0,0 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
/*
* per pending event
*/
typedef struct lws_seq_event {
struct lws_dll2 seq_event_list;
void *data;
void *aux;
lws_seq_events_t e;
} lws_seq_event_t;
/*
* per sequencer
*/
typedef struct lws_sequencer {
struct lws_dll2 seq_list;
lws_sorted_usec_list_t sul_timeout;
lws_sorted_usec_list_t sul_pending;
struct lws_dll2_owner seq_event_owner;
struct lws_context_per_thread *pt;
lws_seq_event_cb cb;
const char *name;
const lws_retry_bo_t *retry;
lws_usec_t time_created;
lws_usec_t timeout; /* 0 or time we timeout */
uint8_t going_down:1;
uint8_t wakesuspend:1;
} lws_seq_t;
#define QUEUE_SANITY_LIMIT 10
static void
lws_sul_seq_heartbeat_cb(lws_sorted_usec_list_t *sul)
{
struct lws_context_per_thread *pt = lws_container_of(sul,
struct lws_context_per_thread, sul_seq_heartbeat);
/* send every sequencer a heartbeat message... it can ignore it */
lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
lws_dll2_get_head(&pt->seq_owner)) {
lws_seq_t *s = lws_container_of(p, lws_seq_t, seq_list);
/* queue the message to inform the sequencer */
lws_seq_queue_event(s, LWSSEQ_HEARTBEAT, NULL, NULL);
} lws_end_foreach_dll_safe(p, tp);
/* schedule the next one */
__lws_sul_insert_us(&pt->pt_sul_owner[LWSSULLI_MISS_IF_SUSPENDED],
&pt->sul_seq_heartbeat, LWS_US_PER_SEC);
}
int
lws_seq_pt_init(struct lws_context_per_thread *pt)
{
pt->sul_seq_heartbeat.cb = lws_sul_seq_heartbeat_cb;
/* schedule the first heartbeat */
__lws_sul_insert_us(&pt->pt_sul_owner[LWSSULLI_MISS_IF_SUSPENDED],
&pt->sul_seq_heartbeat, LWS_US_PER_SEC);
return 0;
}
lws_seq_t *
lws_seq_create(lws_seq_info_t *i)
{
struct lws_context_per_thread *pt = &i->context->pt[i->tsi];
lws_seq_t *seq = lws_zalloc(sizeof(*seq) + i->user_size, __func__);
if (!seq)
return NULL;
seq->cb = i->cb;
seq->pt = pt;
seq->name = i->name;
seq->retry = i->retry;
seq->wakesuspend = i->wakesuspend;
*i->puser = (void *)&seq[1];
/* add the sequencer to the pt */
lws_pt_lock(pt, __func__); /* ---------------------------------- pt { */
lws_dll2_add_tail(&seq->seq_list, &pt->seq_owner);
lws_pt_unlock(pt); /* } pt ------------------------------------------ */
seq->time_created = lws_now_usecs();
/* try to queue the creation cb */
if (lws_seq_queue_event(seq, LWSSEQ_CREATED, NULL, NULL)) {
lws_dll2_remove(&seq->seq_list);
lws_free(seq);
return NULL;
}
return seq;
}
static int
seq_ev_destroy(struct lws_dll2 *d, void *user)
{
lws_seq_event_t *seqe = lws_container_of(d, lws_seq_event_t,
seq_event_list);
lws_dll2_remove(&seqe->seq_event_list);
lws_free(seqe);
return 0;
}
void
lws_seq_destroy(lws_seq_t **pseq)
{
lws_seq_t *seq = *pseq;
/* defeat another thread racing to add events while we are destroying */
seq->going_down = 1;
seq->cb(seq, (void *)&seq[1], LWSSEQ_DESTROYED, NULL, NULL);
lws_pt_lock(seq->pt, __func__); /* -------------------------- pt { */
lws_dll2_remove(&seq->seq_list);
lws_dll2_remove(&seq->sul_timeout.list);
lws_dll2_remove(&seq->sul_pending.list);
/* remove and destroy any pending events */
lws_dll2_foreach_safe(&seq->seq_event_owner, NULL, seq_ev_destroy);
lws_pt_unlock(seq->pt); /* } pt ---------------------------------- */
lws_free_set_NULL(seq);
}
void
lws_seq_destroy_all_on_pt(struct lws_context_per_thread *pt)
{
lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp,
pt->seq_owner.head) {
lws_seq_t *s = lws_container_of(p, lws_seq_t,
seq_list);
lws_seq_destroy(&s);
} lws_end_foreach_dll_safe(p, tp);
}
static void
lws_seq_sul_pending_cb(lws_sorted_usec_list_t *sul)
{
lws_seq_t *seq = lws_container_of(sul, lws_seq_t, sul_pending);
lws_seq_event_t *seqe;
struct lws_dll2 *dh;
int n;
if (!seq->seq_event_owner.count)
return;
/* events are only added at tail, so no race possible yet... */
dh = lws_dll2_get_head(&seq->seq_event_owner);
seqe = lws_container_of(dh, lws_seq_event_t, seq_event_list);
n = (int)seq->cb(seq, (void *)&seq[1], (int)seqe->e, seqe->data, seqe->aux);
/* ... have to lock here though, because we will change the list */
lws_pt_lock(seq->pt, __func__); /* ----------------------------- pt { */
/* detach event from sequencer event list and free it */
lws_dll2_remove(&seqe->seq_event_list);
lws_free(seqe);
lws_pt_unlock(seq->pt); /* } pt ------------------------------------- */
if (n) {
lwsl_info("%s: destroying seq '%s' by request\n", __func__,
seq->name);
lws_seq_destroy(&seq);
}
}
int
lws_seq_queue_event(lws_seq_t *seq, lws_seq_events_t e, void *data, void *aux)
{
lws_seq_event_t *seqe;
if (!seq || seq->going_down)
return 1;
seqe = lws_zalloc(sizeof(*seqe), __func__);
if (!seqe)
return 1;
seqe->e = e;
seqe->data = data;
seqe->aux = aux;
// lwsl_notice("%s: seq %s: event %d\n", __func__, seq->name, e);
lws_pt_lock(seq->pt, __func__); /* ----------------------------- pt { */
if (seq->seq_event_owner.count > QUEUE_SANITY_LIMIT) {
lwsl_err("%s: more than %d events queued\n", __func__,
QUEUE_SANITY_LIMIT);
}
lws_dll2_add_tail(&seqe->seq_event_list, &seq->seq_event_owner);
seq->sul_pending.cb = lws_seq_sul_pending_cb;
__lws_sul_insert_us(&seq->pt->pt_sul_owner[seq->wakesuspend],
&seq->sul_pending, 1);
lws_pt_unlock(seq->pt); /* } pt ------------------------------------- */
return 0;
}
/*
* Check if wsi still extant, by peeking in the message queue for a
* LWSSEQ_WSI_CONN_CLOSE message about wsi. (Doesn't need to do the same for
* CONN_FAIL since that will never have produced any messages prior to that).
*
* Use this to avoid trying to perform operations on wsi that have already
* closed but we didn't get to that message yet.
*
* Returns 0 if not closed yet or 1 if it has closed but we didn't process the
* close message yet.
*/
int
lws_seq_check_wsi(lws_seq_t *seq, struct lws *wsi)
{
lws_seq_event_t *seqe;
struct lws_dll2 *dh;
lws_pt_lock(seq->pt, __func__); /* ----------------------------- pt { */
dh = lws_dll2_get_head(&seq->seq_event_owner);
while (dh) {
seqe = lws_container_of(dh, lws_seq_event_t, seq_event_list);
if (seqe->e == LWSSEQ_WSI_CONN_CLOSE && seqe->data == wsi)
break;
dh = dh->next;
}
lws_pt_unlock(seq->pt); /* } pt ------------------------------------- */
return !!dh;
}
static void
lws_seq_sul_timeout_cb(lws_sorted_usec_list_t *sul)
{
lws_seq_t *s = lws_container_of(sul, lws_seq_t, sul_timeout);
lws_seq_queue_event(s, LWSSEQ_TIMED_OUT, NULL, NULL);
}
/* set us to LWS_SET_TIMER_USEC_CANCEL to remove timeout */
int
lws_seq_timeout_us(lws_seq_t *seq, lws_usec_t us)
{
seq->sul_timeout.cb = lws_seq_sul_timeout_cb;
/* list is always at the very top of the sul */
__lws_sul_insert_us(&seq->pt->pt_sul_owner[seq->wakesuspend],
(lws_sorted_usec_list_t *)&seq->sul_timeout.list, us);
return 0;
}
lws_seq_t *
lws_seq_from_user(void *u)
{
return &((lws_seq_t *)u)[-1];
}
const char *
lws_seq_name(lws_seq_t *seq)
{
return seq->name;
}
lws_usec_t
lws_seq_us_since_creation(lws_seq_t *seq)
{
return lws_now_usecs() - seq->time_created;
}
struct lws_context *
lws_seq_get_context(lws_seq_t *seq)
{
return seq->pt->context;
}

View file

@ -1140,9 +1140,6 @@ lws_create_context(const struct lws_context_creation_info *info)
context->pt[n].http.ah_pool_length = 0;
#endif
lws_pt_mutex_init(&context->pt[n]);
#if defined(LWS_WITH_SEQUENCER)
lws_seq_pt_init(&context->pt[n]);
#endif
#if defined(LWS_WITH_CGI)
if (lws_rops_fidx(&role_ops_cgi, LWS_ROPS_pt_init_destroy))
@ -1712,11 +1709,6 @@ lws_pt_destroy(struct lws_context_per_thread *pt)
lws_dll2_foreach_safe(&pt->ss_client_owner, NULL, lws_sspc_destroy_dll);
#endif
#if defined(LWS_WITH_SEQUENCER)
lws_seq_destroy_all_on_pt(pt);
#endif
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
while (pt->http.ah_list)
_lws_destroy_ah(pt, pt->http.ah_list);
@ -2048,9 +2040,7 @@ next:
struct lws_context_per_thread *pt = &context->pt[n];
(void)pt;
#if defined(LWS_WITH_SEQUENCER)
lws_seq_destroy_all_on_pt(pt);
#endif
LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) {
if (lws_rops_fidx(ar, LWS_ROPS_pt_init_destroy))
(lws_rops_func_fidx(ar, LWS_ROPS_pt_init_destroy)).

View file

@ -76,7 +76,6 @@ typedef struct lws_ss_handle {
struct lws_context *context; /**< lws context we are created on */
const lws_ss_policy_t *policy; /**< system policy for stream */
struct lws_sequencer *seq; /**< owning sequencer if any */
struct lws *wsi; /**< the stream wsi if any */
struct lws_sss_proxy_conn *conn_if_sspc_onw;

View file

@ -417,16 +417,6 @@ lws_ss_event_helper(lws_ss_handle_t *h, lws_ss_constate_t cs)
if (cs == LWSSSCS_DISCONNECTED)
h->ss_dangling_connected = 0;
#if defined(LWS_WITH_SEQUENCER)
/*
* A parent sequencer for the ss is optional, if we have one, keep it
* informed of state changes on the ss connection
*/
if (h->seq && cs != LWSSSCS_DESTROYING)
lws_seq_queue_event(h->seq, LWSSEQ_SS_STATE_BASE + cs,
(void *)h, NULL);
#endif
if (h->info.state) {
h->h_in_svc = h;
r = h->info.state(ss_to_userobj(h), NULL, cs,
@ -820,7 +810,6 @@ _lws_ss_client_connect(lws_ss_handle_t *h, int is_retry, void *conn_if_sspc_onw)
i.host = i.address;
i.origin = i.address;
i.opaque_user_data = h;
i.seq = h->seq;
i.retry_and_idle_policy = h->policy->retry_bo;
i.sys_tls_client_cert = h->policy->client_cert;
@ -938,7 +927,7 @@ lws_ss_client_connect(lws_ss_handle_t *h)
int
lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
void *opaque_user_data, lws_ss_handle_t **ppss,
struct lws_sequencer *seq_owner, const char **ppayload_fmt)
void *reserved, const char **ppayload_fmt)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
const lws_ss_policy_t *pol;
@ -1068,7 +1057,6 @@ lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
h->policy = pol;
h->context = context;
h->tsi = (uint8_t)tsi;
h->seq = seq_owner;
if (h->info.flags & LWSSSINFLAGS_PROXIED)
h->proxy_onward = 1;
@ -1678,12 +1666,6 @@ lws_ss_cancel_notify_dll(struct lws_dll2 *d, void *user)
return 0;
}
struct lws_sequencer *
lws_ss_get_sequencer(lws_ss_handle_t *h)
{
return h->seq;
}
struct lws_context *
lws_ss_get_context(struct lws_ss_handle *h)
{

View file

@ -282,7 +282,7 @@ lws_sspc_event_helper(lws_sspc_handle_t *h, lws_ss_constate_t cs,
int
lws_sspc_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi,
void *opaque_user_data, lws_sspc_handle_t **ppss,
struct lws_sequencer *seq_owner, const char **ppayload_fmt)
void *reserved, const char **ppayload_fmt)
{
lws_sspc_handle_t *h;
uint8_t *ua;