static void C_connect(lcbio_CONNSTART *cs) { int rv; lcbio_SOCKET *s = cs->sock; int retry_once = 0; lcbio_CSERR status; lcbio_TABLE *io = s->io; GT_NEXTSOCK: if (ensure_sock(cs) != 0) { lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); cs_state_signal(cs, CS_ERROR, LCB_CONNECT_ERROR); return; } GT_CONNECT: rv = IOT_V1(io).connect(IOT_ARG(io), s->u.sd, cs->ai->ai_addr, (unsigned)cs->ai->ai_addrlen, C_conncb); if (rv == 0) { lcbio_ref(s); return; } lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); status = lcbio_mkcserr(IOT_ERRNO(io)); switch (status) { case LCBIO_CSERR_INTR: goto GT_CONNECT; case LCBIO_CSERR_CONNECTED: cs_state_signal(cs, CS_CONNECTED, LCB_SUCCESS); return; case LCBIO_CSERR_BUSY: return; case LCBIO_CSERR_EINVAL: if (!retry_once) { retry_once = 1; goto GT_CONNECT; } /* fallthrough */ case LCBIO_CSERR_EFAIL: default: destroy_cursock(cs); goto GT_NEXTSOCK; } }
lcb_sockdata_t * lcbio_C_ai2sock(lcbio_TABLE *io, struct addrinfo **ai, int *connerr) { lcb_sockdata_t *ret = NULL; for (; *ai; *ai = (*ai)->ai_next) { ret = IOT_V1(io).socket( IOT_ARG(io), (*ai)->ai_family, (*ai)->ai_socktype, (*ai)->ai_protocol); if (ret) { return ret; } else { *connerr = IOT_ERRNO(io); } } return ret; }
static int C_put_ex(lcbio_CTX *ctx, lcb_IOV *iov, unsigned niov, unsigned nb) { lcbio_TABLE *iot = ctx->io; lcb_sockdata_t *sd = CTX_SD(ctx); int status = IOT_V1(iot).write2(IOT_ARG(iot), sd, iov, niov, (void *)(uintptr_t)nb, Cw_ex_handler); if (status) { /** error! */ lcbio_OSERR saverr = IOT_ERRNO(iot); ctx->procs.cb_flush_done(ctx, nb, nb); lcbio_ctx_senderr(ctx, lcbio_mklcberr(saverr, ctx->sock->settings)); return 0; } else { ctx->npending++; return 1; } }
static int ensure_sock(lcbio_CONNSTART *cs) { lcbio_SOCKET *s = cs->sock; lcbio_TABLE *io = s->io; int errtmp = 0; if (cs->ai == NULL) { return -1; } if (IOT_IS_EVENT(io)) { if (s->u.fd != INVALID_SOCKET) { /* already have one? */ return 0; } while (s->u.fd == INVALID_SOCKET && cs->ai != NULL) { s->u.fd = lcbio_E_ai2sock(io, &cs->ai, &errtmp); if (s->u.fd != INVALID_SOCKET) { return 0; } } } else { if (s->u.sd) { return 0; } while (s->u.sd == NULL && cs->ai != NULL) { s->u.sd = lcbio_C_ai2sock(io, &cs->ai, &errtmp); if (s->u.sd) { s->u.sd->lcbconn = (void *) cs->sock; s->u.sd->parent = IOT_ARG(io); return 0; } } } if (cs->ai == NULL) { lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); return -1; } return 0; }
/** Extended function used for write-on-callback mode */ static int E_put_ex(lcbio_CTX *ctx, lcb_IOV *iov, unsigned niov, unsigned nb) { lcb_ssize_t nw; lcbio_TABLE *iot = ctx->io; lcb_socket_t fd = CTX_FD(ctx); GT_WRITE_AGAIN: nw = IOT_V0IO(iot).sendv(IOT_ARG(iot), fd, iov, niov); if (nw > 0) { ctx->procs.cb_flush_done(ctx, nb, nw); return 1; } else if (nw == -1) { switch (IOT_ERRNO(iot)) { case EINTR: /* jump back to retry */ goto GT_WRITE_AGAIN; case C_EAGAIN: case EWOULDBLOCK: nw = 0; /* indicate zero bytes were written, but don't send an error */ goto GT_WRITE0; default: /* pretend all the bytes were written and deliver an error during * the next event loop iteration. */ nw = nb; lcbio_ctx_senderr(ctx, convert_lcberr(ctx, LCBIO_IOERR)); goto GT_WRITE0; } } else { /* connection closed. pretend everything was written and send an error */ nw = nb; lcbio_ctx_senderr(ctx, convert_lcberr(ctx, LCBIO_SHUTDOWN)); goto GT_WRITE0; } GT_WRITE0: ctx->procs.cb_flush_done(ctx, nb, nw); return 0; }
lcb_socket_t lcbio_E_ai2sock(lcbio_TABLE *io, struct addrinfo **ai, int *connerr) { lcb_socket_t ret = INVALID_SOCKET; *connerr = 0; for (; *ai; *ai = (*ai)->ai_next) { ret = IOT_V0IO(io).socket0( IOT_ARG(io), (*ai)->ai_family, (*ai)->ai_socktype, (*ai)->ai_protocol); if (ret != INVALID_SOCKET) { return ret; } else { *connerr = IOT_ERRNO(io); } } return ret; }
static lcb_error_t convert_lcberr(const lcbio_CTX *ctx, lcbio_IOSTATUS status) { const lcb_settings *settings = ctx->sock->settings; lcbio_OSERR oserr = IOT_ERRNO(ctx->sock->io); if (lcbio_ssl_check(ctx->sock)) { lcb_error_t err = lcbio_ssl_get_error(ctx->sock); if (err) { return err; } } if (status == LCBIO_SHUTDOWN) { return lcbio_mklcberr(0, settings); } else if (oserr != 0) { return lcbio_mklcberr(oserr, settings); } else { return LCB_NETWORK_ERROR; } }
static void C_conncb(lcb_sockdata_t *sock, int status) { lcbio_SOCKET *s = (void *)sock->lcbconn; lcbio_CONNSTART *cs = (void *)s->ctx; lcb_log(LOGARGS(s, TRACE), CSLOGFMT "Received completion handler. Status=%d. errno=%d", CSLOGID(s), status, IOT_ERRNO(s->io)); if (!--s->refcount) { lcbio__destroy(s); return; } if (!status) { if (cs->state == CS_PENDING) { cs->state = CS_CONNECTED; } cs_handler(cs); } else { lcbio_mksyserr(IOT_ERRNO(s->io), &cs->syserr); destroy_cursock(cs); C_connect(cs); } }
static void E_connect(lcb_socket_t sock, short events, void *arg) { lcbio_CONNSTART *cs = arg; lcbio_SOCKET *s = cs->sock; lcbio_TABLE *io = s->io; int retry_once = 0; lcbio_CSERR connstatus; (void)sock; lcb_log(LOGARGS(s, TRACE), CSLOGFMT "Got event handler for new connection", CSLOGID(s)); GT_NEXTSOCK: if (ensure_sock(cs) == -1) { cs_state_signal(cs, CS_ERROR, LCB_CONNECT_ERROR); return; } if (events & LCB_ERROR_EVENT) { socklen_t errlen = sizeof(int); int sockerr = 0; lcb_log(LOGARGS(s, TRACE), CSLOGFMT "Received ERROR_EVENT", CSLOGID(s)); getsockopt(s->u.fd, SOL_SOCKET, SO_ERROR, (char *)&sockerr, &errlen); lcbio_mksyserr(sockerr, &cs->syserr); destroy_cursock(cs); goto GT_NEXTSOCK; } else { int rv = 0; struct addrinfo *ai = cs->ai; GT_CONNECT: rv = IOT_V0IO(io).connect0( IOT_ARG(io), s->u.fd, ai->ai_addr, (unsigned)ai->ai_addrlen); if (rv == 0) { cs_unwatch(cs); cs_state_signal(cs, CS_CONNECTED, LCB_SUCCESS); return; } } connstatus = lcbio_mkcserr(IOT_ERRNO(io)); lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); switch (connstatus) { case LCBIO_CSERR_INTR: goto GT_CONNECT; case LCBIO_CSERR_CONNECTED: cs_unwatch(cs); cs_state_signal(cs, CS_CONNECTED, LCB_SUCCESS); return; case LCBIO_CSERR_BUSY: lcb_log(LOGARGS(s, TRACE), CSLOGFMT "Scheduling asynchronous watch for socket.", CSLOGID(s)); IOT_V0EV(io).watch( IOT_ARG(io), s->u.fd, cs->event, LCB_WRITE_EVENT, cs, E_connect); cs->ev_active = 1; return; case LCBIO_CSERR_EINVAL: if (!retry_once) { retry_once = 1; goto GT_CONNECT; } /* fallthrough */ case LCBIO_CSERR_EFAIL: default: /* close the current socket and try again */ lcb_log(LOGARGS(s, TRACE), CSLOGFMT "connect() failed. os_error=%d [%s]", CSLOGID(s), IOT_ERRNO(io), strerror(IOT_ERRNO(io))); destroy_cursock(cs); goto GT_NEXTSOCK; } }