EIO_Status CListeningSocket::Accept(CSocket*&       sock,
                                    const STimeout* timeout,
                                    TSOCK_Flags     flags) const
{
    if ( !m_Socket ) {
        sock = 0;
        return eIO_Closed;
    }

    SOCK       x_sock;
    EIO_Status status;
    status = LSOCK_AcceptEx(m_Socket, timeout, &x_sock, flags);
    assert(!x_sock ^ !(status != eIO_Success));
    if (status == eIO_Success) {
        try {
            sock = new CSocket;
        } catch (...) {
            sock = 0;
            SOCK_Abort(x_sock);
            SOCK_Close(x_sock);
            throw;
        }
        sock->Reset(x_sock, eTakeOwnership, eCopyTimeoutsToSOCK);
    } else
        sock = 0;
    return status;
}
/* Prepare connector for reading. Open socket if necessary and
 * make initial connect and send, re-trying if possible until success.
 * Return codes:
 *   eIO_Success = success, connector is ready for reading (uuu->sock != NULL);
 *   eIO_Timeout = maybe (check uuu->sock) connected and no data yet available;
 *   other code  = error, not connected (uuu->sock == NULL).
 */
static EIO_Status s_PreRead(SHttpConnector* uuu,
                            const STimeout* timeout,
                            EReadMode       read_mode)
{
    EIO_Status status;
    char* retry;

    for (;;) {
        status = s_ConnectAndSend(uuu, read_mode);
        if (!uuu->sock) {
            assert(status != eIO_Success);
            break;
        }
        if (status != eIO_Success) {
            if (status != eIO_Timeout  ||
                status == SOCK_Status(uuu->sock, eIO_Read)/*pending*/)
                break;
        }

        /* set timeout */
        SOCK_SetTimeout(uuu->sock, eIO_Read, timeout);

        if (!uuu->read_header)
            break;

        if ((status = s_ReadHeader(uuu, &retry, read_mode)) == eIO_Success) {
            size_t w_size = BUF_Size(uuu->w_buf);
            assert(!uuu->read_header  &&  !retry);
            /* pending output data no longer needed */
            if (BUF_Read(uuu->w_buf, 0, w_size) != w_size) {
                CORE_LOG_X(15, eLOG_Error,
                           "[HTTP]  Cannot discard output buffer");
                assert(0);
            }
            break;
        }
        /* if polling then bail out with eIO_Timeout */
        if(status == eIO_Timeout && timeout && !(timeout->sec | timeout->usec))
            break;

        /* HTTP header read error; disconnect and try to use another server */
        SOCK_Abort(uuu->sock);
        s_DropConnection(uuu, 0/*no wait*/);
        if (!s_Adjust(uuu, &retry, read_mode)) {
            uuu->can_connect = eCC_None;
            break;
        }
    }

    return status;
}
/* Connect to the server specified by uuu->net_info, then compose and form
 * relevant HTTP header, and flush the accumulated output data(uuu->w_buf)
 * after the HTTP header. If connection/write unsuccessful, retry to reconnect
 * and send the data again until permitted by s_Adjust().
 */
static EIO_Status s_ConnectAndSend(SHttpConnector* uuu,
                                   EReadMode       read_mode)
{
    EIO_Status status;

    for (;;) {
        char* null = 0;

        if (!uuu->sock) {
            if ((status = s_Connect(uuu, read_mode)) != eIO_Success)
                break;
            assert(uuu->sock);
            uuu->read_header = 1/*true*/;
            uuu->shut_down = 0/*false*/;
            uuu->expected = 0;
            uuu->received = 0;
            uuu->code = 0;
        } else
            status = eIO_Success;

        if (uuu->w_len) {
            size_t off = BUF_Size(uuu->w_buf) - uuu->w_len;

            SOCK_SetTimeout(uuu->sock, eIO_Write, uuu->w_timeout);
            do {
                char   buf[4096];
                size_t n_written;
                size_t n_write = BUF_PeekAt(uuu->w_buf, off, buf, sizeof(buf));
                status = SOCK_Write(uuu->sock, buf, n_write,
                                    &n_written, eIO_WritePlain);
                if (status != eIO_Success)
                    break;
                uuu->w_len -= n_written;
                off        += n_written;
            } while (uuu->w_len);
        } else if (!uuu->shut_down)
            status = SOCK_Write(uuu->sock, 0, 0, 0, eIO_WritePlain);

        if (status == eIO_Success) {
            assert(uuu->w_len == 0);
            if (!uuu->shut_down) {
                /* 10/07/03: While this call here is perfectly legal, it could
                 * cause connection severed by a buggy CISCO load-balancer. */
                /* 10/28/03: CISCO's beta patch for their LB shows that the
                 * problem has been fixed; no more 2'30" drops in connections
                 * that shut down for write.  We still leave this commented
                 * out to allow unpatched clients to work seamlessly... */ 
                /*SOCK_Shutdown(uuu->sock, eIO_Write);*/
                uuu->shut_down = 1;
            }
            break;
        }

        if (status == eIO_Timeout  &&  uuu->w_timeout  &&
            !(uuu->w_timeout->sec | uuu->w_timeout->usec)) {
            break;
        }
        CORE_LOGF_X(6, eLOG_Error,
                    ("[HTTP]  Error writing body at offset %lu (%s)",
                     (unsigned long)(BUF_Size(uuu->w_buf) - uuu->w_len),
                     IO_StatusStr(status)));

        /* write failed; close and try to use another server */
        SOCK_Abort(uuu->sock);
        s_DropConnection(uuu, 0/*no wait*/);
        if (!s_Adjust(uuu, &null, read_mode)) {
            uuu->can_connect = eCC_None;
            status = eIO_Closed;
            break;
        }
    }

    return status;
}
Beispiel #4
0
static CConn_IOStream::TConn_Pair
s_SocketConnectorBuilder(const SConnNetInfo* net_info,
                         const STimeout*     timeout,
                         const void*         data,
                         size_t              size,
                         TSOCK_Flags         flags)
{
    EIO_Status status = eIO_Success;
    bool       proxy = false;
    SOCK       sock = 0;

    _ASSERT(net_info);
    if ((flags & (fSOCK_LogOn | fSOCK_LogDefault)) == fSOCK_LogDefault
        &&  net_info->debug_printout == eDebugPrintout_Data) {
        flags &= ~fSOCK_LogDefault;
        flags |=  fSOCK_LogOn;
    }
    if (*net_info->http_proxy_host  &&  net_info->http_proxy_port) {
        status = HTTP_CreateTunnel(net_info, fHTTP_NoAutoRetry, &sock);
        _ASSERT(!sock ^ !(status != eIO_Success));
        if (status == eIO_Success
            &&  ((flags & ~(fSOCK_LogOn | fSOCK_LogDefault))  ||  size)) {
            SOCK s;
            status = SOCK_CreateOnTopEx(sock, 0, &s,
                                        data, size, flags);
            _ASSERT(!s ^ !(status != eIO_Success));
            SOCK_Destroy(sock);
            sock = s;
        }
        proxy = true;
    }
    if (!sock  &&  (!proxy  ||  net_info->http_proxy_leak)) {
        const char* host = (net_info->firewall  &&  *net_info->proxy_host
                            ? net_info->proxy_host : net_info->host);
        if (timeout == kDefaultTimeout)
            timeout  = net_info->timeout;
        if (!proxy  &&  net_info->debug_printout) {
            SConnNetInfo* x_net_info = ConnNetInfo_Clone(net_info);
            if (x_net_info) {
                x_net_info->req_method = eReqMethod_Any;
                x_net_info->stateless = 0;
                x_net_info->lb_disable = 0;
                x_net_info->http_proxy_host[0] = '\0';
                x_net_info->http_proxy_port    =   0;
                x_net_info->http_proxy_user[0] = '\0';
                x_net_info->http_proxy_pass[0] = '\0';
                x_net_info->proxy_host[0]      = '\0';
                ConnNetInfo_SetUserHeader(x_net_info, 0);
                if (x_net_info->http_referer) {
                    free((void*) x_net_info->http_referer);
                    x_net_info->http_referer = 0;
                }
                x_net_info->timeout = timeout;
            }
            ConnNetInfo_Log(x_net_info, eLOG_Note, CORE_GetLOG());
            ConnNetInfo_Destroy(x_net_info);
        }
        status = SOCK_CreateEx(host, net_info->port, timeout, &sock,
                               data, size, flags);
        _ASSERT(!sock ^ !(status != eIO_Success));
    }
    string hostport(net_info->host);
    hostport += ':';
    hostport += NStr::UIntToString(net_info->port);
    CONNECTOR c = SOCK_CreateConnectorOnTopEx(sock,
                                              1/*own*/,
                                              hostport.c_str());
    if (!c) {
        SOCK_Abort(sock);
        SOCK_Close(sock);
        status = eIO_Unknown;
    }
    return CConn_IOStream::TConn_Pair(c, status);
}