/* * Event callback for asynchronous connection attempt. */ staticvoid evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg) { structevhttp_connection *evcon = arg; int error; ev_socklen_t errsz = sizeof(error);
if (evcon->fd == -1) evcon->fd = bufferevent_getfd(bufev);
if (!(what & BEV_EVENT_CONNECTED)) { /* some operating systems return ECONNREFUSED immediately * when connecting to a local address. the cleanup is going * to reschedule this function call. */ #ifndef _WIN32 if (errno == ECONNREFUSED) goto cleanup; #endif evhttp_error_cb(bufev, what, arg); return; }
if (evcon->fd == -1) { event_debug(("%s: bufferevent_getfd returned -1", __func__)); goto cleanup; }
/* Check if the connection completed */ if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error, &errsz) == -1) { event_debug(("%s: getsockopt for \"%s:%d\" on "EV_SOCK_FMT, __func__, evcon->address, evcon->port, EV_SOCK_ARG(evcon->fd))); goto cleanup; }
if (error) { event_debug(("%s: connect failed for \"%s:%d\" on " EV_SOCK_FMT": %s", __func__, evcon->address, evcon->port, EV_SOCK_ARG(evcon->fd), evutil_socket_error_to_string(error))); goto cleanup; }
/* We are connected to the server now */ event_debug(("%s: connected to \"%s:%d\" on "EV_SOCK_FMT"\n", __func__, evcon->address, evcon->port, EV_SOCK_ARG(evcon->fd)));
/* Reset the retry count as we were successful in connecting */ evcon->retry_cnt = 0; evcon->state = EVCON_IDLE;
/* reset the bufferevent cbs */ bufferevent_setcb(evcon->bufev, evhttp_read_cb, evhttp_write_cb, evhttp_error_cb, evcon);
if (evcon->bind_address || evcon->bind_port) { evcon->fd = bind_socket( evcon->bind_address, evcon->bind_port, 0/*reuse*/); if (evcon->fd == -1) { event_debug(("%s: failed to bind to \"%s\"", __func__, evcon->bind_address)); return (-1); }
if (bufferevent_setfd(evcon->bufev, evcon->fd)) return (-1); } else { if (bufferevent_setfd(evcon->bufev, -1)) return (-1); }
/* Set up a callback for successful connection setup */ bufferevent_setcb(evcon->bufev, NULL/* evhttp_read_cb */, NULL/* evhttp_write_cb */, evhttp_connection_cb, evcon); if (!evutil_timerisset(&evcon->timeout)) { conststructtimeval conn_tv = { HTTP_CONNECT_TIMEOUT, 0 }; bufferevent_set_timeouts(evcon->bufev, &conn_tv, &conn_tv); } else { bufferevent_set_timeouts(evcon->bufev, &evcon->timeout, &evcon->timeout); } /* make sure that we get a write callback */ if (bufferevent_enable(evcon->bufev, EV_WRITE)) return (-1);
evcon->state = EVCON_CONNECTING;
if (evcon->flags & EVHTTP_CON_REUSE_CONNECTED_ADDR && sa && (sa->sa_family == AF_INET || sa->sa_family == AF_INET6)) { int socklen = sizeof(struct sockaddr_in); if (sa->sa_family == AF_INET6) { socklen = sizeof(struct sockaddr_in6); } ret = bufferevent_socket_connect(evcon->bufev, sa, socklen); } else { ret = bufferevent_socket_connect_hostname(evcon->bufev, evcon->dns_base, evcon->ai_family, address, evcon->port); }
if (ret < 0) { evcon->state = old_state; event_sock_warn(evcon->fd, "%s: connection to \"%s\" failed", __func__, evcon->address); /* some operating systems return ECONNREFUSED immediately * when connecting to a local address. the cleanup is going * to reschedule this function call. */ evhttp_connection_cb_cleanup(evcon); return (0); }