/** Connect to Tomcat * @param addr address to connect to * @param keepalive should we set SO_KEEPALIVE (if !=0) * @param timeout connect timeout in seconds * (<=0: no timeout=blocking) * @param sock_buf size of send and recv buffer * (<=0: use default) * @param l logger * @return JK_INVALID_SOCKET: some kind of error occured * created socket: success * @remark Cares about errno */ jk_sock_t jk_open_socket(jk_sockaddr_t *addr, jk_sockaddr_t *source, int keepalive, int timeout, int connect_timeout, int sock_buf, jk_logger_t *l) { char buf[DUMP_SINFO_BUF_SZ]; jk_sock_t sd; int set = 1; int ret = 0; int flags = 0; #ifdef SO_LINGER struct linger li; #endif JK_TRACE_ENTER(l); errno = 0; #if defined(SOCK_CLOEXEC) && defined(USE_SOCK_CLOEXEC) flags |= SOCK_CLOEXEC; #endif sd = socket(addr->family, SOCK_STREAM | flags, 0); if (!IS_VALID_SOCKET(sd)) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "socket() failed (errno=%d)", errno); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } #if defined(FD_CLOEXEC) && !defined(USE_SOCK_CLOEXEC) if ((flags = fcntl(sd, F_GETFD)) == -1) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "fcntl() failed (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } flags |= FD_CLOEXEC; if (fcntl(sd, F_SETFD, flags) == -1) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "fcntl() failed (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } #endif /* Disable Nagle algorithm */ if (setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (SET_TYPE)&set, sizeof(set))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting TCP_NODELAY (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "socket TCP_NODELAY set to On"); if (keepalive) { #if defined(WIN32) && !defined(NETWARE) DWORD dw; struct tcp_keepalive ka = { 0 }, ks = { 0 }; if (timeout) ka.keepalivetime = timeout * 10000; else ka.keepalivetime = 60 * 10000; /* 10 minutes */ ka.keepaliveinterval = 1000; ka.onoff = 1; if (WSAIoctl(sd, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), &ks, sizeof(ks), &dw, NULL, NULL)) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SIO_KEEPALIVE_VALS (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "socket SO_KEEPALIVE set to %d seconds", ka.keepalivetime / 1000); #else set = 1; if (setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, (SET_TYPE)&set, sizeof(set))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SO_KEEPALIVE (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "socket SO_KEEPALIVE set to On"); #endif } if (sock_buf > 0) { set = sock_buf; /* Set socket send buffer size */ if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, (SET_TYPE)&set, sizeof(set))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SO_SNDBUF (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } set = sock_buf; /* Set socket receive buffer size */ if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, (SET_TYPE)&set, sizeof(set))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SO_RCVBUF (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "socket SO_SNDBUF and SO_RCVBUF set to %d", sock_buf); } if (timeout > 0) { #if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) int tmout = timeout * 1000; setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tmout, sizeof(int)); setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, (const char *) &tmout, sizeof(int)); JK_GET_SOCKET_ERRNO(); #elif defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO) struct timeval tv; tv.tv_sec = timeout; tv.tv_usec = 0; setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (const void *) &tv, sizeof(tv)); setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, (const void *) &tv, sizeof(tv)); #endif if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "timeout %d set for socket=%d", timeout, sd); } #ifdef SO_NOSIGPIPE /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when * sending data to a dead peer. Possibly also existing and in use on other BSD * systems? */ set = 1; if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (const char *)&set, sizeof(int))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SO_NOSIGPIPE (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } #endif #ifdef SO_LINGER /* Make hard closesocket by disabling lingering */ li.l_linger = li.l_onoff = 0; if (setsockopt(sd, SOL_SOCKET, SO_LINGER, (SET_TYPE)&li, sizeof(li))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SO_LINGER (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } #endif /* Tries to connect to Tomcat (continues trying while error is EINTR) */ if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "trying to connect socket %d to %s", sd, jk_dump_hinfo(addr, buf, sizeof(buf))); /* Need more infos for BSD 4.4 and Unix 98 defines, for now only iSeries when Unix98 is required at compile time */ #if (_XOPEN_SOURCE >= 520) && defined(AS400) ((struct sockaddr *)addr)->sa.sin.sa_len = sizeof(struct sockaddr_in); #endif ret = nb_connect(sd, addr, source, connect_timeout, l); #if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) if (JK_IS_SOCKET_ERROR(ret)) { JK_GET_SOCKET_ERRNO(); } #endif /* WIN32 */ /* Check if we are connected */ if (ret) { jk_log(l, JK_LOG_INFO, "connect to %s failed (errno=%d)", jk_dump_hinfo(addr, buf, sizeof(buf)), errno); jk_close_socket(sd, l); sd = JK_INVALID_SOCKET; } else { if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "socket %d [%s] connected", sd, jk_dump_sinfo(sd, buf, sizeof(buf))); } JK_TRACE_EXIT(l); return sd; }
/** Drain and close the socket * @param sd socket to close * @param l logger * @return -1: socket to close is invalid * -1: some kind of error occured (!WIN32) * SOCKET_ERROR: some kind of error occured (WIN32) * 0: success * @remark Does not change errno */ int jk_shutdown_socket(jk_sock_t sd, jk_logger_t *l) { char dummy[512]; char buf[DUMP_SINFO_BUF_SZ]; char *sb = NULL; int rc = 0; size_t rd = 0; size_t rp = 0; int save_errno; int timeout = MS_TO_LINGER; time_t start = time(NULL); JK_TRACE_ENTER(l); if (!IS_VALID_SOCKET(sd)) { JK_TRACE_EXIT(l); return -1; } save_errno = errno; if (JK_IS_DEBUG_LEVEL(l)) { sb = jk_dump_sinfo(sd, buf, sizeof(buf)); jk_log(l, JK_LOG_DEBUG, "About to shutdown socket %d [%s]", sd, sb); } /* Shut down the socket for write, which will send a FIN * to the peer. */ if (shutdown(sd, SHUT_WR)) { rc = jk_close_socket(sd, l); if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "Failed sending SHUT_WR for socket %d [%s]", sd, sb); errno = save_errno; JK_TRACE_EXIT(l); return rc; } do { rp = 0; if (jk_is_input_event(sd, timeout, l)) { /* Do a restartable read on the socket * draining out all the data currently in the socket buffer. */ int num = 0; do { num++; #if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) rc = recv(sd, &dummy[0], sizeof(dummy), 0); if (JK_IS_SOCKET_ERROR(rc)) JK_GET_SOCKET_ERRNO(); #else rc = read(sd, &dummy[0], sizeof(dummy)); #endif if (rc > 0) rp += rc; } while (JK_IS_SOCKET_ERROR(rc) && (errno == EINTR || errno == EAGAIN) && num < MAX_READ_RETRY); if (rc < 0) { /* Read failed. * Bail out from the loop. */ break; } } else { /* Error or timeout (reason is logged within jk_is_input_event) * Exit the drain loop */ break; } rd += rp; if (rp < sizeof(dummy)) { if (timeout > MS_TO_LINGER_LAST) { /* Try one last time with a short timeout */ timeout = MS_TO_LINGER_LAST; continue; } /* We have read less then size of buffer * It's a good chance there will be no more data * to read. */ if ((rc = sononblock(sd))) { rc = jk_close_socket(sd, l); if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "error setting socket %d [%s] to nonblocking", sd, sb); errno = save_errno; JK_TRACE_EXIT(l); return rc; } if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "shutting down the read side of socket %d [%s]", sd, sb); shutdown(sd, SHUT_RD); break; } timeout = MS_TO_LINGER; } while ((rd < MAX_LINGER_BYTES) && (difftime(time(NULL), start) < MAX_SECS_TO_LINGER)); rc = jk_close_socket(sd, l); if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "Shutdown socket %d [%s] and read %d lingering bytes in %d sec.", sd, sb, rd, (int)difftime(time(NULL), start)); errno = save_errno; JK_TRACE_EXIT(l); return rc; }
int jk_open_socket(struct sockaddr_in *addr, int ndelay, int keepalive, jk_logger_t *l) { int sock; jk_log(l, JK_LOG_DEBUG, "Into jk_open_socket\n"); sock = socket(AF_INET, SOCK_STREAM, 0); if(sock > -1) { int ret; /* Tries to connect to JServ (continues trying while error is EINTR) */ do { jk_log(l, JK_LOG_DEBUG, "jk_open_socket, try to connect socket = %d\n", sock); ret = connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); #if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) if(SOCKET_ERROR == ret) { errno = WSAGetLastError() - WSABASEERR; } #endif /* WIN32 */ jk_log(l, JK_LOG_DEBUG, "jk_open_socket, after connect ret = %d\n", ret); } while (-1 == ret && EINTR == errno); /* Check if we connected */ if(0 == ret) { int keep = 1; if(ndelay) { int set = 1; jk_log(l, JK_LOG_DEBUG, "jk_open_socket, set TCP_NODELAY to on\n"); setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&set, sizeof(set)); } if (keepalive) { jk_log(l, JK_LOG_DEBUG, "jk_open_socket, set SO_KEEPALIVE to on\n"); setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&keep, sizeof(keep)); } jk_log(l, JK_LOG_DEBUG, "jk_open_socket, return, sd = %d\n", sock); return sock; } jk_log(l, JK_LOG_INFO, "jk_open_socket, connect() failed errno = %d\n", errno); jk_close_socket(sock); } else { #if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) errno = WSAGetLastError() - WSABASEERR; #endif /* WIN32 */ jk_log(l, JK_LOG_ERROR, "jk_open_socket, socket() failed errno = %d\n", errno); } return -1; }