Compare commits

...
5 Commits
17 changed files with 982 additions and 144 deletions
+1 -1
View File
@@ -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
View File
@@ -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)
+160 -25
View File
@@ -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|");
+69 -16
View File
@@ -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,
-4
View File
@@ -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
View File
@@ -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;
+2
View File
@@ -416,6 +416,8 @@ static hio_dev_mth_t dev_mar_methods =
HIO_NULL,
HIO_NULL,
HIO_NULL, /* sendfile */
HIO_NULL /* readpending */
};
/* ========================================================================= */
+4
View File
@@ -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 */
};
/* ========================================================================= */
+4
View File
@@ -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 */
};
/* ========================================================================= */
+173 -58
View File
@@ -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;
}
+2
View File
@@ -196,6 +196,8 @@ static hio_dev_mth_t dev_shw_methods =
dev_shw_write,
dev_shw_writev,
HIO_NULL, /* sendfile */
HIO_NULL /* readpending */
};
/* ========================================================================= */
+5 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+8 -11
View File
@@ -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:
+1 -2
View File
@@ -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");
+475
View File
@@ -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();
}