void CSocket::Reset(SOCK sock, EOwnership if_to_own, ECopyTimeout whence)
{
    if (m_Socket  &&  m_IsOwned != eNoOwnership)
        SOCK_Close(m_Socket);
    m_Socket  = sock;
    m_IsOwned = if_to_own;
    if (whence == eCopyTimeoutsFromSOCK) {
        if ( sock ) {
            const STimeout* timeout;
            timeout = SOCK_GetTimeout(sock, eIO_Read);
            if ( timeout ) {
                rr_timeout = *timeout;
                r_timeout  = &rr_timeout;
            } else
                r_timeout  = 0;
            timeout = SOCK_GetTimeout(sock, eIO_Write);
            if ( timeout ) {
                ww_timeout = *timeout;
                w_timeout  = &ww_timeout;
            } else
                w_timeout  = 0;
            timeout = SOCK_GetTimeout(sock, eIO_Close);
            if ( timeout ) {
                cc_timeout = *timeout;
                c_timeout  = &cc_timeout;
            } else
                c_timeout  = 0;
        } else
            r_timeout = w_timeout = c_timeout = 0;
    } else if ( sock ) {
        SOCK_SetTimeout(sock, eIO_Read,  r_timeout);
        SOCK_SetTimeout(sock, eIO_Write, w_timeout);
        SOCK_SetTimeout(sock, eIO_Close, c_timeout);
    }
}
/* Parse HTTP header */
static EIO_Status s_ReadHeader(SHttpConnector* uuu,
                               char**          retry,
                               EReadMode       read_mode)
{
    ERetry     redirect = eRetry_None; 
    int        server_error = 0;
    int        http_status;
    EIO_Status status;
    char*      header;
    size_t     size;

    assert(uuu->sock  &&  uuu->read_header);
    *retry = 0;

    /* line by line HTTP header input */
    for (;;) {
        /* do we have full header yet? */
        size = BUF_Size(uuu->http);
        if (!(header = (char*) malloc(size + 1))) {
            CORE_LOGF_X(7, eLOG_Error,
                        ("[HTTP]  Cannot allocate header, %lu bytes",
                         (unsigned long) size));
            return eIO_Unknown;
        }
        verify(BUF_Peek(uuu->http, header, size) == size);
        header[size] = '\0';
        if (size >= 4  &&  strcmp(&header[size - 4], "\r\n\r\n") == 0)
            break/*full header captured*/;
        free(header);

        status = SOCK_StripToPattern(uuu->sock, "\r\n", 2, &uuu->http, 0);
        if (status != eIO_Success) {
            ELOG_Level level;
            if (status == eIO_Timeout) {
                const STimeout* tmo = SOCK_GetTimeout(uuu->sock, eIO_Read);
                if (!tmo)
                    level = eLOG_Error;
                else if (tmo->sec | tmo->usec)
                    level = eLOG_Warning;
                else if (read_mode != eRM_WaitCalled)
                    level = eLOG_Trace;
                else
                    return status;
            } else
                level = eLOG_Error;
            CORE_LOGF_X(8, level, ("[HTTP]  Error reading header (%s)",
                                   IO_StatusStr(status)));
            return status;
        }
    }
    /* the entire header has been read */
    uuu->read_header = 0/*false*/;
    status = eIO_Success;

    if (BUF_Read(uuu->http, 0, size) != size) {
        CORE_LOG_X(9, eLOG_Error, "[HTTP]  Cannot discard HTTP header buffer");
        status = eIO_Unknown;
        assert(0);
    }

    /* HTTP status must come on the first line of the response */
    if (sscanf(header, "HTTP/%*d.%*d %d ", &http_status) != 1 || !http_status)
        http_status = -1;
    if (http_status < 200  ||  299 < http_status) {
        server_error = http_status;
        if      (http_status == 301  ||  http_status == 302)
            redirect = eRetry_Redirect;
        else if (http_status == 401)
            redirect = eRetry_Authenticate;
        else if (http_status == 407)
            redirect = eRetry_ProxyAuthenticate;
        else if (http_status < 0  ||  http_status == 403 || http_status == 404)
            uuu->net_info->max_try = 0;
    }
    uuu->code = http_status < 0 ? -1 : http_status;

    if ((server_error  ||  !uuu->error_header)
        &&  uuu->net_info->debug_printout == eDebugPrintout_Some) {
        /* HTTP header gets printed as part of data logging when
           uuu->net_info->debug_printout == eDebugPrintout_Data. */
        const char* header_header;
        if (!server_error)
            header_header = "HTTP header";
        else if (uuu->flags & fHCC_KeepHeader)
            header_header = "HTTP header (error)";
        else if (uuu->code == 301  ||  uuu->code == 302)
            header_header = "HTTP header (moved)";
        else if (!uuu->net_info->max_try)
            header_header = "HTTP header (unrecoverable error)";
        else
            header_header = "HTTP header (server error, can retry)";
        CORE_DATA_X(19, header, size, header_header);
    }

    {{
        /* parsing "NCBI-Message" tag */
        static const char kNcbiMessageTag[] = "\n" HTTP_NCBI_MESSAGE " ";
        const char* s;
        for (s = strchr(header, '\n');  s  &&  *s;  s = strchr(s + 1, '\n')) {
            if (strncasecmp(s, kNcbiMessageTag, sizeof(kNcbiMessageTag)-1)==0){
                const char* message = s + sizeof(kNcbiMessageTag) - 1;
                while (*message  &&  isspace((unsigned char)(*message)))
                    message++;
                if (!(s = strchr(message, '\r')))
                    s = strchr(message, '\n');
                assert(s);
                do {
                    if (!isspace((unsigned char) s[-1]))
                        break;
                } while (--s > message);
                if (message != s) {
                    if (s_MessageHook) {
                        if (s_MessageIssued <= 0) {
                            s_MessageIssued = 1;
                            s_MessageHook(message);
                        }
                    } else {
                        s_MessageIssued = -1;
                        CORE_LOGF_X(10, eLOG_Critical,
                                    ("[NCBI-MESSAGE]  %.*s",
                                     (int)(s - message), message));
                    }
                }
                break;
            }
        }
    }}

    if (uuu->flags & fHCC_KeepHeader) {
        if (!BUF_Write(&uuu->r_buf, header, size)) {
            CORE_LOG_X(11, eLOG_Error, "[HTTP]  Cannot keep HTTP header");
            status = eIO_Unknown;
        }
        free(header);
        return status;
    }

    if (uuu->parse_http_hdr
        &&  !(*uuu->parse_http_hdr)(header, uuu->adjust_data, server_error)) {
        server_error = 1/*fake, but still boolean true*/;
    }

    if (redirect == eRetry_Redirect) {
        /* parsing "Location" pointer */
        static const char kLocationTag[] = "\nLocation: ";
        char* s;
        for (s = strchr(header, '\n');  s  &&  *s;  s = strchr(s + 1, '\n')) {
            if (strncasecmp(s, kLocationTag, sizeof(kLocationTag) - 1) == 0) {
                char* location = s + sizeof(kLocationTag) - 1;
                while (*location  &&  isspace((unsigned char)(*location)))
                    location++;
                if (!(s = strchr(location, '\r')))
                    s = strchr(location, '\n');
                assert(s);
                do {
                    if (!isspace((unsigned char) s[-1]))
                        break;
                } while (--s > location);
                if (s != location) {
                    size_t len = (size_t)(s - location);
                    memmove(header, location, len);
                    header[len] = '\0';
                    *retry = header;
                }
                break;
            }
        }
    } else if (redirect != eRetry_None) {
        /* parsing "Authenticate" tags */
        static const char kAuthenticateTag[] = "-Authenticate: ";
        char* s;
        for (s = strchr(header, '\n');  s  &&  *s;  s = strchr(s + 1, '\n')) {
            if (strncasecmp(s + (redirect == eRetry_Authenticate ? 4 : 6),
                            kAuthenticateTag, sizeof(kAuthenticateTag)-1)==0){
                if ((redirect == eRetry_Authenticate
                     &&  strncasecmp(s, "\nWWW",   4) != 0)  ||
                    (redirect == eRetry_ProxyAuthenticate
                     &&  strncasecmp(s, "\nProxy", 6) != 0)) {
                    continue;
                }
                /* TODO */
            }
        }
    } else if (!server_error) {
        static const char kContentLengthTag[] = "\nContent-Length: ";
        const char* s;
        for (s = strchr(header, '\n');  s  &&  *s;  s = strchr(s + 1, '\n')) {
            if (!strncasecmp(s,kContentLengthTag,sizeof(kContentLengthTag)-1)){
                const char* expected = s + sizeof(kContentLengthTag) - 1;
                while (*expected  &&  isspace((unsigned char)(*expected)))
                    expected++;
                if (!(s = strchr(expected, '\r')))
                    s = strchr(expected, '\n');
                assert(s);
                do {
                    if (!isspace((unsigned char) s[-1]))
                        break;
                } while (--s > expected);
                if (s != expected) {
                    char* e;
                    errno = 0;
                    uuu->expected = (size_t) strtol(expected, &e, 10);
                    if (errno  ||  e != s)
                        uuu->expected = 0;
                    else if (!uuu->expected)
                        uuu->expected = (size_t)(-1L);
                }
                break;
            }
        }
    }
    if (!*retry)
        free(header);

    /* skip & printout the content, if server error was flagged */
    if (server_error && uuu->net_info->debug_printout == eDebugPrintout_Some) {
        BUF   buf = 0;
        char* body;

        SOCK_SetTimeout(uuu->sock, eIO_Read, 0);
        /* read until EOF */
        SOCK_StripToPattern(uuu->sock, 0, 0, &buf, 0);
        if (!(size = BUF_Size(buf))) {
            CORE_LOG_X(12, eLOG_Trace,
                       "[HTTP]  No HTTP body received with this error");
        } else if ((body = (char*) malloc(size)) != 0) {
            size_t n = BUF_Read(buf, body, size);
            if (n != size) {
                CORE_LOGF_X(13, eLOG_Error,
                            ("[HTTP]  Cannot read server error "
                             "body from buffer (%lu out of %lu)",
                             (unsigned long) n, (unsigned long) size));
            }
            CORE_DATA_X(20, body, n, "Server error body");
            free(body);
        } else {
            CORE_LOGF_X(14, eLOG_Error,
                        ("[HTTP]  Cannot allocate server error "
                         "body, %lu bytes", (unsigned long) size));
        }
        BUF_Destroy(buf);
    }

    return server_error ? eIO_Unknown : status;
}
Exemple #3
0
static void TEST__client_2(SOCK sock)
{
#define W_FIELD  10
#define N_FIELD  1000
#define N_REPEAT 10
#define N_RECONNECT 3
    EIO_Status status;
    size_t     n_io, n_io_done, i;
    char       buf[W_FIELD * N_FIELD + 1];

    CORE_LOGF(eLOG_Note,
              ("TEST__client_2(TC2) @:%hu",
               SOCK_GetLocalPort(sock, eNH_HostByteOrder)));

    /* fill out a buffer to send to server */
    memset(buf, 0, sizeof(buf));
    for (i = 0;  i < N_FIELD;  i++) {
        sprintf(buf + i * W_FIELD, "%10lu", (unsigned long)i);
    }

    /* send the buffer to server, then get it back */
    for (i = 0;  i < N_REPEAT;  i++) {
        char        buf1[sizeof(buf)];
        STimeout    w_to, r_to;
        int/*bool*/ w_timeout_on = (int)(i%2); /* if to start from     */
        int/*bool*/ r_timeout_on = (int)(i%2); /* zero or inf. timeout */
        char*       x_buf;

        /* set timeout */
        w_to.sec  = 0;
        w_to.usec = 0;
        status = SOCK_SetTimeout(sock, eIO_Write, w_timeout_on ? &w_to : 0);
        assert(status == eIO_Success);

        /* reconnect */
        if ((i % N_RECONNECT) == 0) {
            size_t j = i / N_RECONNECT;
            do {
                SOCK_SetDataLogging(sock, eOn);
                status = SOCK_Reconnect(sock, 0, 0, 0);
                SOCK_SetDataLogging(sock, eDefault);
                CORE_LOGF(eLOG_Note,
                          ("TC2::reconnect @:%hu: i=%lu, j=%lu, status=%s",
                           SOCK_GetLocalPort(sock, eNH_HostByteOrder),
                           (unsigned long) i, (unsigned long) j,
                           IO_StatusStr(status)));
                assert(status == eIO_Success);
                assert(SOCK_Status(sock, eIO_Read)  == eIO_Success);
                assert(SOCK_Status(sock, eIO_Write) == eIO_Success);

                /* give a break to let server reset the listening socket */
                X_SLEEP(1);
            } while ( j-- );
        }

        /* send */
        x_buf = buf;
        n_io = sizeof(buf);
        do {
            X_SLEEP(1);
            status = SOCK_Write(sock, x_buf, n_io,
                                &n_io_done, eIO_WritePersist);
            if (status == eIO_Closed)
                CORE_LOG(eLOG_Fatal, "TC2::write: connection closed");

            CORE_LOGF(eLOG_Note,
                      ("TC2::write:"
                       " i=%d, status=%7s, n_io=%5lu, n_io_done=%5lu"
                       " timeout(%d): %5u.%06us",
                       (int) i, IO_StatusStr(status),
                       (unsigned long) n_io, (unsigned long) n_io_done,
                       (int) w_timeout_on, w_to.sec, w_to.usec));
            if ( !w_timeout_on ) {
                assert(status == eIO_Success  &&  n_io_done == n_io);
            } else {
                const STimeout* x_to;
                assert(status == eIO_Success  ||  status == eIO_Timeout);
                x_to = SOCK_GetTimeout(sock, eIO_Write);
                assert(w_to.sec == x_to->sec  &&  w_to.usec == x_to->usec);
            }
            n_io  -= n_io_done;
            x_buf += n_io_done;
            if (status == eIO_Timeout)
                s_DoubleTimeout(&w_to);
            status = SOCK_SetTimeout(sock, eIO_Write, &w_to);
            assert(status == eIO_Success);
            w_timeout_on = 1/*true*/;
        } while ( n_io );

        /* get back the just sent data */
        r_to.sec  = 0;
        r_to.usec = 0;
        status = SOCK_SetTimeout(sock, eIO_Read, r_timeout_on ? &r_to : 0);
        assert(status == eIO_Success);

        x_buf = buf1;
        n_io = sizeof(buf1);
        do {
            if (i%2 == 0) {
                /* peek a little piece twice and compare */
                char   xx_buf1[128], xx_buf2[128];
                size_t xx_io_done1, xx_io_done2;
                if (SOCK_Read(sock, xx_buf1, sizeof(xx_buf1), &xx_io_done1,
                              eIO_ReadPeek) == eIO_Success  &&
                    SOCK_Read(sock, xx_buf2, xx_io_done1, &xx_io_done2,
                              eIO_ReadPeek) == eIO_Success) {
                    assert(xx_io_done1 >= xx_io_done2);
                    assert(memcmp(xx_buf1, xx_buf2, xx_io_done2) == 0);
                }
            }
            status = SOCK_Read(sock, x_buf, n_io, &n_io_done, eIO_ReadPlain);
            if (status == eIO_Closed) {
                assert(SOCK_Status(sock, eIO_Read) == eIO_Closed);
                CORE_LOG(eLOG_Fatal, "TC2::read: connection closed");
            }
            CORE_LOGF(eLOG_Note,
                      ("TC2::read: "
                       " i=%d, status=%7s, n_io=%5lu, n_io_done=%5lu"
                       " timeout(%d): %5u.%06us",
                       (int) i, IO_StatusStr(status),
                       (unsigned long) n_io, (unsigned long) n_io_done,
                       (int) r_timeout_on, r_to.sec, r_to.usec));
            if ( !r_timeout_on ) {
                assert(status == eIO_Success  &&  n_io_done > 0);
            } else {
                const STimeout* x_to;
                assert(status == eIO_Success  ||  status == eIO_Timeout);
                x_to = SOCK_GetTimeout(sock, eIO_Read);
                assert(r_to.sec == x_to->sec  &&  r_to.usec == x_to->usec);
            }

            n_io  -= n_io_done;
            x_buf += n_io_done;
            if (status == eIO_Timeout)
                s_DoubleTimeout(&r_to);
            status = SOCK_SetTimeout(sock, eIO_Read, &r_to);
            assert(status == eIO_Success);
            r_timeout_on = 1/*true*/;
        } while ( n_io );

        assert(memcmp(buf, buf1, sizeof(buf)) == 0);
    }
}