Compare commits

...
5 Commits
4 changed files with 136 additions and 26 deletions
+20
View File
@@ -457,6 +457,18 @@ struct hio_dev_sck_qxmsg_t
}; };
typedef struct hio_dev_sck_qxmsg_t hio_dev_sck_qxmsg_t; typedef struct hio_dev_sck_qxmsg_t hio_dev_sck_qxmsg_t;
/* the qx side channel is a socketpair. on most systems it is a datagram pair,
* where one write is one message and the reader is handed whole messages. where
* a datagram pair cannot carry messages reliably it is a stream pair instead,
* and the reader has to recover the message boundaries itself.
*
* the socket type, the writing side and the reader must agree on which of the
* two it is, so all three ask this one macro rather than testing for the system
* separately. */
#if defined(__BEOS__) || defined(__HAIKU__)
# define HIO_DEV_SCK_QX_STREAM
#endif
struct hio_dev_sck_t struct hio_dev_sck_t
{ {
HIO_DEV_HEADER; HIO_DEV_HEADER;
@@ -512,8 +524,16 @@ struct hio_dev_sck_t
struct struct
{ {
hio_syshnd_t side_chan; /* side-channel for HIO_DEV_SCK_QX */ hio_syshnd_t side_chan; /* side-channel for HIO_DEV_SCK_QX */
#if defined(HIO_DEV_SCK_QX_STREAM)
/* the message being reassembled, and how much of it has arrived.
* a stream side channel splits and merges writes freely, so a read
* can end in the middle of a message and the remainder has to be
* held until the rest comes. only the stream variant needs it, and
* the hio_skad_t inside the message makes it large, so it is not
* carried on the systems that never look at it. */
hio_dev_sck_qxmsg_t qxacc; hio_dev_sck_qxmsg_t qxacc;
hio_oow_t qxacc_len; hio_oow_t qxacc_len;
#endif
} qx; } qx;
struct struct
+100 -20
View File
@@ -209,7 +209,11 @@ open_socket:
if (hio_makesyshndasync(hio, sck) <= -1 || if (hio_makesyshndasync(hio, sck) <= -1 ||
hio_makesyshndcloexec(hio, sck) <= -1) goto oops; hio_makesyshndcloexec(hio, sck) <= -1) goto oops;
/* the label is only jumped to from the branch that asks socket() for the flags
* directly, so it exists only where that branch does */
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) && !(defined(__BEOS__) || defined(__HAIKU__))
done: done:
#endif
return sck; return sck;
oops: oops:
@@ -221,13 +225,11 @@ oops:
static hio_syshnd_t open_async_qx (hio_t* hio, hio_syshnd_t* side_chan) static hio_syshnd_t open_async_qx (hio_t* hio, hio_syshnd_t* side_chan)
{ {
int fd[2]; int fd[2];
int type = SOCK_DGRAM; #if defined(HIO_DEV_SCK_QX_STREAM)
#if defined(__BEOS__) || defined(__HAIKU__)
/* on haiku os r6beta, SOCK_DGRAM isn't reliable. more than one write() on it causes SIGPIPE */ /* on haiku os r6beta, SOCK_DGRAM isn't reliable. more than one write() on it causes SIGPIPE */
type = SOCK_STREAM; int type = SOCK_STREAM;
#else #else
type = SOCK_DGRAM; int type = SOCK_DGRAM;
#endif #endif
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) && !(defined(__BEOS__) || defined(__HAIKU__)) #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) && !(defined(__BEOS__) || defined(__HAIKU__))
@@ -265,7 +267,11 @@ open_socket:
return HIO_SYSHND_INVALID; return HIO_SYSHND_INVALID;
} }
/* the label is only jumped to from the branch that asks socketpair() for the
* flags directly, so it exists only where that branch does */
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) && !(defined(__BEOS__) || defined(__HAIKU__))
done: done:
#endif
*side_chan = fd[1]; /* write end of the pipe */ *side_chan = fd[1]; /* write end of the pipe */
return fd[0]; /* read end of the pipe */ return fd[0]; /* read end of the pipe */
} }
@@ -340,7 +346,7 @@ static int dev_sck_read_bpf (hio_dev_t* dev, void* buf, hio_iolen_t* len, hio_de
{ {
hio_t* hio = dev->hio; hio_t* hio = dev->hio;
hio_dev_sck_t* rdev = (hio_dev_sck_t*)dev; hio_dev_sck_t* rdev = (hio_dev_sck_t*)dev;
bpf_state_t* st = (bpf_state_t*)rdev->bpf_state; bpf_state_t* st = (bpf_state_t*)rdev->u.bpf.state;
struct bpf_hdr* bh; struct bpf_hdr* bh;
hio_oow_t caplen; hio_oow_t caplen;
@@ -400,7 +406,7 @@ static int dev_sck_read_bpf (hio_dev_t* dev, void* buf, hio_iolen_t* len, hio_de
static int dev_sck_readpending_bpf (hio_dev_t* dev) static int dev_sck_readpending_bpf (hio_dev_t* dev)
{ {
hio_dev_sck_t* rdev = (hio_dev_sck_t*)dev; hio_dev_sck_t* rdev = (hio_dev_sck_t*)dev;
bpf_state_t* st = (bpf_state_t*)rdev->bpf_state; bpf_state_t* st = (bpf_state_t*)rdev->u.bpf.state;
return st && st->pos < st->len; return st && st->pos < st->len;
} }
@@ -928,9 +934,9 @@ static int dev_sck_kill (hio_dev_t* dev, int force)
} }
#if defined(USE_BPF) #if defined(USE_BPF)
if (rdev->type == HIO_DEV_SCK_PACKET && rdev->u.bpf.state) if (sck_type_map[rdev->type].domain == __AF_BPF && rdev->u.bpf.state)
{ {
bpf_state_t* st = (bpf_state_t*)rdev->bpf_state; bpf_state_t* st = (bpf_state_t*)rdev->u.bpf.state;
if (st->buf) hio_freemem(hio, st->buf); if (st->buf) hio_freemem(hio, st->buf);
hio_freemem(hio, st); hio_freemem(hio, st);
rdev->u.bpf.state = HIO_NULL; rdev->u.bpf.state = HIO_NULL;
@@ -2114,7 +2120,7 @@ static int dev_sck_ioctl (hio_dev_t* dev, int cmd, void* arg)
} }
#if defined(USE_BPF) #if defined(USE_BPF)
if (rdev->bpf_state) if (sck_type_map[rdev->type].domain == __AF_BPF)
{ {
/* a bpf device attaches to an interface by name, and what the /* a bpf device attaches to an interface by name, and what the
* caller gave is the ifindex hio_skad_init_for_eth() puts in an * caller gave is the ifindex hio_skad_init_for_eth() puts in an
@@ -2314,6 +2320,11 @@ fcntl(rdev->hnd, F_SETFL, flags | O_NONBLOCK);
/* as i know it's connected already, /* as i know it's connected already,
* i don't schedule a connection timeout job */ * i don't schedule a connection timeout job */
/* the connection is up already, so there is no connect timeout
* to schedule. the field must still be marked unset: the ssl
* handshake inherits it as its deadline, and a zero there is a
* deadline already in the past. set it to a negative value */
HIO_INIT_NTIME(&rdev->tmout, -1, 0);
rdev->remoteaddr = conn->remoteaddr; rdev->remoteaddr = conn->remoteaddr;
#if defined(USE_SSL) #if defined(USE_SSL)
rdev->ssl_ctx = ssl_ctx; rdev->ssl_ctx = ssl_ctx;
@@ -3165,9 +3176,27 @@ static int dev_evcb_sck_on_read_qx (hio_dev_t* dev, const void* data, hio_iolen_
if (rdev->type == HIO_DEV_SCK_QX) if (rdev->type == HIO_DEV_SCK_QX)
{ {
#if defined(__BEOS__) || defined(__HAIKU__) #if defined(HIO_DEV_SCK_QX_STREAM)
const hio_uint8_t* p = (const hio_uint8_t*)data; const hio_uint8_t* p;
hio_oow_t rem = (hio_oow_t)dlen; hio_oow_t rem;
#else
hio_dev_sck_qxmsg_t* qxmsg;
#endif
if (dlen <= 0)
{
/* the writing end of the side channel is gone. a qx device carries
* no stream capability bit, so the loop has nothing to recognise
* end-of-file by and would keep reading a handle that stays
* readable with nothing on it. report it as a read failure and let
* the device be halted. */
hio_seterrbfmt(hio, HIO_EINVAL, "qx side channel closed");
return -1;
}
#if defined(HIO_DEV_SCK_QX_STREAM)
p = (const hio_uint8_t*)data;
rem = (hio_oow_t)dlen;
while (rem > 0) while (rem > 0)
{ {
@@ -3179,7 +3208,6 @@ static int dev_evcb_sck_on_read_qx (hio_dev_t* dev, const void* data, hio_iolen_
p += take; p += take;
rem -= take; rem -= take;
if (rdev->u.qx.qxacc_len < HIO_SIZEOF(rdev->u.qx.qxacc)) break; /* wait for more */ if (rdev->u.qx.qxacc_len < HIO_SIZEOF(rdev->u.qx.qxacc)) break; /* wait for more */
rdev->u.qx.qxacc_len = 0; rdev->u.qx.qxacc_len = 0;
@@ -3188,21 +3216,24 @@ static int dev_evcb_sck_on_read_qx (hio_dev_t* dev, const void* data, hio_iolen_
if (make_accepted_client_connection(rdev, rdev->u.qx.qxacc.syshnd, &rdev->u.qx.qxacc.remoteaddr, rdev->u.qx.qxacc.scktype) <= -1) if (make_accepted_client_connection(rdev, rdev->u.qx.qxacc.syshnd, &rdev->u.qx.qxacc.remoteaddr, rdev->u.qx.qxacc.scktype) <= -1)
{ {
/*printf ("unable to accept new client connection %d\n", qxmsg->syshnd);*/ /*printf ("unable to accept new client connection %d\n", qxmsg->syshnd);*/
return (rdev->state & HIO_DEV_SCK_LENIENT)? 0: -1; if (!(rdev->state & HIO_DEV_SCK_LENIENT)) return -1;
/* one read can carry several messages. the accumulator is
* empty again, so the framing is intact and the messages
* queued behind this one are still deliverable - returning
* here would throw them away along with their handles. */
} }
} }
else else
{ {
/* the stream is framed by size alone, so a bad command means /* the stream is framed by size alone, so a bad command means
* the two ends disagree about the message layout and nothing * the two ends disagree about the message layout and nothing
* after this point can be trusted to be a message boundary. */ * after this point can be trusted to be a message boundary.
* there is no resynchronising from that, so the device goes. */
hio_seterrbfmt(hio, HIO_EINVAL, "wrong qx command code"); hio_seterrbfmt(hio, HIO_EINVAL, "wrong qx command code");
return 0; return -1;
} }
} }
#else #else
hio_dev_sck_qxmsg_t* qxmsg;
if (dlen != HIO_SIZEOF(*qxmsg)) if (dlen != HIO_SIZEOF(*qxmsg))
{ {
hio_seterrbfmt(hio, HIO_EINVAL, "wrong qx packet size"); hio_seterrbfmt(hio, HIO_EINVAL, "wrong qx packet size");
@@ -3981,12 +4012,61 @@ int hio_dev_sck_sendfileok (hio_dev_sck_t* dev)
int hio_dev_sck_writetosidechan (hio_dev_sck_t* dev, const void* dptr, hio_oow_t dlen) int hio_dev_sck_writetosidechan (hio_dev_sck_t* dev, const void* dptr, hio_oow_t dlen)
{ {
if (dev->type == HIO_DEV_SCK_QX && write(dev->u.qx.side_chan, dptr, dlen) <= -1) if (dev->type != HIO_DEV_SCK_QX)
{
errno = EINVAL;
return -1;
}
#if defined(HIO_DEV_SCK_QX_STREAM)
/* the reader recovers message boundaries by counting octets, so a message
* left half-written would shift every message behind it and there is no
* recovering from that. a channel that has taken nothing yet is reported as
* a failure, the way a datagram channel reports a full socket buffer; once
* part of a message is out the channel is committed and the remainder is
* pushed out here. the reader drains this channel from the event loop, so
* the room it needs does come back. */
{
const hio_uint8_t* p = (const hio_uint8_t*)dptr;
hio_oow_t rem = dlen;
while (rem > 0)
{
ssize_t n = write(dev->u.qx.side_chan, p, rem);
if (n > 0)
{
p += n;
rem -= n;
}
else if (n == 0)
{
/* neither progress nor an error to wait on. it cannot happen on
* a socket, but looping on it would hang the caller. */
errno = EIO;
return -1;
}
else if (errno == EINTR ||
((errno == EAGAIN || errno == EWOULDBLOCK) && rem < dlen))
{
/* retry: interrupted, or out of room part-way through a message */
}
else
{
/* this doesn't set the error information on the main socket. if you may check errno, though */
/* TODO: make hio_seterrbfmt() thread safe and set the error information properly. still the caller may be in the thread-unsafe context */
return -1;
}
}
}
#else
if (write(dev->u.qx.side_chan, dptr, dlen) <= -1)
{ {
/* this doesn't set the error information on the main socket. if you may check errno, though */ /* this doesn't set the error information on the main socket. if you may check errno, though */
/* TODO: make hio_seterrbfmt() thread safe and set the error information properly. still the caller may be in the thread-unsafe context */ /* TODO: make hio_seterrbfmt() thread safe and set the error information properly. still the caller may be in the thread-unsafe context */
return -1; return -1;
} }
#endif
return 0; return 0;
} }
+14 -5
View File
@@ -646,9 +646,9 @@ int hio_sys_ctrlmux (hio_t* hio, hio_sys_mux_cmd_t cmd, hio_dev_t* dev, int dev_
if (x >= 0) if (x >= 0)
{ {
if (i_flag == EV_DISABLE && o_flag == EV_DISABLE) if (i_flag == EV_DISABLE && o_flag == EV_DISABLE)
dev->dev_cap &= ~HIO_DEV_CAP_WATCH_SUSPENDED;
else
dev->dev_cap |= HIO_DEV_CAP_WATCH_SUSPENDED; dev->dev_cap |= HIO_DEV_CAP_WATCH_SUSPENDED;
else
dev->dev_cap &= ~HIO_DEV_CAP_WATCH_SUSPENDED;
} }
break; break;
} }
@@ -876,6 +876,7 @@ int hio_sys_waitmux (hio_t* hio, const hio_ntime_t* tmout, hio_sys_mux_evtcb_t e
for (i = 0; i < nentries; i++) for (i = 0; i < nentries; i++)
{ {
int events = 0; int events = 0;
int rdhup = 0;
hio_dev_t* dev; hio_dev_t* dev;
dev = mux->revs[i].udata; dev = mux->revs[i].udata;
@@ -887,7 +888,11 @@ int hio_sys_waitmux (hio_t* hio, const hio_ntime_t* tmout, hio_sys_mux_evtcb_t e
if (mux->revs[i].ident != dev->dev_mth->getsyshnd(dev)) continue; /* already closed or something mid-loop? */ if (mux->revs[i].ident != dev->dev_mth->getsyshnd(dev)) continue; /* already closed or something mid-loop? */
if (mux->revs[i].flags & EV_ERROR) events |= HIO_DEV_EVENT_ERR; if (mux->revs[i].flags & EV_ERROR) events |= HIO_DEV_EVENT_ERR;
if (mux->revs[i].flags & EV_EOF) events |= HIO_DEV_EVENT_HUP; if (mux->revs[i].flags & EV_EOF)
{
if (mux->revs[i].filter == EVFILT_READ) rdhup = 1;
else events |= HIO_DEV_EVENT_HUP;
}
if (mux->revs[i].filter == EVFILT_READ) events |= HIO_DEV_EVENT_IN; if (mux->revs[i].filter == EVFILT_READ) events |= HIO_DEV_EVENT_IN;
else if (mux->revs[i].filter == EVFILT_WRITE) events |= HIO_DEV_EVENT_OUT; else if (mux->revs[i].filter == EVFILT_WRITE) events |= HIO_DEV_EVENT_OUT;
#else #else
@@ -903,13 +908,17 @@ int hio_sys_waitmux (hio_t* hio, const hio_ntime_t* tmout, hio_sys_mux_evtcb_t e
{ {
if (mux->revs[j].udata != dev) continue; if (mux->revs[j].udata != dev) continue;
if (mux->revs[j].flags & EV_ERROR) events |= HIO_DEV_EVENT_ERR; if (mux->revs[j].flags & EV_ERROR) events |= HIO_DEV_EVENT_ERR;
if (mux->revs[j].flags & EV_EOF) events |= HIO_DEV_EVENT_HUP; if (mux->revs[j].flags & EV_EOF)
{
if (mux->revs[j].filter == EVFILT_READ) rdhup = 1;
else events |= HIO_DEV_EVENT_HUP;
}
if (mux->revs[j].filter == EVFILT_READ) events |= HIO_DEV_EVENT_IN; if (mux->revs[j].filter == EVFILT_READ) events |= HIO_DEV_EVENT_IN;
else if (mux->revs[j].filter == EVFILT_WRITE) events |= HIO_DEV_EVENT_OUT; else if (mux->revs[j].filter == EVFILT_WRITE) events |= HIO_DEV_EVENT_OUT;
} }
#endif #endif
if (HIO_LIKELY(events)) event_handler(hio, dev, events, 0); if (HIO_LIKELY(events)) event_handler(hio, dev, events, rdhup);
} }
else if (mux->ctrlp[0] != HIO_SYSHND_INVALID) else if (mux->ctrlp[0] != HIO_SYSHND_INVALID)
{ {
+2 -1
View File
@@ -121,7 +121,8 @@ test_pxy()
# and the bytes themselves, not just the count. the upstream emits a # and the bytes themselves, not just the count. the upstream emits a
# repeating a-z pattern keyed to the offset. # repeating a-z pattern keyed to the offset.
local sum=$(curl -s -m 60 "http://${SRVADDR}/pxy/big" | cksum | cut -d' ' -f1) local sum=$(curl -s -m 60 "http://${SRVADDR}/pxy/big" | cksum | cut -d' ' -f1)
local want=$(perl -e 'print map { chr(97 + ($_ % 26)) } 0 .. (8*1024*1024 - 1)' 2>/dev/null | cksum | cut -d' ' -f1) ##local want=$(perl -e 'print map { chr(97 + ($_ % 26)) } 0 .. (8*1024*1024 - 1)' 2>/dev/null | cksum | cut -d' ' -f1)
local want=$(yes abcdefghijklmnopqrstuvwxyz | tr -d '\n' | head -c 8388608 | cksum | cut -d' ' -f1)
if [ -n "$want" ]; then if [ -n "$want" ]; then
tap_ensure "$sum" "$want" "$msg - the relayed bytes are identical to the upstream's" tap_ensure "$sum" "$want" "$msg - the relayed bytes are identical to the upstream's"
else else