Compare commits
5
Commits
5905214f66
...
839e014323
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
839e014323 | ||
|
|
1674131b85 | ||
|
|
060cf0447f | ||
|
|
6f46f8e388 | ||
|
|
11d3cb1d08 |
+1
-1
@@ -196,7 +196,7 @@ HIO_DEBUG1 (hio, "DNC - got dns response over tcp - msgid:%d\n", id);
|
||||
|
||||
if (dev == (hio_dev_sck_t*)reqmsgxtn->dev && pkt->id == reqpkt->id)
|
||||
{
|
||||
if (HIO_LIKELY(reqmsgxtn->on_done)) reqmsgxtn->on_done (dnc, reqmsg, HIO_ENOERR, pkt, pktlen);
|
||||
if (HIO_LIKELY(reqmsgxtn->on_done)) reqmsgxtn->on_done(dnc, reqmsg, HIO_ENOERR, pkt, pktlen);
|
||||
release_dns_msg (dnc, reqmsg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+12
-12
@@ -165,13 +165,13 @@ static int sck_on_read (hio_dev_sck_t* sck, const void* data, hio_iolen_t dlen,
|
||||
{
|
||||
/* error or timeout */
|
||||
/* fire all related fcgi sessions?? -> handled on disconnect */
|
||||
hio_dev_sck_halt (sck);
|
||||
hio_dev_sck_halt(sck);
|
||||
}
|
||||
else if (dlen == 0)
|
||||
{
|
||||
/* EOF */
|
||||
/* fire all related fcgi sessions?? -> handled on disconnect?? */
|
||||
hio_dev_sck_halt (sck);
|
||||
hio_dev_sck_halt(sck);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -218,7 +218,7 @@ static int sck_on_read (hio_dev_sck_t* sck, const void* data, hio_iolen_t dlen,
|
||||
{
|
||||
/* invalid content_len encountered */
|
||||
/* TODO: logging*/
|
||||
hio_dev_sck_halt (sck);
|
||||
hio_dev_sck_halt(sck);
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ static int make_connection_socket (hio_svc_fcgic_t* fcgic, hio_svc_fcgic_conn_t*
|
||||
{
|
||||
/* immediate failure */
|
||||
sck_xtn->conn = HIO_NULL; /* disassociate the socket from the fcgi connection object */
|
||||
hio_dev_sck_halt (sck);
|
||||
hio_dev_sck_halt(sck);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -404,11 +404,11 @@ static void free_connections (hio_svc_fcgic_t* fcgic)
|
||||
struct fcgic_sck_xtn_t* sck_xtn;
|
||||
sck_xtn = hio_dev_sck_getxtn(conn->dev);
|
||||
sck_xtn->conn = HIO_NULL;
|
||||
hio_dev_sck_halt (conn->dev);
|
||||
hio_dev_sck_halt(conn->dev);
|
||||
}
|
||||
|
||||
/* delay destruction of conn->session.ptr and conn */
|
||||
hio_addcfmb (hio, (hio_cfmb_t*)conn, HIO_NULL, destroy_connection_memory);
|
||||
hio_addcfmb(hio, (hio_cfmb_t*)conn, HIO_NULL, destroy_connection_memory);
|
||||
conn = next;
|
||||
}
|
||||
}
|
||||
@@ -473,7 +473,7 @@ static hio_svc_fcgic_sess_t* new_session (hio_svc_fcgic_t* fcgic, const hio_skad
|
||||
|
||||
static void release_session (hio_svc_fcgic_sess_t* sess)
|
||||
{
|
||||
if (sess->on_untie) sess->on_untie (sess, sess->ctx);
|
||||
if (sess->on_untie) sess->on_untie(sess, sess->ctx);
|
||||
sess->active = 0;
|
||||
sess->next = sess->conn->sess.free;
|
||||
sess->conn->sess.free = sess;
|
||||
@@ -494,8 +494,8 @@ hio_svc_fcgic_t* hio_svc_fcgic_start (hio_t* hio, const hio_svc_fcgic_tmout_t* t
|
||||
|
||||
if (tmout) fcgic->tmout = *tmout;
|
||||
|
||||
HIO_SVCL_APPEND_SVC (&hio->actsvc, (hio_svc_t*)fcgic);
|
||||
HIO_DEBUG1 (hio, "FCGIC - STARTED SERVICE %p\n", fcgic);
|
||||
HIO_SVCL_APPEND_SVC(&hio->actsvc, (hio_svc_t*)fcgic);
|
||||
HIO_DEBUG1(hio, "FCGIC - STARTED SERVICE %p\n", fcgic);
|
||||
return fcgic;
|
||||
|
||||
oops:
|
||||
@@ -507,15 +507,15 @@ void hio_svc_fcgic_stop (hio_svc_fcgic_t* fcgic)
|
||||
{
|
||||
hio_t* hio = fcgic->hio;
|
||||
|
||||
HIO_DEBUG1 (hio, "FCGIC - STOPPING SERVICE %p\n", fcgic);
|
||||
HIO_DEBUG1(hio, "FCGIC - STOPPING SERVICE %p\n", fcgic);
|
||||
fcgic->stopping = 1;
|
||||
|
||||
free_connections (fcgic);
|
||||
|
||||
HIO_SVCL_UNLINK_SVC (fcgic);
|
||||
HIO_SVCL_UNLINK_SVC(fcgic);
|
||||
hio_freemem(hio, fcgic);
|
||||
|
||||
HIO_DEBUG1 (hio, "FCGIC - STOPPED SERVICE %p\n", fcgic);
|
||||
HIO_DEBUG1(hio, "FCGIC - STOPPED SERVICE %p\n", fcgic);
|
||||
}
|
||||
|
||||
hio_svc_fcgic_sess_t* hio_svc_fcgic_tie (hio_svc_fcgic_t* fcgic, const hio_skad_t* addr, hio_svc_fcgic_on_read_t on_read, hio_svc_fcgic_on_write_t on_write, hio_svc_fcgic_on_untie_t on_untie, void* ctx)
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
static void clear_unneeded_cfmbs (hio_t* hio);
|
||||
static int schedule_kill_zombie_job (hio_dev_t* dev);
|
||||
static int kill_and_free_device (hio_dev_t* dev, int force);
|
||||
static void free_dead_devices (hio_t* hio);
|
||||
|
||||
static void on_read_timeout (hio_t* hio, const hio_ntime_t* now, hio_tmrjob_t* job);
|
||||
static void on_write_timeout (hio_t* hio, const hio_ntime_t* now, hio_tmrjob_t* job);
|
||||
@@ -56,7 +57,7 @@ static void* mmgr_realloc (hio_mmgr_t* mmgr, void* ptr, hio_oow_t size)
|
||||
|
||||
static void mmgr_free (hio_mmgr_t* mmgr, void* ptr)
|
||||
{
|
||||
return free (ptr);
|
||||
return free(ptr);
|
||||
}
|
||||
|
||||
static hio_mmgr_t default_mmgr =
|
||||
@@ -134,12 +135,13 @@ int hio_init (hio_t* hio, hio_mmgr_t* mmgr, hio_cmgr_t* cmgr, hio_bitmask_t feat
|
||||
|
||||
hio->tmr.capa = tmrcapa;
|
||||
|
||||
HIO_CFMBL_INIT (&hio->cfmb);
|
||||
HIO_DEVL_INIT (&hio->actdev);
|
||||
HIO_DEVL_INIT (&hio->hltdev);
|
||||
HIO_DEVL_INIT (&hio->zmbdev);
|
||||
HIO_CWQ_INIT (&hio->cwq);
|
||||
HIO_SVCL_INIT (&hio->actsvc);
|
||||
HIO_CFMBL_INIT(&hio->cfmb);
|
||||
HIO_DEVL_INIT(&hio->actdev);
|
||||
HIO_DEVL_INIT(&hio->hltdev);
|
||||
HIO_DEVL_INIT(&hio->zmbdev);
|
||||
HIO_DEVL_INIT(&hio->deaddev);
|
||||
HIO_CWQ_INIT(&hio->cwq);
|
||||
HIO_SVCL_INIT(&hio->actsvc);
|
||||
|
||||
hio_sys_gettime(hio, &hio->init_time);
|
||||
return 0;
|
||||
@@ -215,8 +217,11 @@ void hio_fini (hio_t* hio)
|
||||
nhltdevs++;
|
||||
}
|
||||
|
||||
/* nothing should be deferred outside of dispatch, but make sure */
|
||||
free_dead_devices (hio);
|
||||
|
||||
/* clean up all zombie devices */
|
||||
HIO_DEVL_INIT (&diehard);
|
||||
HIO_DEVL_INIT(&diehard);
|
||||
for (dev = HIO_DEVL_FIRST_DEV(&hio->zmbdev); !HIO_DEVL_IS_NIL_DEV(&hio->zmbdev, dev); )
|
||||
{
|
||||
kill_and_free_device (dev, 1);
|
||||
@@ -226,11 +231,11 @@ void hio_fini (hio_t* hio)
|
||||
next_dev = dev->dev_next;
|
||||
|
||||
/* remove the device from the zombie device list */
|
||||
HIO_DEVL_UNLINK_DEV (dev);
|
||||
HIO_DEVL_UNLINK_DEV(dev);
|
||||
dev->dev_cap &= ~HIO_DEV_CAP_ZOMBIE;
|
||||
|
||||
/* put it to a private list for aborting */
|
||||
HIO_DEVL_APPEND_DEV (&diehard, dev);
|
||||
HIO_DEVL_APPEND_DEV(&diehard, dev);
|
||||
|
||||
dev = next_dev;
|
||||
}
|
||||
@@ -248,7 +253,7 @@ void hio_fini (hio_t* hio)
|
||||
* is given to kill_and_free_device(). */
|
||||
dev = HIO_DEVL_FIRST_DEV(&diehard);
|
||||
HIO_ASSERT(hio, !(dev->dev_cap & (HIO_DEV_CAP_ACTIVE | HIO_DEV_CAP_HALTED | HIO_DEV_CAP_ZOMBIE)));
|
||||
HIO_DEVL_UNLINK_DEV (dev);
|
||||
HIO_DEVL_UNLINK_DEV(dev);
|
||||
kill_and_free_device (dev, 2);
|
||||
ndieharddevs++;
|
||||
}
|
||||
@@ -559,10 +564,51 @@ static void fire_cwq_handlers_for_dev (hio_t* hio, hio_dev_t* dev, int for_kill)
|
||||
|
||||
}
|
||||
|
||||
static HIO_INLINE void clear_read_pending (hio_dev_t* dev)
|
||||
{
|
||||
if (dev->dev_cap & HIO_DEV_CAP_IN_PENDING)
|
||||
{
|
||||
dev->dev_cap &= ~HIO_DEV_CAP_IN_PENDING;
|
||||
dev->hio->nrdpendings--;
|
||||
}
|
||||
}
|
||||
|
||||
static HIO_INLINE void update_read_pending (hio_dev_t* dev)
|
||||
{
|
||||
int pending;
|
||||
|
||||
pending = !(dev->dev_cap & (HIO_DEV_CAP_IN_DISABLED | HIO_DEV_CAP_IN_CLOSED)) &&
|
||||
dev->dev_mth->readpending && dev->dev_mth->readpending(dev);
|
||||
|
||||
if (pending)
|
||||
{
|
||||
if (!(dev->dev_cap & HIO_DEV_CAP_IN_PENDING))
|
||||
{
|
||||
dev->dev_cap |= HIO_DEV_CAP_IN_PENDING;
|
||||
dev->hio->nrdpendings++;
|
||||
}
|
||||
}
|
||||
else if (dev->dev_cap & HIO_DEV_CAP_IN_PENDING)
|
||||
{
|
||||
dev->dev_cap &= ~HIO_DEV_CAP_IN_PENDING;
|
||||
dev->hio->nrdpendings--;
|
||||
}
|
||||
}
|
||||
|
||||
static HIO_INLINE void handle_event (hio_t* hio, hio_dev_t* dev, int events, int rdhup)
|
||||
{
|
||||
HIO_ASSERT(hio, hio == dev->hio);
|
||||
|
||||
if (!(dev->dev_cap & HIO_DEV_CAP_ACTIVE))
|
||||
{
|
||||
/* an earlier event in this same batch halted or killed this device.
|
||||
* the batch was captured before any of it ran, so its entry is stale.
|
||||
* a killed device is still allocated at this point - see the deferral
|
||||
* in kill_and_free_device() - so testing the bit here is safe. */
|
||||
HIO_DEBUG1(hio, "DEV(%p) - skipping a stale event for an inactive device\n", dev);
|
||||
return;
|
||||
}
|
||||
|
||||
dev->dev_cap &= ~HIO_DEV_CAP_RENEW_REQUIRED;
|
||||
|
||||
HIO_ASSERT(hio, hio == dev->hio);
|
||||
@@ -815,6 +861,11 @@ static HIO_INLINE void handle_event (hio_t* hio, hio_dev_t* dev, int events, int
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* every exit from the loop above may leave data buffered inside the
|
||||
* transport. the multiplexer never reports readiness for such data,
|
||||
* so flag the device and let __exec() come back to it. */
|
||||
if (dev) update_read_pending (dev);
|
||||
}
|
||||
|
||||
if (dev)
|
||||
@@ -868,6 +919,23 @@ skip_evcb:
|
||||
}
|
||||
}
|
||||
|
||||
static void dispatch_pending_reads (hio_t* hio)
|
||||
{
|
||||
hio_dev_t* dev, * next;
|
||||
|
||||
/* TODO: this dispatcher traverses all devices. we may need a separate list for this kind of device? */
|
||||
|
||||
dev = HIO_DEVL_FIRST_DEV(&hio->actdev);
|
||||
while (!HIO_DEVL_IS_NIL_DEV(&hio->actdev, dev))
|
||||
{
|
||||
/* handle_event() may halt the device and unlink it, so take the
|
||||
* next pointer before dispatching. */
|
||||
next = dev->dev_next;
|
||||
if (dev->dev_cap & HIO_DEV_CAP_IN_PENDING) handle_event(hio, dev, HIO_DEV_EVENT_IN, 0);
|
||||
dev = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_unneeded_cfmbs (hio_t* hio)
|
||||
{
|
||||
hio_cfmb_t* cur, * next;
|
||||
@@ -924,7 +992,12 @@ static HIO_INLINE int __exec (hio_t* hio)
|
||||
|
||||
kill_all_halted_devices (hio);
|
||||
|
||||
if (hio_gettmrtmout(hio, HIO_NULL, &tmout) <= 0)
|
||||
if (hio->nrdpendings > 0)
|
||||
{
|
||||
tmout.sec = 0;
|
||||
tmout.nsec = 0;
|
||||
}
|
||||
else if (hio_gettmrtmout(hio, HIO_NULL, &tmout) <= 0)
|
||||
{
|
||||
/* defaults to 0 or 1 second if timeout can't be acquired.
|
||||
* if this timeout affects how fast the halted device will get killed.
|
||||
@@ -933,11 +1006,20 @@ static HIO_INLINE int __exec (hio_t* hio)
|
||||
tmout.nsec = 0;
|
||||
}
|
||||
|
||||
/* everything from here to the matching decrement dispatches events
|
||||
* out of a snapshot, so device memory must not be recycled yet. */
|
||||
hio->mux_depth++;
|
||||
|
||||
if (hio_sys_waitmux(hio, &tmout, handle_event) <= -1)
|
||||
{
|
||||
HIO_DEBUG0 (hio, "HIO - WARNING - Failed to wait on mutiplexer\n");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (hio->nrdpendings > 0) dispatch_pending_reads(hio);
|
||||
|
||||
hio->mux_depth--;
|
||||
if (!HIO_DEVL_IS_EMPTY(&hio->deaddev)) free_dead_devices(hio);
|
||||
}
|
||||
|
||||
kill_all_halted_devices (hio);
|
||||
@@ -1033,7 +1115,7 @@ hio_dev_t* hio_dev_make (hio_t* hio, hio_oow_t dev_size, hio_dev_mth_t* dev_mth,
|
||||
|
||||
HIO_INIT_NTIME(&dev->rtmout, 0, 0);
|
||||
dev->rtmridx = HIO_TMRIDX_INVALID;
|
||||
HIO_WQ_INIT (&dev->wq);
|
||||
HIO_WQ_INIT(&dev->wq);
|
||||
dev->cw_count = 0;
|
||||
|
||||
/* call the callback function first */
|
||||
@@ -1054,7 +1136,7 @@ hio_dev_t* hio_dev_make (hio_t* hio, hio_oow_t dev_size, hio_dev_mth_t* dev_mth,
|
||||
if (hio_dev_watch(dev, HIO_DEV_WATCH_START, 0) <= -1) goto oops_after_make;
|
||||
|
||||
/* and place the new device object at the back of the active device list */
|
||||
HIO_DEVL_APPEND_DEV (&hio->actdev, dev);
|
||||
HIO_DEVL_APPEND_DEV(&hio->actdev, dev);
|
||||
dev->dev_cap |= HIO_DEV_CAP_ACTIVE;
|
||||
HIO_DEBUG1(hio, "HIO - Set ACTIVE on device %p\n", dev);
|
||||
|
||||
@@ -1109,7 +1191,7 @@ static int kill_and_free_device (hio_dev_t* dev, int force)
|
||||
if (!(dev->dev_cap & HIO_DEV_CAP_ZOMBIE))
|
||||
{
|
||||
HIO_DEBUG1(hio, "HIO - Set ZOMBIE on device %p for kill method failure\n", dev);
|
||||
HIO_DEVL_APPEND_DEV (&hio->zmbdev, dev);
|
||||
HIO_DEVL_APPEND_DEV(&hio->zmbdev, dev);
|
||||
dev->dev_cap |= HIO_DEV_CAP_ZOMBIE;
|
||||
}
|
||||
|
||||
@@ -1121,16 +1203,42 @@ free_device:
|
||||
if (dev->dev_cap & HIO_DEV_CAP_ZOMBIE)
|
||||
{
|
||||
/* detach it from the zombie device list */
|
||||
HIO_DEVL_UNLINK_DEV (dev);
|
||||
HIO_DEVL_UNLINK_DEV(dev);
|
||||
dev->dev_cap &= ~HIO_DEV_CAP_ZOMBIE;
|
||||
HIO_DEBUG1(hio, "HIO - Unset ZOMBIE on device %p\n", dev);
|
||||
}
|
||||
|
||||
if (hio->mux_depth > 0)
|
||||
{
|
||||
/* the multiplexer hands out a batch of events captured before any of
|
||||
* them were dispatched, and each entry carries a raw device pointer.
|
||||
* releasing this object now would leave a dangling pointer in the
|
||||
* remainder of that batch for a callback to trip over. the device is
|
||||
* already dead - unlinked, kill method run, callbacks fired - so just
|
||||
* hold on to the memory until the batch is over. handle_event() skips
|
||||
* it in the meantime because HIO_DEV_CAP_ACTIVE is no longer set. */
|
||||
HIO_DEBUG1(hio, "HIO - Deferring the release of device %p until the current dispatch ends\n", dev);
|
||||
HIO_DEVL_APPEND_DEV(&hio->deaddev, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
HIO_DEBUG1(hio, "HIO - Freeed device %p\n", dev);
|
||||
hio_freemem(hio, dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* release what kill_and_free_device() held back during dispatch */
|
||||
static void free_dead_devices (hio_t* hio)
|
||||
{
|
||||
while (!HIO_DEVL_IS_EMPTY(&hio->deaddev))
|
||||
{
|
||||
hio_dev_t* dev = HIO_DEVL_FIRST_DEV(&hio->deaddev);
|
||||
HIO_DEVL_UNLINK_DEV(dev);
|
||||
HIO_DEBUG1(hio, "HIO - Freeed deferred device %p\n", dev);
|
||||
hio_freemem(hio, dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void kill_zombie_job_handler (hio_t* hio, const hio_ntime_t* now, hio_tmrjob_t* job)
|
||||
{
|
||||
hio_dev_t* dev = (hio_dev_t*)job->ctx;
|
||||
@@ -1202,6 +1310,10 @@ void hio_dev_kill (hio_dev_t* dev)
|
||||
dev->rtmridx = HIO_TMRIDX_INVALID;
|
||||
}
|
||||
|
||||
/* the device is leaving the active or halted list for good. hio_dev_halt()
|
||||
* does this as well, but a device can be killed while still active. */
|
||||
clear_read_pending (dev);
|
||||
|
||||
/* clear completed write event queues */
|
||||
if (dev->cw_count > 0) fire_cwq_handlers_for_dev (hio, dev, 1);
|
||||
|
||||
@@ -1218,14 +1330,14 @@ void hio_dev_kill (hio_dev_t* dev)
|
||||
{
|
||||
/* this device is in the halted state.
|
||||
* unlink it from the halted device list */
|
||||
HIO_DEVL_UNLINK_DEV (dev);
|
||||
HIO_DEVL_UNLINK_DEV(dev);
|
||||
dev->dev_cap &= ~HIO_DEV_CAP_HALTED;
|
||||
HIO_DEBUG1(hio, "HIO - Unset HALTED on device %p\n", dev);
|
||||
}
|
||||
else
|
||||
{
|
||||
HIO_ASSERT(hio, dev->dev_cap & HIO_DEV_CAP_ACTIVE);
|
||||
HIO_DEVL_UNLINK_DEV (dev);
|
||||
HIO_DEVL_UNLINK_DEV(dev);
|
||||
dev->dev_cap &= ~HIO_DEV_CAP_ACTIVE;
|
||||
HIO_DEBUG1(hio, "HIO - Unset ACTIVE on device %p\n", dev);
|
||||
}
|
||||
@@ -1258,17 +1370,19 @@ void hio_dev_halt (hio_dev_t* dev)
|
||||
{
|
||||
hio_t* hio = dev->hio;
|
||||
|
||||
clear_read_pending(dev);
|
||||
|
||||
if (dev->dev_cap & HIO_DEV_CAP_ACTIVE)
|
||||
{
|
||||
HIO_DEBUG1(hio, "HIO - Halting device %p\n", dev);
|
||||
|
||||
/* delink the device object from the active device list */
|
||||
HIO_DEVL_UNLINK_DEV (dev);
|
||||
HIO_DEVL_UNLINK_DEV(dev);
|
||||
dev->dev_cap &= ~HIO_DEV_CAP_ACTIVE;
|
||||
HIO_DEBUG1(hio, "HIO - Unset ACTIVE on device %p\n", dev);
|
||||
|
||||
/* place it at the back of the halted device list */
|
||||
HIO_DEVL_APPEND_DEV (&hio->hltdev, dev);
|
||||
HIO_DEVL_APPEND_DEV(&hio->hltdev, dev);
|
||||
dev->dev_cap |= HIO_DEV_CAP_HALTED;
|
||||
HIO_DEBUG1(hio, "HIO - Set HALTED on device %p\n", dev);
|
||||
}
|
||||
@@ -1351,10 +1465,28 @@ int hio_dev_watch (hio_dev_t* dev, hio_dev_watch_cmd_t cmd, int events)
|
||||
}
|
||||
|
||||
|
||||
/* the transport may need a direction watched that the device's own i/o
|
||||
* state doesn't call for. the classic case is tls: SSL_read() can return
|
||||
* SSL_ERROR_WANT_WRITE and SSL_write() can return SSL_ERROR_WANT_READ
|
||||
* while a renegotiation or a key update is in flight. the request is kept
|
||||
* on the device rather than passed in per call because a later RENEW -
|
||||
* which derives output watching from the write queue alone - would
|
||||
* otherwise drop it silently. HIO_DEV_WATCH_STOP jumps straight to
|
||||
* ctrl_mux and is not affected. */
|
||||
events |= dev->dev_extra_events;
|
||||
|
||||
/* this function honors HIO_DEV_EVENT_IN and HIO_DEV_EVENT_OUT only
|
||||
* as valid input event bits. it intends to provide simple abstraction
|
||||
* by reducing the variety of event bits that the caller has to handle. */
|
||||
if ((events & HIO_DEV_EVENT_IN) && !(dev->dev_cap & (HIO_DEV_CAP_IN_CLOSED | HIO_DEV_CAP_IN_DISABLED)))
|
||||
* by reducing the variety of event bits that the caller has to handle.
|
||||
*
|
||||
* HIO_DEV_CAP_IN_DISABLED is the application's backpressure switch while
|
||||
* dev_extra_events is the transport's protocol requirement. the latter
|
||||
* must win, or a tls write stalled on SSL_ERROR_WANT_READ could never
|
||||
* complete once the application has turned reading off. HIO_DEV_CAP_IN_CLOSED
|
||||
* is different - the input side is really gone - and still wins. */
|
||||
/*if ((events & HIO_DEV_EVENT_IN) && !(dev->dev_cap & (HIO_DEV_CAP_IN_CLOSED | HIO_DEV_CAP_IN_DISABLED))) <- before dev_extra_events was added*/
|
||||
if ((events & HIO_DEV_EVENT_IN) && !(dev->dev_cap & HIO_DEV_CAP_IN_CLOSED) &&
|
||||
(!(dev->dev_cap & HIO_DEV_CAP_IN_DISABLED) || (dev->dev_extra_events & HIO_DEV_EVENT_IN))) /* <- disabled but the underlying device wants to read it */
|
||||
{
|
||||
if (dev->dev_cap & HIO_DEV_CAP_IN)
|
||||
{
|
||||
@@ -1420,11 +1552,13 @@ static int __dev_read (hio_dev_t* dev, int enabled, const hio_ntime_t* tmout, vo
|
||||
if (enabled)
|
||||
{
|
||||
dev->dev_cap &= ~HIO_DEV_CAP_IN_DISABLED;
|
||||
update_read_pending(dev);
|
||||
if (!(dev->dev_cap & HIO_DEV_CAP_IN_WATCHED)) goto renew_watch_now;
|
||||
}
|
||||
else
|
||||
{
|
||||
dev->dev_cap |= HIO_DEV_CAP_IN_DISABLED;
|
||||
update_read_pending(dev);
|
||||
if ((dev->dev_cap & HIO_DEV_CAP_IN_WATCHED)) goto renew_watch_now;
|
||||
}
|
||||
|
||||
@@ -1777,7 +1911,7 @@ static HIO_INLINE int __dev_write (hio_dev_t* dev, const void* data, hio_iolen_t
|
||||
goto enqueue_completed_write;
|
||||
}
|
||||
|
||||
return 1; /* written immediately and called on_write callback. but this line will never be reached */
|
||||
/* code never reaches this part. goto to the section below is made */
|
||||
|
||||
enqueue_data:
|
||||
iov.iov_ptr = (void*)uptr;
|
||||
@@ -1884,7 +2018,7 @@ static HIO_INLINE int __dev_writev (hio_dev_t* dev, hio_iovec_t* iov, hio_iolen_
|
||||
goto enqueue_completed_write;
|
||||
}
|
||||
|
||||
return 1; /* written immediately and called on_write callback. but this line will never be reached */
|
||||
/* code never reaches this part. goto to the section below is made */
|
||||
|
||||
enqueue_data:
|
||||
return __enqueue_pending_write(dev, len, urem, iov, iovcnt, index, tmout, wrctx, dstaddr);
|
||||
@@ -1972,7 +2106,7 @@ static int __dev_sendfile (hio_dev_t* dev, hio_syshnd_t in_fd, hio_foff_t foff,
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1; /* written immediately and called on_write callback. but this line will never be reached */
|
||||
/* code never reaches this part. goto to the section below is made */
|
||||
|
||||
enqueue_data:
|
||||
return __enqueue_pending_sendfile(dev, len, urem, uoff, in_fd, tmout, wrctx, HIO_NULL);
|
||||
@@ -2264,6 +2398,7 @@ hio_oow_t hio_dev_cap_to_bcstr (hio_bitmask_t cap, hio_bch_t* buf, hio_oow_t siz
|
||||
if (cap & HIO_DEV_CAP_IN_WATCHED) len += hio_copy_bcstr(&buf[len], size - len, "in_watched|");
|
||||
if (cap & HIO_DEV_CAP_OUT_WATCHED) len += hio_copy_bcstr(&buf[len], size - len, "out_watched|");
|
||||
if (cap & HIO_DEV_CAP_PRI_WATCHED) len += hio_copy_bcstr(&buf[len], size - len, "pri_watched|");
|
||||
if (cap & HIO_DEV_CAP_IN_PENDING) len += hio_copy_bcstr(&buf[len], size - len, "in_pending|");
|
||||
if (cap & HIO_DEV_CAP_ACTIVE) len += hio_copy_bcstr(&buf[len], size - len, "active|");
|
||||
if (cap & HIO_DEV_CAP_HALTED) len += hio_copy_bcstr(&buf[len], size - len, "halted|");
|
||||
if (cap & HIO_DEV_CAP_ZOMBIE) len += hio_copy_bcstr(&buf[len], size - len, "zombie|");
|
||||
|
||||
@@ -242,6 +242,14 @@ struct hio_dev_mth_t
|
||||
int (*write) (hio_dev_t* dev, const void* data, hio_iolen_t* len, const hio_devaddr_t* dstaddr);
|
||||
int (*writev) (hio_dev_t* dev, const hio_iovec_t* iov, hio_iolen_t* iovcnt, const hio_devaddr_t* dstaddr);
|
||||
int (*sendfile) (hio_dev_t* dev, hio_syshnd_t in_fd, hio_foff_t foff, hio_iolen_t* len);
|
||||
|
||||
|
||||
/* optional. return non-zero if the transport is holding data that the
|
||||
* multiplexer cannot see - a decrypted tls record buffered inside the
|
||||
* ssl library, for instance. a level-triggered readiness notification
|
||||
* never fires for such data, so the core must re-enter the read loop
|
||||
* on its own instead of blocking in the multiplexer. */
|
||||
int (*readpending) (hio_dev_t* dev);
|
||||
};
|
||||
|
||||
struct hio_dev_evcb_t
|
||||
@@ -380,6 +388,7 @@ struct hio_wq_t
|
||||
hio_dev_evcb_t* dev_evcb; \
|
||||
hio_ntime_t rtmout; \
|
||||
hio_tmridx_t rtmridx; \
|
||||
int dev_extra_events; /* events the transport needs watched regardless of the device's own i/o state. see hio_dev_watch() */ \
|
||||
hio_wq_t wq; \
|
||||
hio_oow_t cw_count; \
|
||||
hio_dev_t* dev_prev; \
|
||||
@@ -426,7 +435,6 @@ enum hio_dev_cap_t
|
||||
HIO_DEV_CAP_PRI = ((hio_bitmask_t)1 << 3), /* meaningful only if #HIO_DEV_CAP_IN is set */
|
||||
HIO_DEV_CAP_STREAM = ((hio_bitmask_t)1 << 4), /* byte stream */
|
||||
|
||||
|
||||
HIO_DEV_CAP_IN_DISABLED = ((hio_bitmask_t)1 << 5),
|
||||
HIO_DEV_CAP_OUT_UNQUEUEABLE = ((hio_bitmask_t)1 << 6),
|
||||
HIO_DEV_CAP_ALL_MASK = (HIO_DEV_CAP_VIRTUAL | HIO_DEV_CAP_IN | HIO_DEV_CAP_OUT | HIO_DEV_CAP_PRI | HIO_DEV_CAP_STREAM | HIO_DEV_CAP_IN_DISABLED | HIO_DEV_CAP_OUT_UNQUEUEABLE),
|
||||
@@ -440,13 +448,14 @@ enum hio_dev_cap_t
|
||||
HIO_DEV_CAP_IN_WATCHED = ((hio_bitmask_t)1 << 12),
|
||||
HIO_DEV_CAP_OUT_WATCHED = ((hio_bitmask_t)1 << 13),
|
||||
HIO_DEV_CAP_PRI_WATCHED = ((hio_bitmask_t)1 << 14), /**< can be set only if HIO_DEV_CAP_IN_WATCHED is set */
|
||||
HIO_DEV_CAP_ACTIVE = ((hio_bitmask_t)1 << 15),
|
||||
HIO_DEV_CAP_HALTED = ((hio_bitmask_t)1 << 16),
|
||||
HIO_DEV_CAP_ZOMBIE = ((hio_bitmask_t)1 << 17),
|
||||
HIO_DEV_CAP_RENEW_REQUIRED = ((hio_bitmask_t)1 << 18),
|
||||
HIO_DEV_CAP_WATCH_STARTED = ((hio_bitmask_t)1 << 19),
|
||||
HIO_DEV_CAP_WATCH_SUSPENDED = ((hio_bitmask_t)1 << 20),
|
||||
HIO_DEV_CAP_WATCH_REREG_REQUIRED = ((hio_bitmask_t)1 << 21)
|
||||
HIO_DEV_CAP_IN_PENDING = ((hio_bitmask_t)1 << 15),
|
||||
HIO_DEV_CAP_ACTIVE = ((hio_bitmask_t)1 << 16),
|
||||
HIO_DEV_CAP_HALTED = ((hio_bitmask_t)1 << 17),
|
||||
HIO_DEV_CAP_ZOMBIE = ((hio_bitmask_t)1 << 18),
|
||||
HIO_DEV_CAP_RENEW_REQUIRED = ((hio_bitmask_t)1 << 19),
|
||||
HIO_DEV_CAP_WATCH_STARTED = ((hio_bitmask_t)1 << 20),
|
||||
HIO_DEV_CAP_WATCH_SUSPENDED = ((hio_bitmask_t)1 << 21),
|
||||
HIO_DEV_CAP_WATCH_REREG_REQUIRED = ((hio_bitmask_t)1 << 22),
|
||||
};
|
||||
typedef enum hio_dev_cap_t hio_dev_cap_t;
|
||||
|
||||
@@ -805,7 +814,9 @@ struct hio_t
|
||||
hio_dev_t actdev; /* list head of active devices */
|
||||
hio_dev_t hltdev; /* list head of halted devices */
|
||||
hio_dev_t zmbdev; /* list head of zombie devices */
|
||||
|
||||
hio_dev_t deaddev; /* list head of devices killed during multiplexer dispatch, awaiting the actual free */
|
||||
int mux_depth; /* how deep we are inside multiplexer event dispatch. non-zero defers device memory release */
|
||||
hio_oow_t nrdpendings; /* number of active devices carraying HIO_DEV_CAP_IN_PENDING */
|
||||
|
||||
hio_ntime_t init_time;
|
||||
struct
|
||||
@@ -1033,15 +1044,34 @@ HIO_EXPORT int hio_dev_timedread (
|
||||
const hio_ntime_t* tmout
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* The hio_dev_write() function posts a writing request.
|
||||
* It attempts to write data immediately if there is no pending requests.
|
||||
* If writing fails, it returns -1. If writing succeeds, it calls the
|
||||
* on_write callback. If the callback fails, it returns -1. If the callback
|
||||
* succeeds, it returns 1. If no immediate writing is possible, the request
|
||||
* is enqueued to a pending request list. If enqueing gets successful,
|
||||
* it returns 0. otherwise it returns -1.
|
||||
*
|
||||
* If the write queue is empty, it attempts to write immediately and whatever
|
||||
* the transport accepts on the spot goes out there and then. Any remainder,
|
||||
* or the whole request if the queue was not empty, is enqueued and pushed
|
||||
* out as the device becomes writable again.
|
||||
*
|
||||
* The on_write() callback is never invoked from within this function, not
|
||||
* even when the entire request is written immediately. It is placed on the
|
||||
* completed write queue and fired later from the event loop. This is
|
||||
* deliberate - calling it inline would let a callback that issues another
|
||||
* write re-enter this function. Callers must not assume on_write() has run
|
||||
* by the time this function returns.
|
||||
*
|
||||
* It returns 0 on success and -1 on failure. Success covers both the
|
||||
* immediate and the enqueued case; the two are not distinguishable through
|
||||
* the return value. Failure means the request was not accepted at all -
|
||||
* the writing end is already closed (#HIO_ENOCAPA), the transport reported
|
||||
* an error, or a queue entry could not be allocated.
|
||||
*
|
||||
* A failure occurring after the request has been accepted - the transport
|
||||
* erroring out while draining the queue, or on_write() returning -1 - is
|
||||
* not reported here. It halts the device instead.
|
||||
*
|
||||
* For a stream device, a request of zero length closes the writing end and
|
||||
* sets #HIO_DEV_CAP_OUT_CLOSED, so every subsequent write on that device
|
||||
* fails.
|
||||
*/
|
||||
HIO_EXPORT int hio_dev_write (
|
||||
hio_dev_t* dev,
|
||||
@@ -1051,6 +1081,11 @@ HIO_EXPORT int hio_dev_write (
|
||||
const hio_devaddr_t* dstaddr
|
||||
);
|
||||
|
||||
/**
|
||||
* The hio_dev_writev() function posts a writing request for a vector of
|
||||
* buffers. It follows the hio_dev_write() contract in full - 0 or -1, and
|
||||
* a deferred on_write() reporting the total length of the whole vector.
|
||||
*/
|
||||
HIO_EXPORT int hio_dev_writev (
|
||||
hio_dev_t* dev,
|
||||
hio_iovec_t* iov,
|
||||
@@ -1059,6 +1094,12 @@ HIO_EXPORT int hio_dev_writev (
|
||||
const hio_devaddr_t* dstaddr
|
||||
);
|
||||
|
||||
/**
|
||||
* The hio_dev_sendfile() function posts a writing request sourced from an
|
||||
* open file descriptor. It follows the hio_dev_write() contract, and fails
|
||||
* with #HIO_ENOCAPA on a device whose transport has no sendfile method or
|
||||
* which is not a stream.
|
||||
*/
|
||||
HIO_EXPORT int hio_dev_sendfile (
|
||||
hio_dev_t* dev,
|
||||
hio_syshnd_t in_fd,
|
||||
@@ -1067,6 +1108,14 @@ HIO_EXPORT int hio_dev_sendfile (
|
||||
void* wrctx
|
||||
);
|
||||
|
||||
/**
|
||||
* The hio_dev_timedwrite() function is hio_dev_write() with a deadline on
|
||||
* the enqueued portion of the request. If the request has not drained by
|
||||
* the time tmout elapses, it is dropped from the write queue and on_write()
|
||||
* is fired with a length of -1 and the hio error number set to #HIO_ETMOUT.
|
||||
* The deadline bounds the wait only - the return value still reports
|
||||
* nothing beyond whether the request was accepted.
|
||||
*/
|
||||
HIO_EXPORT int hio_dev_timedwrite (
|
||||
hio_dev_t* dev,
|
||||
const void* data,
|
||||
@@ -1077,6 +1126,10 @@ HIO_EXPORT int hio_dev_timedwrite (
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* The hio_dev_timedwritev() function is hio_dev_writev() with the deadline
|
||||
* described for hio_dev_timedwrite().
|
||||
*/
|
||||
HIO_EXPORT int hio_dev_timedwritev (
|
||||
hio_dev_t* dev,
|
||||
hio_iovec_t* iov,
|
||||
|
||||
@@ -606,10 +606,6 @@ static int open_peer_with_mode (file_t* file, const hio_bch_t* actual_file, int
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static int bind_task_to_peer (file_t* file, hio_htre_t* req, const hio_bch_t* file_path, const hio_bch_t* mime_type)
|
||||
|
||||
+5
-6
@@ -39,9 +39,8 @@
|
||||
|
||||
#define PXY_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH
|
||||
|
||||
|
||||
#define PXY_PEER_CONNECT_TMOUT (5)
|
||||
#define PXY_PENDING_IO_THRESHOLD 5
|
||||
#define PXY_PENDING_IO_THRESHOLD (5)
|
||||
|
||||
#define PXY_OVER_READ_FROM_CLIENT (1 << 0)
|
||||
#define PXY_OVER_READ_FROM_PEER (1 << 1)
|
||||
@@ -700,8 +699,8 @@ static int bind_task_to_peer (pxy_t* pxy, hio_dev_sck_t* csck, hio_htre_t* req,
|
||||
htrd = hio_htrd_open(hio, HIO_SIZEOF(*pxtn));
|
||||
if (HIO_UNLIKELY(!sck || !htrd)) goto oops;
|
||||
|
||||
hio_htrd_setoption (htrd, HIO_HTRD_RESPONSE);
|
||||
hio_htrd_setrecbs (htrd, &peer_htrd_recbs);
|
||||
hio_htrd_setoption(htrd, HIO_HTRD_RESPONSE);
|
||||
hio_htrd_setrecbs(htrd, &peer_htrd_recbs);
|
||||
|
||||
pxy->peer = sck;
|
||||
pxy->peer_htrd = htrd;
|
||||
@@ -718,9 +717,9 @@ static int bind_task_to_peer (pxy_t* pxy, hio_dev_sck_t* csck, hio_htre_t* req,
|
||||
* handed over once the connection completes. */
|
||||
if (build_request_head(pxy, req) <= -1) goto oops;
|
||||
|
||||
HIO_MEMSET (&c, 0, HIO_SIZEOF(c));
|
||||
HIO_MEMSET(&c, 0, HIO_SIZEOF(c));
|
||||
c.remoteaddr = *skad;
|
||||
HIO_INIT_NTIME (&c.connect_tmout, PXY_PEER_CONNECT_TMOUT, 0);
|
||||
HIO_INIT_NTIME(&c.connect_tmout, PXY_PEER_CONNECT_TMOUT, 0);
|
||||
if (hio_dev_sck_connect(pxy->peer, &c) <= -1) goto oops;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -416,6 +416,8 @@ static hio_dev_mth_t dev_mar_methods =
|
||||
HIO_NULL,
|
||||
HIO_NULL,
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL /* readpending */
|
||||
};
|
||||
|
||||
/* ========================================================================= */
|
||||
|
||||
@@ -381,6 +381,8 @@ static hio_dev_mth_t dev_pipe_methods =
|
||||
HIO_NULL,
|
||||
HIO_NULL,
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL /* readpending */
|
||||
};
|
||||
|
||||
static hio_dev_mth_t dev_pipe_methods_slave =
|
||||
@@ -396,6 +398,8 @@ static hio_dev_mth_t dev_pipe_methods_slave =
|
||||
dev_pipe_write_slave,
|
||||
dev_pipe_writev_slave,
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL /* readpending */
|
||||
};
|
||||
|
||||
/* ========================================================================= */
|
||||
|
||||
@@ -755,6 +755,8 @@ static hio_dev_mth_t dev_pro_methods =
|
||||
HIO_NULL, /* write */
|
||||
HIO_NULL, /* writev */
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL /* readpending */
|
||||
};
|
||||
|
||||
static hio_dev_mth_t dev_pro_methods_slave =
|
||||
@@ -770,6 +772,8 @@ static hio_dev_mth_t dev_pro_methods_slave =
|
||||
dev_pro_write_slave,
|
||||
dev_pro_writev_slave,
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL /* readpending */
|
||||
};
|
||||
|
||||
/* ========================================================================= */
|
||||
|
||||
@@ -96,6 +96,19 @@
|
||||
# define USE_SSL
|
||||
#endif
|
||||
|
||||
#if defined(USE_SSL)
|
||||
/* the largest plaintext record openssl hands back from a single SSL_read().
|
||||
* SSL3_RT_MAX_PLAIN_LENGTH is 16384 and the slack covers the padding and
|
||||
* compression allowance counted by SSL3_RT_MAX_ENCRYPTED_OVERHEAD. */
|
||||
#define HIO_SSL_MAX_READ_RECORD (16384 + 2048)
|
||||
|
||||
/* see the note at SSL_set_read_ahead() in do_ssl(). the core reads through
|
||||
* hio->bigbuf, and that buffer being at least one record wide is what keeps
|
||||
* SSL_pending() at zero. this fires the day the TODO on bigbuf in hio.h gets
|
||||
* acted on and the buffer shrinks below a record. */
|
||||
HIO_STATIC_ASSERT(HIO_COUNTOF(((hio_t*)0)->bigbuf) >= HIO_SSL_MAX_READ_RECORD);
|
||||
#endif
|
||||
|
||||
/* ========================================================================= */
|
||||
|
||||
static hio_syshnd_t open_async_socket (hio_t* hio, int domain, int type, int proto)
|
||||
@@ -133,7 +146,7 @@ done:
|
||||
|
||||
oops:
|
||||
hio_seterrwithsyserr(hio, 0, errno);
|
||||
if (sck != HIO_SYSHND_INVALID) close (sck);
|
||||
if (sck != HIO_SYSHND_INVALID) close(sck);
|
||||
return HIO_SYSHND_INVALID;
|
||||
}
|
||||
|
||||
@@ -171,8 +184,8 @@ open_socket:
|
||||
hio_makesyshndcloexec(hio, fd[1]) <= -1)
|
||||
{
|
||||
hio_seterrwithsyserr(hio, 0, errno);
|
||||
close (fd[0]);
|
||||
close (fd[1]);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
return HIO_SYSHND_INVALID;
|
||||
}
|
||||
|
||||
@@ -200,7 +213,7 @@ static hio_syshnd_t open_async_bpf (hio_t* hio)
|
||||
return fd;
|
||||
oops:
|
||||
hio_seterrwithsyserr(hio, 0, errno);
|
||||
if (fd != HIO_SYSHND_INVALID) close (fd);
|
||||
if (fd != HIO_SYSHND_INVALID) close(fd);
|
||||
return HIO_SYSHND_INVALID;
|
||||
}
|
||||
|
||||
@@ -382,7 +395,7 @@ static HIO_INLINE int schedule_timer_job_after (hio_dev_sck_t* dev, const hio_nt
|
||||
|
||||
/* ======================================================================== */
|
||||
#if defined(USE_SSL)
|
||||
static void set_ssl_error (hio_t* hio, int sslerr)
|
||||
static void set_ssl_error(hio_t* hio, int sslerr)
|
||||
{
|
||||
hio_bch_t emsg[128];
|
||||
ERR_error_string_n (sslerr, emsg, HIO_COUNTOF(emsg));
|
||||
@@ -449,8 +462,8 @@ static int dev_sck_make (hio_dev_t* dev, void* ctx)
|
||||
return 0;
|
||||
|
||||
oops:
|
||||
if (hnd != HIO_SYSHND_INVALID) close (hnd);
|
||||
if (side_chan != HIO_SYSHND_INVALID) close (side_chan);
|
||||
if (hnd != HIO_SYSHND_INVALID) close(hnd);
|
||||
if (side_chan != HIO_SYSHND_INVALID) close(side_chan);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -478,7 +491,7 @@ static int dev_sck_make_client (hio_dev_t* dev, void* ctx)
|
||||
oops:
|
||||
if (rdev->hnd != HIO_SYSHND_INVALID)
|
||||
{
|
||||
close (rdev->hnd);
|
||||
close(rdev->hnd);
|
||||
rdev->hnd = HIO_SYSHND_INVALID;
|
||||
}
|
||||
return -1;
|
||||
@@ -487,7 +500,7 @@ oops:
|
||||
static void dev_sck_fail_before_make_client (void* ctx)
|
||||
{
|
||||
hio_syshnd_t* clisckhnd = (hio_syshnd_t*)ctx;
|
||||
close (*clisckhnd);
|
||||
close(*clisckhnd);
|
||||
}
|
||||
|
||||
static int dev_sck_kill (hio_dev_t* dev, int force)
|
||||
@@ -513,40 +526,40 @@ static int dev_sck_kill (hio_dev_t* dev, int force)
|
||||
/* non-stream, but lisenable or connectable can have the progress bits on */
|
||||
/*HIO_ASSERT(hio, (rdev->state & HIO_DEV_SCK_ALL_PROGRESS_BITS) == 0);*/
|
||||
|
||||
if (rdev->on_disconnect) rdev->on_disconnect (rdev);
|
||||
if (rdev->on_disconnect) rdev->on_disconnect(rdev);
|
||||
}
|
||||
#else
|
||||
if (rdev->on_disconnect) rdev->on_disconnect (rdev);
|
||||
if (rdev->on_disconnect) rdev->on_disconnect(rdev);
|
||||
#endif
|
||||
if (rdev->tmrjob_index != HIO_TMRIDX_INVALID)
|
||||
{
|
||||
hio_deltmrjob (hio, rdev->tmrjob_index);
|
||||
hio_deltmrjob(hio, rdev->tmrjob_index);
|
||||
HIO_ASSERT(hio, rdev->tmrjob_index == HIO_TMRIDX_INVALID);
|
||||
}
|
||||
|
||||
#if defined(USE_SSL)
|
||||
if (rdev->ssl)
|
||||
{
|
||||
SSL_shutdown ((SSL*)rdev->ssl); /* is this needed? */
|
||||
SSL_free ((SSL*)rdev->ssl);
|
||||
SSL_shutdown((SSL*)rdev->ssl); /* is this needed? */
|
||||
SSL_free((SSL*)rdev->ssl);
|
||||
rdev->ssl = HIO_NULL;
|
||||
}
|
||||
if (!(rdev->state & (HIO_DEV_SCK_ACCEPTED | HIO_DEV_SCK_ACCEPTING_SSL)) && rdev->ssl_ctx)
|
||||
{
|
||||
SSL_CTX_free ((SSL_CTX*)rdev->ssl_ctx);
|
||||
SSL_CTX_free((SSL_CTX*)rdev->ssl_ctx);
|
||||
rdev->ssl_ctx = HIO_NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rdev->hnd != HIO_SYSHND_INVALID)
|
||||
{
|
||||
close (rdev->hnd);
|
||||
close(rdev->hnd);
|
||||
rdev->hnd = HIO_SYSHND_INVALID;
|
||||
}
|
||||
|
||||
if (rdev->side_chan != HIO_SYSHND_INVALID)
|
||||
{
|
||||
close (rdev->side_chan);
|
||||
close(rdev->side_chan);
|
||||
rdev->side_chan = HIO_SYSHND_INVALID;
|
||||
}
|
||||
|
||||
@@ -561,6 +574,24 @@ static hio_syshnd_t dev_sck_getsyshnd (hio_dev_t* dev)
|
||||
}
|
||||
/* ------------------------------------------------------------------------------ */
|
||||
|
||||
#if defined(USE_SSL)
|
||||
/* an SSL_read() may need to write and an SSL_write() may need to read - during
|
||||
* a tls 1.2 renegotiation or a tls 1.3 key update, for instance. record the
|
||||
* direction the tls layer is blocked on so that hio_dev_watch() keeps watching
|
||||
* it until the stalled operation gets through. pass 0 to clear.
|
||||
*
|
||||
* HIO_DEV_WATCH_RENEW recomputes the natural event set - input unless the
|
||||
* application disabled it, output only when the write queue is non-empty -
|
||||
* and hio_dev_watch() then ORs dev_extra_events on top of that. so clearing
|
||||
* this and renewing puts the device back exactly where it would have been. */
|
||||
static int ssl_want_events (hio_dev_sck_t* rdev, int events)
|
||||
{
|
||||
if (rdev->dev_extra_events == events) return 0;
|
||||
rdev->dev_extra_events = events;
|
||||
return hio_dev_watch((hio_dev_t*)rdev, HIO_DEV_WATCH_RENEW, HIO_DEV_EVENT_IN);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int dev_sck_read_stream (hio_dev_t* dev, void* buf, hio_iolen_t* len, hio_devaddr_t* srcaddr)
|
||||
{
|
||||
hio_t* hio = dev->hio;
|
||||
@@ -575,11 +606,29 @@ static int dev_sck_read_stream (hio_dev_t* dev, void* buf, hio_iolen_t* len, hio
|
||||
if (x <= -1)
|
||||
{
|
||||
int err = SSL_get_error((SSL*)rdev->ssl, x);
|
||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) return 0;
|
||||
set_ssl_error (hio, err);
|
||||
if (err == SSL_ERROR_WANT_READ)
|
||||
{
|
||||
/* no data yet. input is watched already. */
|
||||
return (ssl_want_events(rdev, 0) <= -1)? -1: 0;
|
||||
}
|
||||
if (err == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
/* the tls layer must push bytes out before it can decrypt any
|
||||
* more. watch output even with nothing queued for writing. */
|
||||
return (ssl_want_events(rdev, HIO_DEV_EVENT_OUT) <= -1)? -1: 0;
|
||||
}
|
||||
set_ssl_error(hio, err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ssl_want_events(rdev, 0) <= -1) return -1;
|
||||
|
||||
/* openssl buffers whole records internally and bytes left in that
|
||||
* buffer are invisible to epoll and kqueue - a level-triggered
|
||||
* readiness notification never fires for them, so they would sit
|
||||
* there until the peer happens to send more. read_ahead being off
|
||||
* plus a buffer wider than a record is what prevents it. */
|
||||
HIO_ASSERT(hio, SSL_pending((SSL*)rdev->ssl) == 0);
|
||||
*len = x;
|
||||
}
|
||||
else
|
||||
@@ -773,7 +822,7 @@ static int dev_sck_write_stream (hio_dev_t* dev, const void* data, hio_iolen_t*
|
||||
* the socket, probably leaving it in the half-closed state */
|
||||
if ((x = SSL_shutdown((SSL*)rdev->ssl)) <= -1)
|
||||
{
|
||||
set_ssl_error (hio, SSL_get_error((SSL*)rdev->ssl, x));
|
||||
set_ssl_error(hio, SSL_get_error((SSL*)rdev->ssl, x));
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
@@ -782,12 +831,24 @@ static int dev_sck_write_stream (hio_dev_t* dev, const void* data, hio_iolen_t*
|
||||
x = SSL_write((SSL*)rdev->ssl, data, *len);
|
||||
if (x <= -1)
|
||||
{
|
||||
int err = SSL_get_error ((SSL*)rdev->ssl, x);
|
||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) return 0;
|
||||
set_ssl_error (hio, err);
|
||||
int err = SSL_get_error((SSL*)rdev->ssl, x);
|
||||
if (err == SSL_ERROR_WANT_READ)
|
||||
{
|
||||
/* the tls layer must consume incoming bytes before it can
|
||||
* encrypt any more. the application may have disabled reading
|
||||
* for backpressure, so this has to override that. */
|
||||
return (ssl_want_events(rdev, HIO_DEV_EVENT_IN) <= -1)? -1: 0;
|
||||
}
|
||||
if (err == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
/* output is watched already - the core queues and arms it. */
|
||||
return (ssl_want_events(rdev, 0) <= -1)? -1: 0;
|
||||
}
|
||||
set_ssl_error(hio, err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ssl_want_events(rdev, 0) <= -1) return -1;
|
||||
*len = x;
|
||||
}
|
||||
else
|
||||
@@ -850,7 +911,7 @@ static int dev_sck_writev_stream (hio_dev_t* dev, const hio_iovec_t* iov, hio_io
|
||||
* the socket, probably leaving it in the half-closed state */
|
||||
if ((x = SSL_shutdown((SSL*)rdev->ssl)) <= -1)
|
||||
{
|
||||
set_ssl_error (hio, SSL_get_error((SSL*)rdev->ssl, x));
|
||||
set_ssl_error(hio, SSL_get_error((SSL*)rdev->ssl, x));
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
@@ -866,13 +927,22 @@ static int dev_sck_writev_stream (hio_dev_t* dev, const hio_iovec_t* iov, hio_io
|
||||
if (x <= -1)
|
||||
{
|
||||
int err = SSL_get_error ((SSL*)rdev->ssl, x);
|
||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) return 0;
|
||||
set_ssl_error (hio, err);
|
||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
if (ssl_want_events(rdev, (err == SSL_ERROR_WANT_READ)? HIO_DEV_EVENT_IN: 0) <= -1) return -1;
|
||||
/* earlier elements of the vector went out for real. reporting
|
||||
* 0 here would make the caller resend them and duplicate
|
||||
* bytes on the wire. report the partial count instead. */
|
||||
if (nwritten > 0) { *iovcnt = nwritten; return 1; }
|
||||
return 0;
|
||||
}
|
||||
set_ssl_error(hio, err);
|
||||
return -1;
|
||||
}
|
||||
nwritten += x;
|
||||
}
|
||||
|
||||
if (ssl_want_events(rdev, 0) <= -1) return -1;
|
||||
*iovcnt = nwritten;
|
||||
}
|
||||
else
|
||||
@@ -1078,7 +1148,7 @@ static int dev_sck_sendfile_stream (hio_dev_t* dev, hio_syshnd_t in_fd, hio_foff
|
||||
* the socket, probably leaving it in the half-closed state */
|
||||
if ((x = SSL_shutdown((SSL*)rdev->ssl)) <= -1)
|
||||
{
|
||||
set_ssl_error (hio, SSL_get_error((SSL*)rdev->ssl, x));
|
||||
set_ssl_error(hio, SSL_get_error((SSL*)rdev->ssl, x));
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
@@ -1088,11 +1158,23 @@ static int dev_sck_sendfile_stream (hio_dev_t* dev, hio_syshnd_t in_fd, hio_foff
|
||||
if (x <= -1)
|
||||
{
|
||||
int err = SSL_get_error ((SSL*)rdev->ssl, x);
|
||||
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) return 0;
|
||||
set_ssl_error (hio, err);
|
||||
if (err == SSL_ERROR_WANT_READ)
|
||||
{
|
||||
/* the tls layer must consume incoming bytes before it can
|
||||
* encrypt any more. the application may have disabled reading
|
||||
* for backpressure, so this has to override that. */
|
||||
return (ssl_want_events(rdev, HIO_DEV_EVENT_IN) <= -1)? -1: 0;
|
||||
}
|
||||
if (err == SSL_ERROR_WANT_WRITE)
|
||||
{
|
||||
/* output is watched already - the core queues and arms it. */
|
||||
return (ssl_want_events(rdev, 0) <= -1)? -1: 0;
|
||||
}
|
||||
set_ssl_error(hio, err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ssl_want_events(rdev, 0) <= -1) return -1;
|
||||
*len = x;
|
||||
}
|
||||
else
|
||||
@@ -1141,8 +1223,12 @@ static int dev_sck_sendfile_stream (hio_dev_t* dev, hio_syshnd_t in_fd, hio_foff
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------ */
|
||||
|
||||
#if defined(USE_SSL)
|
||||
static int dev_sck_readpending (hio_dev_t* dev)
|
||||
{
|
||||
hio_dev_sck_t* rdev = (hio_dev_sck_t*)dev;
|
||||
return rdev->ssl? SSL_pending((SSL*)rdev->ssl): 0;
|
||||
}
|
||||
|
||||
static int do_ssl (hio_dev_sck_t* dev, int (*ssl_func)(SSL*))
|
||||
{
|
||||
@@ -1158,17 +1244,24 @@ static int do_ssl (hio_dev_sck_t* dev, int (*ssl_func)(SSL*))
|
||||
ssl = SSL_new(dev->ssl_ctx);
|
||||
if (!ssl)
|
||||
{
|
||||
set_ssl_error (hio, ERR_get_error());
|
||||
set_ssl_error(hio, ERR_get_error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SSL_set_fd(ssl, dev->hnd) == 0)
|
||||
{
|
||||
set_ssl_error (hio, ERR_get_error());
|
||||
set_ssl_error(hio, ERR_get_error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSL_set_read_ahead (ssl, 0);
|
||||
/* keep openssl reading one record at a time instead of slurping
|
||||
* whatever else the kernel has ready into its own buffer. together
|
||||
* with a read buffer at least HIO_SSL_MAX_READ_RECORD wide, this is
|
||||
* what guarantees a single SSL_read() drains the record and leaves
|
||||
* SSL_pending() at zero. surplus records stay in the kernel buffer
|
||||
* where the multiplexer can still see them. this is load-bearing,
|
||||
* not a tuning knob. */
|
||||
SSL_set_read_ahead(ssl, 0);
|
||||
|
||||
dev->ssl = ssl;
|
||||
}
|
||||
@@ -1194,7 +1287,7 @@ static int do_ssl (hio_dev_sck_t* dev, int (*ssl_func)(SSL*))
|
||||
}
|
||||
else
|
||||
{
|
||||
set_ssl_error (hio, err);
|
||||
set_ssl_error(hio, err);
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
@@ -1205,7 +1298,7 @@ static int do_ssl (hio_dev_sck_t* dev, int (*ssl_func)(SSL*))
|
||||
|
||||
if (hio_dev_watch((hio_dev_t*)dev, watcher_cmd, watcher_events) <= -1)
|
||||
{
|
||||
hio_stop (hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
hio_stop(hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
@@ -1331,14 +1424,14 @@ static int dev_sck_ioctl (hio_dev_t* dev, int cmd, void* arg)
|
||||
if (rdev->ssl_ctx)
|
||||
{
|
||||
#if defined(USE_SSL)
|
||||
SSL_CTX_free (rdev->ssl_ctx);
|
||||
SSL_CTX_free(rdev->ssl_ctx);
|
||||
#endif
|
||||
rdev->ssl_ctx = HIO_NULL;
|
||||
|
||||
if (rdev->ssl)
|
||||
{
|
||||
#if defined(USE_SSL)
|
||||
SSL_free (rdev->ssl);
|
||||
SSL_free(rdev->ssl);
|
||||
#endif
|
||||
rdev->ssl = HIO_NULL;
|
||||
}
|
||||
@@ -1356,7 +1449,7 @@ static int dev_sck_ioctl (hio_dev_t* dev, int cmd, void* arg)
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
if (!ssl_ctx)
|
||||
{
|
||||
set_ssl_error (hio, ERR_get_error());
|
||||
set_ssl_error(hio, ERR_get_error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1365,12 +1458,12 @@ static int dev_sck_ioctl (hio_dev_t* dev, int cmd, void* arg)
|
||||
SSL_CTX_check_private_key(ssl_ctx) == 0 /*||
|
||||
SSL_CTX_use_certificate_chain_file(ssl_ctx, bnd->chainfile) == 0*/)
|
||||
{
|
||||
set_ssl_error (hio, ERR_get_error());
|
||||
SSL_CTX_free (ssl_ctx);
|
||||
set_ssl_error(hio, ERR_get_error());
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSL_CTX_set_read_ahead (ssl_ctx, 0);
|
||||
SSL_CTX_set_read_ahead(ssl_ctx, 0);
|
||||
SSL_CTX_set_mode (ssl_ctx, SSL_CTX_get_mode(ssl_ctx) |
|
||||
/*SSL_MODE_ENABLE_PARTIAL_WRITE |*/
|
||||
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
@@ -1387,7 +1480,7 @@ static int dev_sck_ioctl (hio_dev_t* dev, int cmd, void* arg)
|
||||
{
|
||||
hio_seterrwithsyserr(hio, 0, errno);
|
||||
#if defined(USE_SSL)
|
||||
if (ssl_ctx) SSL_CTX_free (ssl_ctx);
|
||||
if (ssl_ctx) SSL_CTX_free(ssl_ctx);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
@@ -1437,11 +1530,11 @@ static int dev_sck_ioctl (hio_dev_t* dev, int cmd, void* arg)
|
||||
{
|
||||
if (rdev->ssl)
|
||||
{
|
||||
SSL_free (rdev->ssl);
|
||||
SSL_free(rdev->ssl);
|
||||
rdev->ssl = HIO_NULL;
|
||||
}
|
||||
|
||||
SSL_CTX_free (rdev->ssl_ctx);
|
||||
SSL_CTX_free(rdev->ssl_ctx);
|
||||
rdev->ssl_ctx = HIO_NULL;
|
||||
}
|
||||
|
||||
@@ -1450,12 +1543,12 @@ static int dev_sck_ioctl (hio_dev_t* dev, int cmd, void* arg)
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if (!ssl_ctx)
|
||||
{
|
||||
set_ssl_error (hio, ERR_get_error());
|
||||
set_ssl_error(hio, ERR_get_error());
|
||||
return -1;
|
||||
}
|
||||
|
||||
SSL_CTX_set_read_ahead (ssl_ctx, 0);
|
||||
SSL_CTX_set_mode (ssl_ctx, SSL_CTX_get_mode(ssl_ctx) |
|
||||
SSL_CTX_set_read_ahead(ssl_ctx, 0);
|
||||
SSL_CTX_set_mode(ssl_ctx, SSL_CTX_get_mode(ssl_ctx) |
|
||||
/* SSL_MODE_ENABLE_PARTIAL_WRITE | */
|
||||
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
}
|
||||
@@ -1477,7 +1570,7 @@ fcntl (rdev->hnd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (hio_dev_watch((hio_dev_t*)rdev, HIO_DEV_WATCH_UPDATE, HIO_DEV_EVENT_IN | HIO_DEV_EVENT_OUT) <= -1)
|
||||
{
|
||||
/* watcher update failure. it's critical */
|
||||
hio_stop (hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
hio_stop(hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
goto oops_connect_watcher_error;
|
||||
}
|
||||
else
|
||||
@@ -1494,7 +1587,7 @@ fcntl (rdev->hnd, F_SETFL, flags | O_NONBLOCK);
|
||||
{
|
||||
/* update rdev->tmout to the deadline of the connect timeout job */
|
||||
HIO_ASSERT(hio, rdev->tmrjob_index != HIO_TMRIDX_INVALID);
|
||||
hio_gettmrjobdeadline (hio, rdev->tmrjob_index, &rdev->tmout);
|
||||
hio_gettmrjobdeadline(hio, rdev->tmrjob_index, &rdev->tmout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1513,12 +1606,12 @@ fcntl (rdev->hnd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (hio_dev_watch((hio_dev_t*)rdev, HIO_DEV_WATCH_UPDATE, HIO_DEV_EVENT_IN) <= -1)
|
||||
{
|
||||
/* watcher update failure. it's critical */
|
||||
hio_stop (hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
hio_stop(hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
}
|
||||
|
||||
oops_connect_watcher_error:
|
||||
#if defined(USE_SSL)
|
||||
if (ssl_ctx) SSL_CTX_free (ssl_ctx);
|
||||
if (ssl_ctx) SSL_CTX_free(ssl_ctx);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
@@ -1531,7 +1624,7 @@ fcntl (rdev->hnd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (hio_dev_watch((hio_dev_t*)rdev, HIO_DEV_WATCH_UPDATE, HIO_DEV_EVENT_IN | HIO_DEV_EVENT_OUT) <= -1)
|
||||
{
|
||||
/* watcher update failure. it's critical */
|
||||
hio_stop (hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
hio_stop(hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
goto oops_connect;
|
||||
}
|
||||
|
||||
@@ -1584,7 +1677,7 @@ fcntl (rdev->hnd, F_SETFL, flags | O_NONBLOCK);
|
||||
if (hio_dev_watch((hio_dev_t*)rdev, HIO_DEV_WATCH_STOP, 0) <= -1 ||
|
||||
hio_dev_watch((hio_dev_t*)rdev, HIO_DEV_WATCH_START, 0) <= -1)
|
||||
{
|
||||
hio_stop (hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
hio_stop(hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -1612,6 +1705,8 @@ static hio_dev_mth_t dev_mth_sck_stateless =
|
||||
dev_sck_write_stateless,
|
||||
dev_sck_writev_stateless,
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL, /* readpending */
|
||||
};
|
||||
|
||||
|
||||
@@ -1628,6 +1723,12 @@ static hio_dev_mth_t dev_mth_sck_stream =
|
||||
dev_sck_write_stream,
|
||||
dev_sck_writev_stream,
|
||||
dev_sck_sendfile_stream,
|
||||
|
||||
#if defined(USE_SSL)
|
||||
dev_sck_readpending
|
||||
#else
|
||||
HIO_NULL
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(ENABLE_SCTP)
|
||||
@@ -1644,6 +1745,8 @@ static hio_dev_mth_t dev_mth_sck_sctp_sp =
|
||||
dev_sck_write_sctp_sp,
|
||||
dev_sck_writev_sctp_sp,
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL, /* readpending */
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1659,7 +1762,9 @@ static hio_dev_mth_t dev_mth_clisck_stateless =
|
||||
dev_sck_read_stateless,
|
||||
dev_sck_write_stateless,
|
||||
dev_sck_writev_stateless,
|
||||
HIO_NULL,
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL, /* readpending */
|
||||
};
|
||||
|
||||
static hio_dev_mth_t dev_mth_clisck_stream =
|
||||
@@ -1674,7 +1779,13 @@ static hio_dev_mth_t dev_mth_clisck_stream =
|
||||
dev_sck_read_stream,
|
||||
dev_sck_write_stream,
|
||||
dev_sck_writev_stream,
|
||||
dev_sck_sendfile_stream
|
||||
dev_sck_sendfile_stream, /* sendfile */
|
||||
|
||||
#if defined(USE_SSL)
|
||||
dev_sck_readpending /* readpending */
|
||||
#else
|
||||
HIO_NULL /* readpending */
|
||||
#endif
|
||||
};
|
||||
|
||||
#if defined(ENABLE_SCTP)
|
||||
@@ -1690,7 +1801,9 @@ static hio_dev_mth_t dev_mth_clisck_sctp_sp =
|
||||
dev_sck_read_sctp_sp,
|
||||
dev_sck_write_sctp_sp,
|
||||
dev_sck_writev_sctp_sp,
|
||||
HIO_NULL,
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL /* readpending - no ssl on a seqpacket socket */
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1707,6 +1820,8 @@ static hio_dev_mth_t dev_mth_sck_bpf =
|
||||
dev_sck_write_bpf,
|
||||
dev_sck_writev_bpf,
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL, /* readpending */
|
||||
};
|
||||
|
||||
/* ========================================================================= */
|
||||
@@ -1744,7 +1859,7 @@ static int harvest_outgoing_connection (hio_dev_sck_t* rdev)
|
||||
if (hio_dev_watch((hio_dev_t*)rdev, HIO_DEV_WATCH_RENEW, HIO_DEV_EVENT_IN) <= -1)
|
||||
{
|
||||
/* watcher update failure. it's critical */
|
||||
hio_stop (hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
hio_stop(hio, HIO_STOPREQ_WATCHER_ERROR);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1816,7 +1931,7 @@ static int make_accepted_client_connection (hio_dev_sck_t* rdev, hio_syshnd_t cl
|
||||
/* this is a special optional callback. If you don't want a client socket device
|
||||
* to be created upon accept, you may implement the on_raw_accept() handler.
|
||||
* the socket handle is delegated to the callback. */
|
||||
rdev->on_raw_accept (rdev, clisck, remoteaddr);
|
||||
rdev->on_raw_accept(rdev, clisck, remoteaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -196,6 +196,8 @@ static hio_dev_mth_t dev_shw_methods =
|
||||
dev_shw_write,
|
||||
dev_shw_writev,
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL /* readpending */
|
||||
};
|
||||
|
||||
/* ========================================================================= */
|
||||
|
||||
@@ -572,7 +572,9 @@ static hio_dev_mth_t dev_thr_methods =
|
||||
HIO_NULL,
|
||||
HIO_NULL,
|
||||
HIO_NULL,
|
||||
HIO_NULL /* sendfile */
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL /* readpending */
|
||||
};
|
||||
|
||||
static hio_dev_mth_t dev_thr_methods_slave =
|
||||
@@ -588,6 +590,8 @@ static hio_dev_mth_t dev_thr_methods_slave =
|
||||
dev_thr_write_slave,
|
||||
dev_thr_writev_slave,
|
||||
HIO_NULL, /* sendfile */
|
||||
|
||||
HIO_NULL /* readpending */
|
||||
};
|
||||
|
||||
/* ========================================================================= */
|
||||
|
||||
+13
-1
@@ -18,7 +18,7 @@ check_SCRIPTS = s-001.sh s-002.sh
|
||||
EXTRA_DIST = $(check_SCRIPTS) tap.inc t-cgi.sh
|
||||
|
||||
## programs run as tests
|
||||
TESTPROGS = t-001 t-002 t-003 t-004 t-005 t-006 t-007 t-008
|
||||
TESTPROGS = t-001 t-002 t-003 t-004 t-005 t-006 t-007 t-008 t-009
|
||||
|
||||
## helpers the test scripts drive. built with the tests, but not tests
|
||||
## themselves - they are servers and would never exit.
|
||||
@@ -76,6 +76,14 @@ t_008_CFLAGS = $(CFLAGS_COMMON)
|
||||
t_008_LDFLAGS = $(LDFLAGS_COMMON)
|
||||
t_008_LDADD = $(LIBADD_COMMON)
|
||||
|
||||
## t-009 drives a tls client and a tls server in one loop. it skips itself
|
||||
## if the openssl command is unavailable to generate a throwaway certificate.
|
||||
t_009_SOURCES = t-009.c tap.h
|
||||
t_009_CPPFLAGS = $(CPPFLAGS_COMMON)
|
||||
t_009_CFLAGS = $(CFLAGS_COMMON)
|
||||
t_009_LDFLAGS = $(LDFLAGS_COMMON)
|
||||
t_009_LDADD = $(LIBADD_COMMON)
|
||||
|
||||
## a minimal FastCGI responder, driven by s-002.sh
|
||||
fcgis_SOURCES = fcgis.c
|
||||
fcgis_CPPFLAGS = $(CPPFLAGS_COMMON)
|
||||
@@ -97,6 +105,10 @@ httssvr_CFLAGS = $(CFLAGS_COMMON)
|
||||
httssvr_LDFLAGS = $(LDFLAGS_COMMON)
|
||||
httssvr_LDADD = $(LIBADD_COMMON)
|
||||
|
||||
## t-009 writes a throwaway certificate into the build directory and removes
|
||||
## it on the way out. this catches the case where it dies before that.
|
||||
CLEANFILES = t009-cert.pem t009-key.pem
|
||||
|
||||
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/ac/tap-driver.sh
|
||||
TESTS = $(TESTPROGS) $(check_SCRIPTS)
|
||||
|
||||
|
||||
+46
-5
@@ -108,7 +108,7 @@ CONFIG_CLEAN_FILES =
|
||||
CONFIG_CLEAN_VPATH_FILES =
|
||||
am__EXEEXT_1 = t-001$(EXEEXT) t-002$(EXEEXT) t-003$(EXEEXT) \
|
||||
t-004$(EXEEXT) t-005$(EXEEXT) t-006$(EXEEXT) t-007$(EXEEXT) \
|
||||
t-008$(EXEEXT)
|
||||
t-008$(EXEEXT) t-009$(EXEEXT)
|
||||
am__EXEEXT_2 = fcgis$(EXEEXT) httpecho$(EXEEXT) httssvr$(EXEEXT)
|
||||
am_fcgis_OBJECTS = fcgis-fcgis.$(OBJEXT)
|
||||
fcgis_OBJECTS = $(am_fcgis_OBJECTS)
|
||||
@@ -182,6 +182,12 @@ t_008_DEPENDENCIES = $(am__DEPENDENCIES_2)
|
||||
t_008_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(t_008_CFLAGS) $(CFLAGS) \
|
||||
$(t_008_LDFLAGS) $(LDFLAGS) -o $@
|
||||
am_t_009_OBJECTS = t_009-t-009.$(OBJEXT)
|
||||
t_009_OBJECTS = $(am_t_009_OBJECTS)
|
||||
t_009_DEPENDENCIES = $(am__DEPENDENCIES_2)
|
||||
t_009_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(t_009_CFLAGS) $(CFLAGS) \
|
||||
$(t_009_LDFLAGS) $(LDFLAGS) -o $@
|
||||
AM_V_P = $(am__v_P_@AM_V@)
|
||||
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
|
||||
am__v_P_0 = false
|
||||
@@ -203,7 +209,7 @@ am__depfiles_remade = ./$(DEPDIR)/fcgis-fcgis.Po \
|
||||
./$(DEPDIR)/t_002-t-002.Po ./$(DEPDIR)/t_003-t-003.Po \
|
||||
./$(DEPDIR)/t_004-t-004.Po ./$(DEPDIR)/t_005-t-005.Po \
|
||||
./$(DEPDIR)/t_006-t-006.Po ./$(DEPDIR)/t_007-t-007.Po \
|
||||
./$(DEPDIR)/t_008-t-008.Po
|
||||
./$(DEPDIR)/t_008-t-008.Po ./$(DEPDIR)/t_009-t-009.Po
|
||||
am__mv = mv -f
|
||||
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
|
||||
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
|
||||
@@ -226,11 +232,11 @@ am__v_CCLD_1 =
|
||||
SOURCES = $(fcgis_SOURCES) $(httpecho_SOURCES) $(httssvr_SOURCES) \
|
||||
$(t_001_SOURCES) $(t_002_SOURCES) $(t_003_SOURCES) \
|
||||
$(t_004_SOURCES) $(t_005_SOURCES) $(t_006_SOURCES) \
|
||||
$(t_007_SOURCES) $(t_008_SOURCES)
|
||||
$(t_007_SOURCES) $(t_008_SOURCES) $(t_009_SOURCES)
|
||||
DIST_SOURCES = $(fcgis_SOURCES) $(httpecho_SOURCES) $(httssvr_SOURCES) \
|
||||
$(t_001_SOURCES) $(t_002_SOURCES) $(t_003_SOURCES) \
|
||||
$(t_004_SOURCES) $(t_005_SOURCES) $(t_006_SOURCES) \
|
||||
$(t_007_SOURCES) $(t_008_SOURCES)
|
||||
$(t_007_SOURCES) $(t_008_SOURCES) $(t_009_SOURCES)
|
||||
am__can_run_installinfo = \
|
||||
case $$AM_UPDATE_INFO_DIR in \
|
||||
n|no|NO) false;; \
|
||||
@@ -620,7 +626,7 @@ LDFLAGS_COMMON = -L$(abs_builddir)/../lib -L$(libdir)
|
||||
LIBADD_COMMON = ../lib/libhio.la $(LIBM)
|
||||
check_SCRIPTS = s-001.sh s-002.sh
|
||||
EXTRA_DIST = $(check_SCRIPTS) tap.inc t-cgi.sh
|
||||
TESTPROGS = t-001 t-002 t-003 t-004 t-005 t-006 t-007 t-008
|
||||
TESTPROGS = t-001 t-002 t-003 t-004 t-005 t-006 t-007 t-008 t-009
|
||||
HELPERPROGS = fcgis httpecho httssvr
|
||||
t_001_SOURCES = t-001.c tap.h
|
||||
t_001_CPPFLAGS = $(CPPFLAGS_COMMON)
|
||||
@@ -662,6 +668,11 @@ t_008_CPPFLAGS = $(CPPFLAGS_COMMON)
|
||||
t_008_CFLAGS = $(CFLAGS_COMMON)
|
||||
t_008_LDFLAGS = $(LDFLAGS_COMMON)
|
||||
t_008_LDADD = $(LIBADD_COMMON)
|
||||
t_009_SOURCES = t-009.c tap.h
|
||||
t_009_CPPFLAGS = $(CPPFLAGS_COMMON)
|
||||
t_009_CFLAGS = $(CFLAGS_COMMON)
|
||||
t_009_LDFLAGS = $(LDFLAGS_COMMON)
|
||||
t_009_LDADD = $(LIBADD_COMMON)
|
||||
fcgis_SOURCES = fcgis.c
|
||||
fcgis_CPPFLAGS = $(CPPFLAGS_COMMON)
|
||||
fcgis_CFLAGS = $(CFLAGS_COMMON)
|
||||
@@ -677,6 +688,7 @@ httssvr_CPPFLAGS = $(CPPFLAGS_COMMON)
|
||||
httssvr_CFLAGS = $(CFLAGS_COMMON)
|
||||
httssvr_LDFLAGS = $(LDFLAGS_COMMON)
|
||||
httssvr_LDADD = $(LIBADD_COMMON)
|
||||
CLEANFILES = t009-cert.pem t009-key.pem
|
||||
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/ac/tap-driver.sh
|
||||
TEST_EXTENSIONS = .sh
|
||||
SH_LOG_DRIVER = $(LOG_DRIVER)
|
||||
@@ -763,6 +775,10 @@ t-008$(EXEEXT): $(t_008_OBJECTS) $(t_008_DEPENDENCIES) $(EXTRA_t_008_DEPENDENCIE
|
||||
@rm -f t-008$(EXEEXT)
|
||||
$(AM_V_CCLD)$(t_008_LINK) $(t_008_OBJECTS) $(t_008_LDADD) $(LIBS)
|
||||
|
||||
t-009$(EXEEXT): $(t_009_OBJECTS) $(t_009_DEPENDENCIES) $(EXTRA_t_009_DEPENDENCIES)
|
||||
@rm -f t-009$(EXEEXT)
|
||||
$(AM_V_CCLD)$(t_009_LINK) $(t_009_OBJECTS) $(t_009_LDADD) $(LIBS)
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
|
||||
@@ -780,6 +796,7 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_006-t-006.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_007-t-007.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_008-t-008.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_009-t-009.Po@am__quote@ # am--include-marker
|
||||
|
||||
$(am__depfiles_remade):
|
||||
@$(MKDIR_P) $(@D)
|
||||
@@ -965,6 +982,20 @@ t_008-t-008.obj: t-008.c
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(t_008_CPPFLAGS) $(CPPFLAGS) $(t_008_CFLAGS) $(CFLAGS) -c -o t_008-t-008.obj `if test -f 't-008.c'; then $(CYGPATH_W) 't-008.c'; else $(CYGPATH_W) '$(srcdir)/t-008.c'; fi`
|
||||
|
||||
t_009-t-009.o: t-009.c
|
||||
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(t_009_CPPFLAGS) $(CPPFLAGS) $(t_009_CFLAGS) $(CFLAGS) -MT t_009-t-009.o -MD -MP -MF $(DEPDIR)/t_009-t-009.Tpo -c -o t_009-t-009.o `test -f 't-009.c' || echo '$(srcdir)/'`t-009.c
|
||||
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_009-t-009.Tpo $(DEPDIR)/t_009-t-009.Po
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t-009.c' object='t_009-t-009.o' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(t_009_CPPFLAGS) $(CPPFLAGS) $(t_009_CFLAGS) $(CFLAGS) -c -o t_009-t-009.o `test -f 't-009.c' || echo '$(srcdir)/'`t-009.c
|
||||
|
||||
t_009-t-009.obj: t-009.c
|
||||
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(t_009_CPPFLAGS) $(CPPFLAGS) $(t_009_CFLAGS) $(CFLAGS) -MT t_009-t-009.obj -MD -MP -MF $(DEPDIR)/t_009-t-009.Tpo -c -o t_009-t-009.obj `if test -f 't-009.c'; then $(CYGPATH_W) 't-009.c'; else $(CYGPATH_W) '$(srcdir)/t-009.c'; fi`
|
||||
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_009-t-009.Tpo $(DEPDIR)/t_009-t-009.Po
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t-009.c' object='t_009-t-009.obj' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(t_009_CPPFLAGS) $(CPPFLAGS) $(t_009_CFLAGS) $(CFLAGS) -c -o t_009-t-009.obj `if test -f 't-009.c'; then $(CYGPATH_W) 't-009.c'; else $(CYGPATH_W) '$(srcdir)/t-009.c'; fi`
|
||||
|
||||
mostlyclean-libtool:
|
||||
-rm -f *.lo
|
||||
|
||||
@@ -1245,6 +1276,13 @@ t-008.log: t-008$(EXEEXT)
|
||||
--log-file $$b.log --trs-file $$b.trs \
|
||||
$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
|
||||
"$$tst" $(AM_TESTS_FD_REDIRECT)
|
||||
t-009.log: t-009$(EXEEXT)
|
||||
@p='t-009$(EXEEXT)'; \
|
||||
b='t-009'; \
|
||||
$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
|
||||
--log-file $$b.log --trs-file $$b.trs \
|
||||
$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
|
||||
"$$tst" $(AM_TESTS_FD_REDIRECT)
|
||||
.sh.log:
|
||||
@p='$<'; \
|
||||
$(am__set_b); \
|
||||
@@ -1324,6 +1362,7 @@ mostlyclean-generic:
|
||||
-$(am__rm_f) $(TEST_SUITE_LOG)
|
||||
|
||||
clean-generic:
|
||||
-$(am__rm_f) $(CLEANFILES)
|
||||
|
||||
distclean-generic:
|
||||
-$(am__rm_f) $(CONFIG_CLEAN_FILES)
|
||||
@@ -1349,6 +1388,7 @@ distclean: distclean-am
|
||||
-rm -f ./$(DEPDIR)/t_006-t-006.Po
|
||||
-rm -f ./$(DEPDIR)/t_007-t-007.Po
|
||||
-rm -f ./$(DEPDIR)/t_008-t-008.Po
|
||||
-rm -f ./$(DEPDIR)/t_009-t-009.Po
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-compile distclean-generic \
|
||||
distclean-tags
|
||||
@@ -1405,6 +1445,7 @@ maintainer-clean: maintainer-clean-am
|
||||
-rm -f ./$(DEPDIR)/t_006-t-006.Po
|
||||
-rm -f ./$(DEPDIR)/t_007-t-007.Po
|
||||
-rm -f ./$(DEPDIR)/t_008-t-008.Po
|
||||
-rm -f ./$(DEPDIR)/t_009-t-009.Po
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
|
||||
@@ -532,14 +532,15 @@ done:
|
||||
static void test_kill_peer_from_callback (void)
|
||||
{
|
||||
/* two devices are made readable together so the multiplexer reports
|
||||
* both in one batch. whichever is dispatched first kills the other,
|
||||
* which means the second entry in the batch always refers to a device
|
||||
* that has already been freed - regardless of the order the kernel
|
||||
* chose. nothing in the dispatch loop revalidates that pointer.
|
||||
* both in one batch. whichever is dispatched first kills the other, so
|
||||
* the second entry in the batch always refers to a device that is gone -
|
||||
* regardless of the order the kernel chose.
|
||||
*
|
||||
* this only bites on a multiplexer that dispatches from a snapshot;
|
||||
* see MUX_DISPATCHES_FROM_SNAPSHOT above. the test runs either way so
|
||||
* that the safe backends stay covered against a regression. */
|
||||
* this only bites on a multiplexer that dispatches from a snapshot; see
|
||||
* MUX_DISPATCHES_FROM_SNAPSHOT above. the core covers it by holding the
|
||||
* memory of a device killed mid-dispatch until the batch finishes, and
|
||||
* by skipping any event for a device that is no longer active. the test
|
||||
* runs on every backend so both halves stay covered. */
|
||||
tdev_t* a, * b;
|
||||
int peer_a = -1, peer_b = -1;
|
||||
|
||||
@@ -561,10 +562,6 @@ static void test_kill_peer_from_callback (void)
|
||||
pump ();
|
||||
|
||||
OK (g_on_read_calls == 1, "the surviving device read once and killed its peer");
|
||||
|
||||
#if MUX_DISPATCHES_FROM_SNAPSHOT
|
||||
todo ("known issue C1: the mux dispatch loop does not revalidate device pointers within a batch", 1);
|
||||
#endif
|
||||
OK (g_use_after_free == 0, "no event is dispatched to a device killed earlier in the same batch");
|
||||
|
||||
done:
|
||||
|
||||
@@ -280,8 +280,7 @@ static void test_immediate_write (void)
|
||||
OK (rc >= 0, "hio_dev_write() reports success for an immediate write");
|
||||
OK (g_cw_n == 0, "on_write is not invoked from inside hio_dev_write()");
|
||||
|
||||
todo ("hio.h documents a return of 1 for an immediate write; __dev_write() returns 0 via the completion queue", 1);
|
||||
OK (rc == 1, "hio_dev_write() returns 1 for an immediate write as documented");
|
||||
OK (rc == 0, "hio_dev_write() returns 0 for an immediate write, same as for an enqueued one");
|
||||
|
||||
pump ();
|
||||
OK (g_cw_n == 1, "the deferred completion fires on the next loop iteration");
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* tls transport tests.
|
||||
*
|
||||
* an hio tcp server and an hio tcp client living in the same loop, both
|
||||
* speaking ssl. this exercises dev_sck_read_stream()/dev_sck_write_stream()
|
||||
* on the openssl branch rather than the plain recv()/send() one, which no
|
||||
* other test in this directory reaches.
|
||||
*
|
||||
* the payloads deliberately span many tls records (a record tops out at
|
||||
* 16kb), so one logical hio_dev_sck_write() turns into repeated SSL_write()
|
||||
* calls that complete partially, and the reading side takes many SSL_read()
|
||||
* calls to drain. the byte pattern is position-dependent so reordering,
|
||||
* duplication or truncation all show up as a mismatch rather than as a
|
||||
* short count.
|
||||
*
|
||||
* the certificate is generated at run time with the openssl command. if that
|
||||
* is not available the whole file skips rather than fails - the library is
|
||||
* still perfectly usable without the openssl tool installed.
|
||||
*/
|
||||
|
||||
#include <hio-sck.h>
|
||||
#include <hio-prv.h>
|
||||
#include "tap.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define CERTFILE "t009-cert.pem"
|
||||
#define KEYFILE "t009-key.pem"
|
||||
|
||||
#define BULK_LEN (128 * 1024)
|
||||
#define BP_LEN (64 * 1024)
|
||||
#define BP_CHUNK (8 * 1024) /* stop reading after this much, then resume */
|
||||
#define DEADLINE_S 20
|
||||
|
||||
static hio_t* g_hio = HIO_NULL;
|
||||
static hio_dev_sck_t* g_srv = HIO_NULL; /* listener */
|
||||
static hio_dev_sck_t* g_acc = HIO_NULL; /* accepted peer of the client */
|
||||
static hio_dev_sck_t* g_cli = HIO_NULL; /* connecting client */
|
||||
|
||||
static int g_connected; /* client reached the CONNECTED state */
|
||||
static int g_accepted; /* listener produced an ACCEPTED device */
|
||||
static int g_timeout;
|
||||
|
||||
static hio_uint8_t* g_out; /* what the client sends */
|
||||
static hio_oow_t g_outlen;
|
||||
|
||||
static hio_uint8_t* g_srv_in; /* what the server collected */
|
||||
static hio_oow_t g_srv_inlen;
|
||||
static hio_uint8_t* g_cli_in; /* what the client collected back */
|
||||
static hio_oow_t g_cli_inlen;
|
||||
|
||||
static int g_srv_bad; /* server saw a byte it did not expect */
|
||||
static int g_echo_started;
|
||||
static int g_done;
|
||||
|
||||
static int g_backpressure; /* toggle reading off in the server on_read */
|
||||
static int g_bp_paused; /* reading is currently off */
|
||||
static int g_bp_pauses; /* how many times we paused - proves it happened */
|
||||
|
||||
static void quiet_logging (hio_t* hio)
|
||||
{
|
||||
hio_bitmask_t mask = HIO_LOG_ERROR | HIO_LOG_FATAL | HIO_LOG_ALL_TYPES;
|
||||
hio_setoption (hio, HIO_LOG_MASK, &mask);
|
||||
}
|
||||
|
||||
/* position-dependent so any shuffling is visible */
|
||||
static hio_uint8_t pat (hio_oow_t i)
|
||||
{
|
||||
return (hio_uint8_t)((i * 7 + (i >> 8) * 31 + 13) & 0xff);
|
||||
}
|
||||
|
||||
static void fill_pattern (hio_uint8_t* buf, hio_oow_t len)
|
||||
{
|
||||
hio_oow_t i;
|
||||
for (i = 0; i < len; i++) buf[i] = pat(i);
|
||||
}
|
||||
|
||||
static int matches_pattern (const hio_uint8_t* buf, hio_oow_t len)
|
||||
{
|
||||
hio_oow_t i;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (buf[i] != pat(i)) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void on_deadline (hio_t* hio, const hio_ntime_t* now, hio_tmrjob_t* job)
|
||||
{
|
||||
g_timeout = 1;
|
||||
}
|
||||
|
||||
static hio_tmridx_t g_deadline_idx = HIO_TMRIDX_INVALID;
|
||||
|
||||
static void arm_deadline (void)
|
||||
{
|
||||
hio_tmrjob_t j;
|
||||
HIO_MEMSET (&j, 0, HIO_SIZEOF(j));
|
||||
hio_gettime (g_hio, &j.when);
|
||||
j.when.sec += DEADLINE_S;
|
||||
j.handler = on_deadline;
|
||||
/* idxptr keeps g_deadline_idx correct as the heap reshuffles, so the
|
||||
* job can still be cancelled once the transfer finishes. */
|
||||
j.idxptr = &g_deadline_idx;
|
||||
g_deadline_idx = hio_instmrjob(g_hio, &j);
|
||||
}
|
||||
|
||||
static void disarm_deadline (void)
|
||||
{
|
||||
/* a pending timer keeps hio_exec() sleeping until it expires, which
|
||||
* would make teardown take the full deadline rather than no time. */
|
||||
if (g_deadline_idx != HIO_TMRIDX_INVALID)
|
||||
{
|
||||
hio_deltmrjob (g_hio, g_deadline_idx);
|
||||
g_deadline_idx = HIO_TMRIDX_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/* resume reading on the server side after a short pause */
|
||||
static void on_resume (hio_t* hio, const hio_ntime_t* now, hio_tmrjob_t* job)
|
||||
{
|
||||
hio_dev_sck_t* sck = (hio_dev_sck_t*)job->ctx;
|
||||
g_bp_paused = 0;
|
||||
hio_dev_sck_read (sck, 1);
|
||||
}
|
||||
|
||||
static void pause_reading (hio_dev_sck_t* sck)
|
||||
{
|
||||
hio_tmrjob_t j;
|
||||
|
||||
g_bp_paused = 1;
|
||||
g_bp_pauses++;
|
||||
hio_dev_sck_read (sck, 0);
|
||||
|
||||
HIO_MEMSET (&j, 0, HIO_SIZEOF(j));
|
||||
hio_gettime (g_hio, &j.when);
|
||||
j.when.nsec += 20000000; /* 20ms */
|
||||
if (j.when.nsec >= HIO_NSECS_PER_SEC) { j.when.sec++; j.when.nsec -= HIO_NSECS_PER_SEC; }
|
||||
j.handler = on_resume;
|
||||
j.ctx = sck;
|
||||
hio_instmrjob (g_hio, &j);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* server side */
|
||||
|
||||
static int srv_on_read (hio_dev_sck_t* sck, const void* data, hio_iolen_t dlen, const hio_skad_t* srcaddr)
|
||||
{
|
||||
hio_oow_t i;
|
||||
const hio_uint8_t* p = (const hio_uint8_t*)data;
|
||||
|
||||
if (dlen <= 0) return 0; /* eof or error */
|
||||
|
||||
for (i = 0; i < (hio_oow_t)dlen; i++)
|
||||
{
|
||||
if (g_srv_inlen >= g_outlen) { g_srv_bad = 1; break; }
|
||||
if (p[i] != pat(g_srv_inlen)) g_srv_bad = 1;
|
||||
g_srv_in[g_srv_inlen++] = p[i];
|
||||
}
|
||||
|
||||
if (g_backpressure && !g_bp_paused && g_srv_inlen < g_outlen &&
|
||||
(g_srv_inlen / BP_CHUNK) > (hio_oow_t)g_bp_pauses)
|
||||
{
|
||||
pause_reading (sck);
|
||||
}
|
||||
|
||||
if (g_srv_inlen >= g_outlen && !g_echo_started)
|
||||
{
|
||||
/* echo the whole thing back so the opposite direction is covered too */
|
||||
g_echo_started = 1;
|
||||
if (hio_dev_sck_write(sck, g_srv_in, g_outlen, HIO_NULL, HIO_NULL) <= -1) return -1;
|
||||
}
|
||||
|
||||
return 0; /* deliberately non-greedy - the case the pending path exists for */
|
||||
}
|
||||
|
||||
static int srv_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wrctx, const hio_skad_t* dstaddr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void srv_on_connect (hio_dev_sck_t* sck)
|
||||
{
|
||||
if (sck->state & HIO_DEV_SCK_ACCEPTED)
|
||||
{
|
||||
g_accepted = 1;
|
||||
g_acc = sck;
|
||||
}
|
||||
}
|
||||
|
||||
static void srv_on_disconnect (hio_dev_sck_t* sck)
|
||||
{
|
||||
if (sck == g_acc) g_acc = HIO_NULL;
|
||||
else if (sck == g_srv) g_srv = HIO_NULL;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* client side */
|
||||
|
||||
static int cli_on_read (hio_dev_sck_t* sck, const void* data, hio_iolen_t dlen, const hio_skad_t* srcaddr)
|
||||
{
|
||||
hio_oow_t i;
|
||||
const hio_uint8_t* p = (const hio_uint8_t*)data;
|
||||
|
||||
if (dlen <= 0) return 0;
|
||||
|
||||
for (i = 0; i < (hio_oow_t)dlen; i++)
|
||||
{
|
||||
if (g_cli_inlen >= g_outlen) break;
|
||||
g_cli_in[g_cli_inlen++] = p[i];
|
||||
}
|
||||
|
||||
if (g_cli_inlen >= g_outlen) g_done = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cli_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wrctx, const hio_skad_t* dstaddr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cli_on_connect (hio_dev_sck_t* sck)
|
||||
{
|
||||
if (sck->state & HIO_DEV_SCK_CONNECTED)
|
||||
{
|
||||
g_connected = 1;
|
||||
if (hio_dev_sck_write(sck, g_out, g_outlen, HIO_NULL, HIO_NULL) <= -1)
|
||||
hio_dev_sck_halt (sck);
|
||||
}
|
||||
}
|
||||
|
||||
static void cli_on_disconnect (hio_dev_sck_t* sck)
|
||||
{
|
||||
if (sck == g_cli) g_cli = HIO_NULL;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void reset_state (hio_oow_t len)
|
||||
{
|
||||
g_connected = g_accepted = g_timeout = 0;
|
||||
g_srv_inlen = g_cli_inlen = 0;
|
||||
g_srv_bad = g_echo_started = g_done = 0;
|
||||
g_bp_paused = g_bp_pauses = 0;
|
||||
g_outlen = len;
|
||||
fill_pattern (g_out, len);
|
||||
HIO_MEMSET (g_srv_in, 0, len);
|
||||
HIO_MEMSET (g_cli_in, 0, len);
|
||||
}
|
||||
|
||||
/* build the ssl listener and return its actual bound address */
|
||||
static int start_server (hio_skad_t* boundaddr)
|
||||
{
|
||||
hio_dev_sck_make_t mi;
|
||||
hio_dev_sck_bind_t bi;
|
||||
hio_dev_sck_listen_t li;
|
||||
|
||||
HIO_MEMSET (&mi, 0, HIO_SIZEOF(mi));
|
||||
mi.type = HIO_DEV_SCK_TCP4;
|
||||
mi.on_read = srv_on_read;
|
||||
mi.on_write = srv_on_write;
|
||||
mi.on_connect = srv_on_connect;
|
||||
mi.on_disconnect = srv_on_disconnect;
|
||||
|
||||
g_srv = hio_dev_sck_make(g_hio, 0, &mi);
|
||||
if (!g_srv) return -1;
|
||||
|
||||
HIO_MEMSET (&bi, 0, HIO_SIZEOF(bi));
|
||||
/* port 0 - the kernel picks a free one, so concurrent runs never collide */
|
||||
if (hio_bcstrtoskad(g_hio, "127.0.0.1:0", &bi.localaddr) <= -1) return -1;
|
||||
bi.options = HIO_DEV_SCK_BIND_REUSEADDR | HIO_DEV_SCK_BIND_SSL;
|
||||
bi.ssl_certfile = CERTFILE;
|
||||
bi.ssl_keyfile = KEYFILE;
|
||||
if (hio_dev_sck_bind(g_srv, &bi) <= -1) return -1;
|
||||
|
||||
HIO_MEMSET (&li, 0, HIO_SIZEOF(li));
|
||||
li.backlogs = 8;
|
||||
/* how long an accepted socket may take to finish its ssl handshake.
|
||||
* a negative value means no limit, which is what a test wants - the
|
||||
* run is already bounded by its own deadline timer, and a hard limit
|
||||
* here would only add a way to flake on a loaded machine. zero is a
|
||||
* zero-length window and would halt the peer the instant it is
|
||||
* accepted, before any ClientHello could be read. */
|
||||
HIO_INIT_NTIME (&li.accept_tmout, -1, 0);
|
||||
if (hio_dev_sck_listen(g_srv, &li) <= -1) return -1;
|
||||
|
||||
/* the bind request asked for port 0. dev->localaddr keeps the requested
|
||||
* address, so ask the kernel which port it actually handed out. */
|
||||
if (hio_dev_sck_getsockaddr(g_srv, boundaddr) <= -1) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_client (const hio_skad_t* peer)
|
||||
{
|
||||
hio_dev_sck_make_t mi;
|
||||
hio_dev_sck_connect_t ci;
|
||||
|
||||
HIO_MEMSET (&mi, 0, HIO_SIZEOF(mi));
|
||||
mi.type = HIO_DEV_SCK_TCP4;
|
||||
mi.on_read = cli_on_read;
|
||||
mi.on_write = cli_on_write;
|
||||
mi.on_connect = cli_on_connect;
|
||||
mi.on_disconnect = cli_on_disconnect;
|
||||
|
||||
g_cli = hio_dev_sck_make(g_hio, 0, &mi);
|
||||
if (!g_cli) return -1;
|
||||
|
||||
HIO_MEMSET (&ci, 0, HIO_SIZEOF(ci));
|
||||
ci.remoteaddr = *peer;
|
||||
ci.options = HIO_DEV_SCK_CONNECT_SSL;
|
||||
HIO_INIT_NTIME (&ci.connect_tmout, 5, 0);
|
||||
if (hio_dev_sck_connect(g_cli, &ci) <= -1) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_until_done (void)
|
||||
{
|
||||
arm_deadline ();
|
||||
while (!g_done && !g_timeout)
|
||||
{
|
||||
if (hio_exec(g_hio) <= -1) break;
|
||||
}
|
||||
disarm_deadline ();
|
||||
}
|
||||
|
||||
static void teardown (void)
|
||||
{
|
||||
if (g_cli) hio_dev_sck_halt (g_cli);
|
||||
if (g_acc) hio_dev_sck_halt (g_acc);
|
||||
if (g_srv) hio_dev_sck_halt (g_srv);
|
||||
/* let the halted devices be reaped so the loop is quiescent */
|
||||
hio_exec (g_hio);
|
||||
hio_exec (g_hio);
|
||||
g_cli = g_acc = g_srv = HIO_NULL;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* a full-size transfer in both directions over tls. this is the plain
|
||||
* regression net for the openssl read and write paths. */
|
||||
static void test_bulk (void)
|
||||
{
|
||||
hio_skad_t peer;
|
||||
|
||||
reset_state (BULK_LEN);
|
||||
g_backpressure = 0;
|
||||
|
||||
OK (start_server(&peer) == 0, "tls listener binds and listens");
|
||||
OK (start_client(&peer) == 0, "tls client connect initiated");
|
||||
|
||||
run_until_done ();
|
||||
|
||||
OK (!g_timeout, "tls bulk transfer completed before the deadline");
|
||||
OK (g_connected, "client reached the CONNECTED state (handshake done)");
|
||||
OK (g_accepted, "listener produced an ACCEPTED device (handshake done)");
|
||||
OK (g_srv_inlen == (hio_oow_t)BULK_LEN, "server received the whole payload");
|
||||
OK (!g_srv_bad, "server payload arrived in order and unmodified");
|
||||
OK (g_cli_inlen == (hio_oow_t)BULK_LEN, "client received the whole echo");
|
||||
OK (matches_pattern(g_cli_in, BULK_LEN), "echoed payload is byte-identical");
|
||||
OK (g_hio->nrdpendings == 0, "no device left flagged as read-pending");
|
||||
|
||||
teardown ();
|
||||
}
|
||||
|
||||
/* the receiving side repeatedly turns input off mid-transfer. with tls this
|
||||
* is the case where SSL_write() on the peer can report SSL_ERROR_WANT_READ
|
||||
* while HIO_DEV_CAP_IN_DISABLED is set on this end. */
|
||||
static void test_backpressure (void)
|
||||
{
|
||||
hio_skad_t peer;
|
||||
|
||||
reset_state (BP_LEN);
|
||||
g_backpressure = 1;
|
||||
|
||||
OK (start_server(&peer) == 0, "tls listener binds and listens (backpressure)");
|
||||
OK (start_client(&peer) == 0, "tls client connect initiated (backpressure)");
|
||||
|
||||
run_until_done ();
|
||||
|
||||
OK (!g_timeout, "transfer completed despite repeated read suspension");
|
||||
OK (g_bp_pauses > 0, "input was actually suspended at least once");
|
||||
OK (g_srv_inlen == (hio_oow_t)BP_LEN, "server received the whole payload");
|
||||
OK (!g_srv_bad, "server payload arrived in order and unmodified");
|
||||
OK (matches_pattern(g_cli_in, BP_LEN), "echoed payload is byte-identical");
|
||||
OK (g_hio->nrdpendings == 0, "no device left flagged as read-pending");
|
||||
|
||||
teardown ();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* a library built without openssl rejects HIO_DEV_SCK_BIND_SSL with
|
||||
* HIO_ENOIMPL. there is nothing to test in that case. */
|
||||
static int ssl_supported (void)
|
||||
{
|
||||
hio_skad_t addr;
|
||||
int supported;
|
||||
|
||||
if (start_server(&addr) == 0) supported = 1;
|
||||
else supported = (hio_geterrnum(g_hio) != HIO_ENOIMPL);
|
||||
|
||||
teardown ();
|
||||
return supported;
|
||||
}
|
||||
|
||||
static int make_cert (void)
|
||||
{
|
||||
int rc = system("openssl req -x509 -newkey rsa:2048 -nodes"
|
||||
" -keyout " KEYFILE " -out " CERTFILE
|
||||
" -days 3650 -subj /CN=localhost >/dev/null 2>&1");
|
||||
return (rc == 0 && access(CERTFILE, R_OK) == 0 && access(KEYFILE, R_OK) == 0);
|
||||
}
|
||||
|
||||
int main (void)
|
||||
{
|
||||
hio_errinf_t errinf;
|
||||
int have_ssl;
|
||||
|
||||
/* skip_all() has to come before no_plan() - tap.h refuses a second plan -
|
||||
* so both preconditions are settled before any test output is produced. */
|
||||
|
||||
g_out = (hio_uint8_t*)malloc(BULK_LEN);
|
||||
g_srv_in = (hio_uint8_t*)malloc(BULK_LEN);
|
||||
g_cli_in = (hio_uint8_t*)malloc(BULK_LEN);
|
||||
if (!g_out || !g_srv_in || !g_cli_in)
|
||||
{
|
||||
no_plan ();
|
||||
bail_out ("out of memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_hio = hio_open(HIO_NULL, 0, HIO_NULL, HIO_FEATURE_ALL, 16, &errinf);
|
||||
if (!g_hio)
|
||||
{
|
||||
no_plan ();
|
||||
bail_out ("unable to open hio");
|
||||
return -1;
|
||||
}
|
||||
quiet_logging (g_hio);
|
||||
|
||||
if (!make_cert())
|
||||
{
|
||||
skip_all ("openssl command not usable - cannot generate a test certificate");
|
||||
hio_close (g_hio);
|
||||
return exit_status();
|
||||
}
|
||||
|
||||
have_ssl = ssl_supported();
|
||||
if (!have_ssl)
|
||||
{
|
||||
skip_all ("this build of hio has no ssl support");
|
||||
unlink (CERTFILE);
|
||||
unlink (KEYFILE);
|
||||
hio_close (g_hio);
|
||||
return exit_status();
|
||||
}
|
||||
|
||||
no_plan ();
|
||||
|
||||
test_bulk ();
|
||||
test_backpressure ();
|
||||
|
||||
hio_close (g_hio);
|
||||
|
||||
free (g_out); free (g_srv_in); free (g_cli_in);
|
||||
unlink (CERTFILE);
|
||||
unlink (KEYFILE);
|
||||
|
||||
return exit_status();
|
||||
}
|
||||
Reference in New Issue
Block a user