Пример #1
0
static char* s_ServiceName(const char* service,
                           int/*bool*/ ismask, size_t depth)
{
    char   buf[128];
    char   srv[128];
    size_t len;
    char*  s;

    if (depth > 7) {
        assert(service  &&  *service);
        CORE_LOGF_X(7, eLOG_Error,
                    ("[%s]  Maximal service name recursion depth reached: %lu",
                     service, (unsigned long) depth));
        return 0/*failure*/;
    }
    len = 0;
    assert(sizeof(buf) > sizeof(CONN_SERVICE_NAME));
    if (!service  ||  (!ismask  &&  (!*service  ||  strpbrk(service, "?*")))
        ||  (len = strlen(service)) >= sizeof(buf)-sizeof(CONN_SERVICE_NAME)) {
        CORE_LOGF_X(8, eLOG_Error,
                    ("%s%s%s%s service name",
                     !service  ||  !*service ? "" : "[",
                     !service ? "" : service,
                     !service  ||  !*service ? "" : "]  ",
                     !service ? "NULL" : !*service ? "Empty" :
                     len < sizeof(buf)-sizeof(CONN_SERVICE_NAME) ? "Invalid" :
                     "Too long"));
        return 0/*failure*/;
    }
    if (!s_Fast  &&  !ismask) {
        s = (char*) memcpy(buf, service, len) + len;
        *s++ = '_';
        memcpy(s, CONN_SERVICE_NAME, sizeof(CONN_SERVICE_NAME));
        /* Looking for "service_CONN_SERVICE_NAME" in the environment */
        if (!(s = getenv(strupr(buf)))  ||  !*s) {
            /* Looking for "CONN_SERVICE_NAME" in registry section [service] */
            buf[len++] = '\0';
            CORE_REG_GET(buf, buf + len, srv, sizeof(srv), 0);
            s = srv;
        }
        if (*s  &&  strcasecmp(s, service) != 0)
            return s_ServiceName(s, ismask, ++depth);
    }
    return strdup(service);
}
Пример #2
0
static int/*bool*/ s_ParseHeader(const char* header,
                                 void*       data,
                                 int         server_error)
{
    static const char   kStateless[] = "TRY_STATELESS";
    static const size_t klen = sizeof(kStateless) - 1;
    SServiceConnector* uuu = (SServiceConnector*) data;

    SERV_Update(uuu->iter, header, server_error);
    if (server_error)
        return 1/*parsed okay*/;

    while (header && *header) {
        if (strncasecmp(header, HTTP_CONNECTION_INFO,
                        sizeof(HTTP_CONNECTION_INFO) - 1) == 0) {
            unsigned int  i1, i2, i3, i4, ticket;
            unsigned char o1, o2, o3, o4;
            char ipaddr[40];

            if (uuu->host)
                break/*failed - duplicate connection info*/;
            header += sizeof(HTTP_CONNECTION_INFO) - 1;
            while (*header  &&  isspace((unsigned char)(*header)))
                header++;
            if (strncasecmp(header, kStateless, klen) == 0  &&
                (!header[klen]  ||  isspace((unsigned char) header[klen]))) {
                /* Special keyword for switching into stateless mode */
                uuu->host = (unsigned int)(-1);
#if defined(_DEBUG) && !defined(NDEBUG)
                if (uuu->net_info->debug_printout) {
                    CORE_LOGF_X(2, eLOG_Note,
                                ("[%s]  Fallback to stateless", uuu->service));
                }
#endif /*_DEBUG && !NDEBUG*/
            } else {
                int n;
                if (sscanf(header, "%u.%u.%u.%u %hu %x%n",
                           &i1, &i2, &i3, &i4, &uuu->port, &ticket, &n) < 6  ||
                    (header[n]  &&  !isspace((unsigned char) header[n]))) {
                    break/*failed - unreadable connection info*/;
                }
                o1 = i1; o2 = i2; o3 = i3; o4 = i4;
                sprintf(ipaddr, "%u.%u.%u.%u", o1, o2, o3, o4);
                if (!(uuu->host = SOCK_gethostbyname(ipaddr))  ||  !uuu->port)
                    break/*failed - bad host:port in connection info*/;
                uuu->ticket = SOCK_HostToNetLong(ticket);
            }
        }
        if ((header = strchr(header, '\n')) != 0)
            header++;
    }
    if (!header  ||  !*header)
        return 1/*success*/;

    uuu->host = 0;
    return 0/*failure*/;
}
Пример #3
0
static int/*bool*/ s_OpenDispatcher(SServiceConnector* uuu)
{
    if (!(uuu->iter = SERV_OpenEx(uuu->service, uuu->types,
                                  SERV_LOCALHOST, uuu->net_info, 0, 0))) {
        CORE_LOGF_X(5, eLOG_Error,
                    ("[%s]  Cannot locate server", uuu->service));
        return 0/*false*/;
    }
    return 1/*true*/;
}
Пример #4
0
static void s_Resolve(SERV_ITER iter)
{
    struct SDISPD_Data* data = (struct SDISPD_Data*) iter->data;
    SConnNetInfo* net_info = data->net_info;
    EIO_Status status = eIO_Success;
    CONNECTOR c = 0;
    CONN conn;
    char* s;

    assert(!(data->eof | data->fail));
    assert(!!net_info->stateless == !!iter->stateless);
    /* Obtain additional header information */
    if ((!(s = SERV_Print(iter, 0, 0))
         ||  ConnNetInfo_OverrideUserHeader(net_info, s))
        &&
        ConnNetInfo_OverrideUserHeader(net_info,
                                       iter->ok_down  &&  iter->ok_suppressed
                                       ? "Dispatch-Mode: PROMISCUOUS\r\n"
                                       : iter->ok_down
                                       ? "Dispatch-Mode: OK_DOWN\r\n"
                                       : iter->ok_suppressed
                                       ? "Dispatch-Mode: OK_SUPPRESSED\r\n"
                                       : "Dispatch-Mode: INFORMATION_ONLY\r\n")
        &&
        ConnNetInfo_OverrideUserHeader(net_info, iter->reverse_dns
                                       ? "Client-Mode: REVERSE_DNS\r\n"
                                       : !net_info->stateless
                                       ? "Client-Mode: STATEFUL_CAPABLE\r\n"
                                       : "Client-Mode: STATELESS_ONLY\r\n")) {
        c = HTTP_CreateConnectorEx(net_info, fHTTP_Flushable, s_ParseHeader,
                                   iter/*data*/, s_Adjust, 0/*cleanup*/);
    }
    if (s) {
        ConnNetInfo_DeleteUserHeader(net_info, s);
        free(s);
    }
    if (c  &&  (status = CONN_Create(c, &conn)) == eIO_Success) {
        /* Send all the HTTP data... */
        CONN_Flush(conn);
        /* ...then trigger the header callback */
        CONN_Close(conn);
    } else {
        CORE_LOGF_X(5, eLOG_Error,
                    ("%s%s%sUnable to create auxiliary HTTP %s: %s",
                     &"["[!*iter->name], iter->name, *iter->name ? "]  " : "",
                     c              ? "connection" : "connector",
                     IO_StatusStr(c ? status       : eIO_Unknown)));
        if (c  &&  c->destroy)
            c->destroy(c);
        assert(0);
    }
}
Пример #5
0
static const char* s_SendRcpt(SOCK sock, const char* to,
                              char buf[], size_t buf_size,
                              const char what[],
                              const char write_error[],
                              const char proto_error[])
{
    char c;
    while ((c = *to++) != 0) {
        char   quote = 0;
        size_t k = 0;
        if (isspace((unsigned char) c))
            continue;
        while (k < buf_size) {
            if (quote) {
                if (c == quote)
                    quote = 0;
            } else if (c == '"'  ||  c == '<') {
                quote = c == '<' ? '>' : c;
            } else if (c == ',')
                break;
            buf[k++] = c == '\t' ? ' ' : c;
            if (!(c = *to++))
                break;
            if (isspace((unsigned char) c)) {
                while (isspace((unsigned char)(*to)))
                    to++;
            }
        }
        if (k >= buf_size)
            SENDMAIL_RETURN(3, "Recipient address is too long");
        buf[k] = '\0'/*just in case*/;
        if (quote) {
            CORE_LOGF_X(1, eLOG_Warning,
                        ("[SendMail]  Unbalanced delimiters in "
                         "recipient %s for %s: \"%c\" expected",
                         buf, what, quote));
        }
        if (!s_SockWrite(sock, "RCPT TO: <", 0)  ||
            !s_SockWrite(sock, buf, k)           ||
            !s_SockWrite(sock, ">" MX_CRLF, 1 + 2)) {
            SENDMAIL_RETURN(4, write_error);
        }
        if (!s_SockReadResponse(sock, 250, 251, buf, buf_size))
            SENDMAIL_RETURN2(5, proto_error, buf);
        if (!c)
            break;
    }
    return 0;
}
Пример #6
0
static int/*tri-state-bool, inverted*/ s_Shmem_TryWLock(int which)
{
    int semnum = (which << 1) | 1;
    int/*bool*/ no_undo = 0;
    struct sembuf rwlock[2];
    int i = 0;

    for (;;) {
        rwlock[0].sem_num = semnum;
        rwlock[0].sem_op  = 0; /* precondition:  [1] == 0 */
        rwlock[0].sem_flg = 0;
        rwlock[1].sem_num = semnum;
        rwlock[1].sem_op  = 1; /* postcondition: [1] == 1 */
        rwlock[1].sem_flg = no_undo ? 0 : SEM_UNDO;

        if (semop(s_Muxid, rwlock, sizeof(rwlock)/sizeof(rwlock[0])) >= 0) {
            /* No new read accesses are allowed after this point */
            s_NoUndo[semnum - 1] = no_undo;
            /* Look at [2] first, then continue with it */
            if (s_Shmem_Lock(which, 2, 0/*wait*/) != 0)
                return 1/*partially locked*/;
            return 0/*okay*/;
        }
        if (i)
            break;
        i = errno;
        if (i == ENOSPC) {
            CORE_LOGF_X(8, eLOG_Warning,
                        ("LBSM PreW-locking[%d] w/o undo", which + 1));
            no_undo = 1;
            continue;
        }
        if (i == EINTR)
            continue;
        if (i != ENOMEM)
            break;
#ifdef LBSM_DEBUG
        CORE_LOGF(eLOG_Trace,
                  ("LBSM PreW-lock[%d] wait(ENOMEM)", which + 1));
#endif/*LBSM_DEBUG*/
        sleep(1);
    }
    return -1/*not locked*/;
}
Пример #7
0
/* For 'which' block: check specified [sem] first, then continue with [2]++ */
static int/*syscall*/ s_Shmem_Lock(int which, int sem, int no_wait)
{
    int semnum = (which << 1) + sem;
    int accsem = (which << 1) + 2;
    int/*bool*/ no_undo = 0;
    struct sembuf lock[2];
    int i = 0;

    for (;;) {
        lock[0].sem_num = semnum;
        lock[0].sem_op  = 0; /* precondition:  [sem] == 0 */
        lock[0].sem_flg = no_wait ? IPC_NOWAIT : 0;
        lock[1].sem_num = accsem;
        lock[1].sem_op  = 1; /* postcondition: [2]++      */
        lock[1].sem_flg = no_undo ? 0 : SEM_UNDO;

        if (semop(s_Muxid, lock, sizeof(lock)/sizeof(lock[0])) >= 0) {
            s_NoUndo[accsem - 1] = no_undo;
            return 0;
        }
        if (i)
            break;
        i = errno;
        if (i == ENOSPC) {
            CORE_LOGF_X(7, eLOG_Warning,
                        ("LBSM %c-locking[%d] w/o undo",
                         "RW"[sem > 1], which + 1));
            no_undo = 1;
            continue;
        }
        if (i == EINTR)
            continue;
        if (no_wait  ||  i != ENOMEM)
            break;
#ifdef LBSM_DEBUG
        CORE_LOGF(eLOG_Trace,
                  ("LBSM %c-locking[%d] wait(ENOMEM)",
                   "RW"[sem > 1], which + 1));
#endif /*LBSM_DEBUG*/
        sleep(1);
    }
    return -1;
}
Пример #8
0
void LBSM_Shmem_Detach(HEAP heap)
{
    int which;

    if (!heap)
        return;
    which = HEAP_Serial(heap);
#ifdef LBSM_DEBUG
    CORE_LOGF(eLOG_Trace, ("LBSM shmem[%d] detaching", which));
#endif /*LBSM_DEBUG*/
    if (which != 1  &&  which != 2) {
        CORE_LOGF_X(12, eLOG_Error,
                    ("Bad block number (%d) for LBSM shmem to unlock", which));
    } else
        s_Shmem_RUnlock(which - 1);
    HEAP_Detach(heap);
#ifdef LBSM_DEBUG
    CORE_LOG(eLOG_Trace, "LBSM DETACHED");
#endif /*LBSM_DEBUG*/
}
Пример #9
0
static const SLBSM_Host* s_LookupHost(HEAP heap, const SERV_ITER iter,
                                      const SLBSM_Service* svc)
{
    unsigned int addr =
        svc->info.host ? svc->info.host : s_GetLocalHostAddress(heap);
    const SLBSM_Host* host = LBSM_LookupHost(heap, addr, &svc->entry);
    if (!host  ||  host->entry.good < iter->time) {
        if (svc->info.rate > 0.0) {
            char buf[40];
            if (SOCK_ntoa(addr, buf, sizeof(buf)) != 0)
                strcpy(buf, "(unknown)");
            CORE_LOGF_X(8, eLOG_Error,
                        ("Dynamic %s server `%s' on [%s] w/%s host entry",
                         SERV_TypeStr(svc->info.type),
                         (const char*) svc + svc->name,
                         buf, host ? "outdated" : "o"));
        }
        return 0;
    }
    return host;
}
Пример #10
0
HEAP LBSM_Shmem_Create(void)
{
    int/*bool*/ one = 0/*false*/, two = 0/*false*/;
    HEAP heap = 0;
    size_t i;

    for (i = 0;  i < 2;  i++)
        s_Shmid[i] = shmget(k_ShmemKey[i], 0, 0);
    if ((one = (s_Shmid[0] >= 0)) | (two = (s_Shmid[1] >= 0))) {
        CORE_LOGF_X(13, eLOG_Warning,
                    ("Re-creating existing LBSM shmem segment%s %s%s%s",
                     one ^ two ? ""    : "s",
                     one       ? "[1]" : "",
                     one ^ two ? ""    : " and ",
                     two       ? "[2]" : ""));
        LBSM_Shmem_Destroy(0);
    }
    if (!(i = CORE_GetVMPageSize()))
        i = 4096;
    heap = HEAP_Create(0, 0, i, s_LBSM_ExpandHeap, 0);
    return heap;
}
Пример #11
0
static void s_Shmem_Destroy(int which, pid_t own_pid)
{
    if (s_Shmid[which] < 0) {
        assert(!s_Shmem[which]  &&  !s_ShmemSize[which]);
        return;
    }
    if (s_Shmem[which]) {
        if (shmdt(s_Shmem[which]) < 0) {
            CORE_LOGF_ERRNO_X(14, eLOG_Error, errno,
                              ("Unable to detach LBSM shmem[%d]", which + 1));
        }
        s_Shmem[which] = 0;
    }
    if (own_pid) {
        struct shmid_ds shm_ds;
        if (shmctl(s_Shmid[which], IPC_STAT, &shm_ds) < 0)
            shm_ds.shm_cpid = 0;
        if (shm_ds.shm_cpid != own_pid) {
            if (shm_ds.shm_cpid) {
                CORE_LOGF_X(15, eLOG_Error,
                            ("Not an owner (%lu) to remove LBSM shmem[%d]",
                             (long) shm_ds.shm_cpid, which + 1));
            } else {
                CORE_LOGF_ERRNO(eLOG_Error, errno,
                                ("Unable to stat LBSM shmem[%d]", which + 1));
            }
            own_pid = 0;
        }
    } else
        own_pid = 1;
    if (own_pid  &&  shmctl(s_Shmid[which], IPC_RMID, 0) < 0) {
        CORE_LOGF_ERRNO_X(16, eLOG_Error, errno,
                          ("Unable to remove LBSM shmem[%d]", which + 1));
    }
    s_Shmid[which]     = -1;
    s_ShmemSize[which] =  0;
}
Пример #12
0
static SERV_ITER s_Open(const char*          service,
                        unsigned/*bool*/     ismask,
                        TSERV_Type           types,
                        unsigned int         preferred_host,
                        unsigned short       preferred_port,
                        double               preference,
                        const SConnNetInfo*  net_info,
                        const SSERV_InfoCPtr skip[],
                        size_t               n_skip,
                        unsigned/*bool*/     external,
                        const char*          arg,
                        const char*          val,
                        SSERV_Info**         info,
                        HOST_INFO*           host_info)
{
    int/*bool*/ do_lbsmd = -1/*unassigned*/, do_dispd = -1/*unassigned*/;
    const SSERV_VTable* op;
    SERV_ITER iter;
    const char* s;

    if (!(s = s_ServiceName(service, ismask, 0)))
        return 0;
    if (!(iter = (SERV_ITER) calloc(1, sizeof(*iter)))) {
        free((void*) s);
        return 0;
    }
    assert(ismask  ||  *s);

    iter->name              = s;
    iter->type              = types & fSERV_All;
    iter->host              = (preferred_host == SERV_LOCALHOST
                               ? SOCK_GetLocalHostAddress(eDefault)
                               : preferred_host);
    iter->port              = preferred_port;
    iter->pref              = (preference < 0.0
                               ? -1.0
                               :  0.01 * (preference > 100.0
                                          ? 100.0
                                          : preference));
    if (ismask)
        iter->ismask        = 1;
    if (types & fSERV_IncludeDown)
        iter->ok_down       = 1;
    if (types & fSERV_IncludeSuppressed)
        iter->ok_suppressed = 1;
    if (types & fSERV_ReverseDns)
        iter->reverse_dns   = 1;
    if (types & fSERV_Stateless)
        iter->stateless     = 1;
    iter->external          = external;
    if (arg  &&  *arg) {
        iter->arg           = arg;
        iter->arglen        = strlen(arg);
        if (val) {
            iter->val       = val;
            iter->vallen    = strlen(val);
        }
    }
    iter->time              = (TNCBI_Time) time(0);

    if (n_skip) {
        size_t i;
        for (i = 0;  i < n_skip;  i++) {
            const char* name = (iter->ismask  ||  skip[i]->type == fSERV_Dns
                                ? SERV_NameOfInfo(skip[i]) : "");
            SSERV_Info* temp = SERV_CopyInfoEx(skip[i],
                                               !iter->reverse_dns  ||  *name ?
                                               name : s);
            if (temp) {
                temp->time = NCBI_TIME_INFINITE;
                if (!s_AddSkipInfo(iter, name, temp)) {
                    free(temp);
                    temp = 0;
                }
            }
            if (!temp) {
                SERV_Close(iter);
                return 0;
            }
        }
    }
    assert(n_skip == iter->n_skip);

    if (net_info) {
        if (net_info->firewall)
            iter->type |= fSERV_Firewall;
        if (net_info->stateless)
            iter->stateless = 1;
        if (net_info->lb_disable)
            do_lbsmd = 0/*false*/;
    } else
        do_dispd = 0/*false*/;
    /* Ugly optimization not to access the registry more than necessary */
    if ((!s_IsMapperConfigured(service, REG_CONN_LOCAL_ENABLE)               ||
         !(op = SERV_LOCAL_Open(iter, info, host_info)))                 &&
        (!do_lbsmd                                                           ||
         !(do_lbsmd= !s_IsMapperConfigured(service, REG_CONN_LBSMD_DISABLE)) ||
         !(op = SERV_LBSMD_Open(iter, info, host_info,
                                !do_dispd                                    ||
                                !(do_dispd = !s_IsMapperConfigured
                                  (service, REG_CONN_DISPD_DISABLE)))))  &&
        (!do_dispd                                                           ||
         !(do_dispd= !s_IsMapperConfigured(service, REG_CONN_DISPD_DISABLE)) ||
         !(op = SERV_DISPD_Open(iter, net_info, info, host_info)))) {
        if (!do_lbsmd  &&  !do_dispd) {
            CORE_LOGF_X(1, eLOG_Error,
                        ("[%s]  No service mappers available", service));
        }
        SERV_Close(iter);
        return 0;
    }

    assert(op != 0);
    iter->op = op;
    return iter;
}
Пример #13
0
/* Return non-zero if successful;  return zero on failure (no lock acquired).
 * If "wait" passed greater than zero do not attempt to assassinate a contender
 * (try to identify and print its PID anyways).  Otherwise, having killed
 * the contender, try to re-acquire the lock (without.
 */
static int/*bool*/ s_Shmem_WLock(int which, int/*bool*/ wait)
{
    static union semun dummy;
    int locked;
    int pid;

#ifdef LBSM_DEBUG
    CORE_LOGF(eLOG_Trace,
              ("LBSM W-lock[%d] acquire%s", which + 1, wait ? " w/wait" : ""));
#endif /*LBSM_DEBUG*/

    if ((locked = s_Shmem_TryWLock(which)) == 0)
        return 1/*success*/;

    if (locked < 0) {
        /* even [1] was not successfully locked, so we have either
         *   a/ a hanging writer, or
         *   b/ a hanging old reader (which doesn't change [2] in block 0 only)
         * In either case, we can try to obtain a PID of the offender.
         */
        if ((pid = semctl(s_Muxid, (which << 1) | 1, GETPID, dummy)) > 0) {
            int self   = (pid_t) pid == getpid();
            int other  = !self  &&  (kill(pid, 0) == 0  ||  errno == EPERM);
            int killed = 0;
            if (!wait) {
                if (other  &&  kill(pid, SIGTERM) == 0) {
                    CORE_LOGF_X(17, eLOG_Warning,
                                ("Terminating PID %lu", (long) pid));
                    sleep(1); /* let them catch SIGTERM and exit gracefully */
                    kill(pid, SIGKILL);
                    killed = 1;
                } else {
                    CORE_LOGF_X(18, eLOG_Warning,
                                ("Unable to kill PID %lu", (long) pid));
                }
            }
            CORE_LOGF_X(19, eLOG_Warning,
                        ("LBSM lock[%d] %s revoked from PID %lu (%s)",
                         which + 1, killed ? "is being" : "has to be",
                         (long) pid, self  ? "self" :
                         other ? (killed ? "killed" : "hanging") : "zombie"));
        } else if (pid < 0)
            return 0/*severe failure: most likely removed IPC id*/;
    } else {
        pid = 0;
        /* [1] was free (now locked) but [2] was taken by someone else,
         * we have a hanging reader, no additional info is available.
         */
        if (!wait) {
            union semun arg;
            arg.val = 1;
            if (semctl(s_Muxid, (which << 1) + 2, SETVAL, arg) < 0) {
                CORE_LOGF_ERRNO_X(9, eLOG_Error, errno,
                                  ("Unable to adjust LBSM access count[%d]",
                                   which + 1));
            }
            wait = 1/*this makes us undo [1] and fail*/;
        }
        if (wait) {
            int x_errno = errno;
            s_Shmem_Unlock(which, 1);
            errno = x_errno;
        }
    }

    if (!pid) {
        int val;
        if ((val = semctl(s_Muxid, (which << 1) + 2, GETVAL, dummy)) > 1) {
            CORE_LOGF_X(20, eLOG_Warning,
                        ("%u hanging readers in LBSM shmem[%d]%s",
                         (unsigned int) val, which + 1, wait ? "" :
                         locked < 0 ? ", revoking lock" : ", lock revoked"));
        } else {
            CORE_LOGF_X(21, eLOG_Warning,
                        ("A hanging reader in LBSM shmem[%d]%s",
                         which + 1, wait ? "" :
                         locked < 0 ? ", revoking lock" : ", lock revoked"));
        }
    }

    if (wait)
        return 0/*failure*/;

    if (locked > 0)
        return 1/*success: good to go*/;

#ifdef LBSM_DEBUG
    CORE_LOGF(eLOG_Trace,
              ("LBSM W-lock[%d] re-acquire", which + 1));
#endif /*LBSM_DEBUG*/
    return s_Shmem_WLock(which, 0/*no wait*/);
}
Пример #14
0
static EIO_Status s_VT_Open(CONNECTOR connector, const STimeout* timeout)
{
    SServiceConnector* uuu = (SServiceConnector*) connector->handle;
    SMetaConnector* meta = connector->meta;
    EIO_Status status;

    for (;;) {
        const SSERV_Info* info;
        SConnNetInfo* net_info;
        CONNECTOR conn;
        int stateless;

        assert(!uuu->meta.list  &&  !uuu->name  &&  !uuu->descr);
        if (!uuu->iter  &&  !s_OpenDispatcher(uuu)) {
            status = eIO_Closed;
            break;
        }

        if (uuu->net_info->firewall  &&  strcasecmp(uuu->iter->name, "local"))
            info = 0;
        else if (!(info = s_GetNextInfo(uuu))) {
            status = eIO_Closed;
            break;
        }

        if (!(net_info = ConnNetInfo_Clone(uuu->net_info))) {
            status = eIO_Unknown;
            break;
        }

        net_info->scheme = eURL_Unspec;
        conn = s_Open(uuu, timeout, info, net_info, 0/*!second_try*/);
        if (conn)
            uuu->descr = ConnNetInfo_URL(net_info);
        stateless = net_info->stateless;

        ConnNetInfo_Destroy(net_info);

        if (!conn) {
            if (!info) {
                status = eIO_Closed;
                break;
            }
            continue;
        }

        /* Setup the new connector on a temporary meta-connector... */
        memset(&uuu->meta, 0, sizeof(uuu->meta));
        METACONN_Add(&uuu->meta, conn);
        /* ...then link it in using current connection's meta */
        conn->meta = meta;
        conn->next = meta->list;
        meta->list = conn;

        if (!uuu->descr  &&  uuu->meta.descr)
            CONN_SET_METHOD(meta, descr, uuu->meta.descr, uuu->meta.c_descr);
        CONN_SET_METHOD    (meta, wait,  uuu->meta.wait,  uuu->meta.c_wait);
        CONN_SET_METHOD    (meta, write, uuu->meta.write, uuu->meta.c_write);
        CONN_SET_METHOD    (meta, flush, uuu->meta.flush, uuu->meta.c_flush);
        CONN_SET_METHOD    (meta, read,  uuu->meta.read,  uuu->meta.c_read);
        CONN_SET_METHOD    (meta, status,uuu->meta.status,uuu->meta.c_status);
        if (uuu->meta.get_type) {
            const char* type;
            if ((type = uuu->meta.get_type(uuu->meta.c_get_type)) != 0) {
                size_t slen = strlen(uuu->service);
                size_t tlen = strlen(type);
                char* name = (char*) malloc(slen + tlen + 2);
                if (name) {
                    memcpy(name,        uuu->service, slen);
                    name[slen++] = '/';
                    memcpy(name + slen, type,         tlen);
                    tlen += slen;
                    name[tlen]   = '\0';
                    uuu->name = name;
                }
            }
        }

        status = uuu->meta.open
            ? uuu->meta.open(uuu->meta.c_open, timeout) : eIO_Success;
        if (status == eIO_Success)
            break;

        if (!stateless  &&  (!info  ||  info->type == fSERV_Firewall)) {
            static const char kFWLink[] = { "http://www.ncbi.nlm.nih.gov"
                                            "/IEB/ToolBox/NETWORK"
                                            "/dispatcher.html#Firewalling"};
            CORE_LOGF_X(6, eLOG_Error,
                        ("[%s]  %s connection failed (%s) indicating possible "
                         "firewall configuration problem; please consult <%s>",
                         uuu->service, !info ? "Firewall" : "Stateful relay",
                         IO_StatusStr(status), kFWLink));
        }
        s_Close(connector, timeout, 0/*don't close dispatcher just as yet*/);
    }

    uuu->status = status;
    return status;
}
Пример #15
0
static CONNECTOR s_Open(SServiceConnector* uuu,
                        const STimeout*    timeout,
                        const SSERV_Info*  info,
                        SConnNetInfo*      net_info,
                        int/*bool*/        second_try)
{
    int/*bool*/ but_last = 0/*false*/;
    const char* user_header; /* either "" or non-empty dynamic string */
    char*       iter_header;
    EReqMethod  req_method;

    if (info  &&  info->type != fSERV_Firewall) {
        /* Not a firewall/relay connection here */
        assert(!second_try);
        /* We know the connection point, let's try to use it! */
        if (info->type != fSERV_Standalone  ||  !net_info->stateless) {
            SOCK_ntoa(info->host, net_info->host, sizeof(net_info->host));
            net_info->port = info->port;
        }

        switch (info->type) {
        case fSERV_Ncbid:
            /* Connection directly to NCBID, add NCBID-specific tags */
            if (net_info->stateless) {
                /* Connection request with data */
                user_header = "Connection-Mode: STATELESS\r\n"; /*default*/
                req_method  = eReqMethod_Post;
            } else {
                /* We will be waiting for conn-info back */
                user_header = "Connection-Mode: STATEFUL\r\n";
                req_method  = eReqMethod_Get;
            }
            user_header = s_AdjustNetParams(uuu->service, net_info, req_method,
                                            NCBID_WEBPATH,
                                            SERV_NCBID_ARGS(&info->u.ncbid),
                                            0, user_header, info->mime_t,
                                            info->mime_s, info->mime_e, 0);
            break;
        case fSERV_Http:
        case fSERV_HttpGet:
        case fSERV_HttpPost:
            /* Connection directly to CGI */
            req_method  = info->type == fSERV_HttpGet
                ? eReqMethod_Get : (info->type == fSERV_HttpPost
                                    ? eReqMethod_Post : eReqMethod_Any);
            user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/
            user_header = s_AdjustNetParams(uuu->service, net_info, req_method,
                                            SERV_HTTP_PATH(&info->u.http),
                                            SERV_HTTP_ARGS(&info->u.http),
                                            0, user_header, info->mime_t,
                                            info->mime_s, info->mime_e, 0);
            break;
        case fSERV_Standalone:
            if (!net_info->stateless)
                return s_CreateSocketConnector(net_info, 0, 0);
            /* Otherwise, it will be a pass-thru connection via dispatcher */
            user_header = "Client-Mode: STATELESS_ONLY\r\n"; /*default*/
            user_header = s_AdjustNetParams(uuu->service, net_info,
                                            eReqMethod_Post, 0, 0,
                                            0, user_header, info->mime_t,
                                            info->mime_s, info->mime_e, 0);
            but_last = 1/*true*/;
            break;
        default:
            user_header = 0;
            break;
        }
    } else {
        EMIME_Type     mime_t;
        EMIME_SubType  mime_s;
        EMIME_Encoding mime_e;
        if (net_info->stateless  ||
            (info  &&  (info->u.firewall.type & fSERV_Http))) {
            if (info) {
                req_method = info->u.firewall.type == fSERV_HttpGet
                    ? eReqMethod_Get : (info->u.firewall.type == fSERV_HttpPost
                                        ? eReqMethod_Post : eReqMethod_Any);
                net_info->stateless = 1/*true*/;
            } else
                req_method = eReqMethod_Any;
        } else
            req_method = eReqMethod_Get;
        if (info) {
            mime_t = info->mime_t;
            mime_s = info->mime_s;
            mime_e = info->mime_e;
        } else {
            mime_t = eMIME_T_Undefined;
            mime_s = eMIME_Undefined;
            mime_e = eENCOD_None;
        }
        /* Firewall/relay connection to dispatcher, special tags */
        user_header = net_info->stateless
            ? "Client-Mode: STATELESS_ONLY\r\n" /*default*/
            : "Client-Mode: STATEFUL_CAPABLE\r\n";
        user_header = s_AdjustNetParams(uuu->service, net_info, req_method,
                                        0, 0, 0, user_header,
                                        mime_t, mime_s, mime_e, 0);
    }
    if (!user_header)
        return 0;

    if ((iter_header = SERV_Print(uuu->iter, net_info, but_last)) != 0) {
        size_t uh_len;
        if ((uh_len = strlen(user_header)) > 0) {
            char*  ih;
            size_t ih_len = strlen(iter_header);
            if ((ih = (char*) realloc(iter_header, ih_len + uh_len + 1)) != 0){
                strcpy(ih + ih_len, user_header);
                iter_header = ih;
            }
            free((char*) user_header);
        }
        user_header = iter_header;
    } else if (!*user_header)
        user_header = 0; /* special case of assignment of literal "" */

    if (uuu->user_header) {
        ConnNetInfo_DeleteUserHeader(net_info, uuu->user_header);
        free((void*) uuu->user_header);
    }
    uuu->user_header = user_header;
    if (user_header && !ConnNetInfo_OverrideUserHeader(net_info, user_header))
        return 0;

    if (!second_try) {
        ConnNetInfo_ExtendUserHeader
            (net_info, "User-Agent: NCBIServiceConnector/"
             DISP_PROTOCOL_VERSION
#ifdef NCBI_CXX_TOOLKIT
             " (C++ Toolkit)"
#else
             " (C Toolkit)"
#endif
             "\r\n");
    }

    if (!net_info->stateless  &&  (!info                         ||
                                   info->type == fSERV_Firewall  ||
                                   info->type == fSERV_Ncbid)) {
        /* Auxiliary HTTP connector first */
        EIO_Status status = eIO_Success;
        CONNECTOR c;
        CONN conn;

        /* Clear connection info */
        uuu->host = 0;
        uuu->port = 0;
        uuu->ticket = 0;
        net_info->max_try = 1;
        c = HTTP_CreateConnectorEx(net_info,
                                   (uuu->params.flags & fHCC_Flushable)
                                   | fHCC_SureFlush/*flags*/,
                                   s_ParseHeader, 0/*adj.info*/,
                                   uuu/*adj.data*/, 0/*cleanup.data*/);
        /* Wait for connection info back (error-transparent by DISPD.CGI) */
        if (c  &&  (status = CONN_Create(c, &conn)) == eIO_Success) {
            CONN_SetTimeout(conn, eIO_Open,      timeout);
            CONN_SetTimeout(conn, eIO_ReadWrite, timeout);
            CONN_SetTimeout(conn, eIO_Close,     timeout);
            CONN_Flush(conn);
            /* This also triggers parse header callback */
            CONN_Close(conn);
        } else {
            const char* error = c ? IO_StatusStr(status) : 0;
            CORE_LOGF_X(4, eLOG_Error,
                        ("[%s]  Unable to create auxiliary HTTP %s%s%s",
                         uuu->service, c ? "connection" : "connector",
                         error  &&  *error ? ": " : "", error ? error : ""));
            assert(0);
        }
        if (!uuu->host)
            return 0/*failed, no connection info returned*/;
        if (uuu->host == (unsigned int)(-1)) {
            /* Firewall mode only in stateful mode, fallback requested */
            assert((!info  ||  info->type == fSERV_Firewall)  &&  !second_try);
            /* Try to use stateless mode instead */
            net_info->stateless = 1/*true*/;
            return s_Open(uuu, timeout, info, net_info, 1/*second try*/);
        }
        SOCK_ntoa(uuu->host, net_info->host, sizeof(net_info->host));
        net_info->port = uuu->port;
        return s_CreateSocketConnector(net_info, &uuu->ticket,
                                       uuu->ticket ? sizeof(uuu->ticket) : 0);
    }
    return HTTP_CreateConnectorEx(net_info,
                                  (uuu->params.flags
                                   & (fHCC_Flushable | fHCC_NoAutoRetry))
                                  | fHCC_AutoReconnect,
                                  s_ParseHeader, s_AdjustNetInfo,
                                  uuu/*adj.data*/, 0/*cleanup.data*/);
}
Пример #16
0
/*ARGSUSED*/
static int/*bool*/ s_IsContentTypeDefined(const char*         service,
                                          const SConnNetInfo* net_info,
                                          EMIME_Type          mime_t,
                                          EMIME_SubType       mime_s,
                                          EMIME_Encoding      mime_e)
{
    const char* s;

    assert(net_info);
    for (s = net_info->http_user_header;  s;  s = strchr(s, '\n')) {
        if (s != net_info->http_user_header)
            s++;
        if (!*s)
            break;
        if (strncasecmp(s, "content-type: ", 14) == 0) {
#if defined(_DEBUG) && !defined(NDEBUG)
            EMIME_Type     m_t;
            EMIME_SubType  m_s;
            EMIME_Encoding m_e;
            char           c_t[MAX_CONTENT_TYPE_LEN];
            if (net_info->debug_printout         &&
                mime_t != eMIME_T_Undefined      &&
                mime_t != eMIME_T_Unknown        &&
                (!MIME_ParseContentTypeEx(s, &m_t, &m_s, &m_e)
                 ||   mime_t != m_t
                 ||  (mime_s != eMIME_Undefined  &&
                      mime_s != eMIME_Unknown    &&
                      m_s    != eMIME_Unknown    &&  mime_s != m_s)
                 ||  (mime_e != eENCOD_None      &&
                      m_e    != eENCOD_None      &&  mime_e != m_e))) {
                const char* c;
                size_t len;
                char* t;
                for (s += 15; *s; s++) {
                    if (!isspace((unsigned char)(*s)))
                        break;
                }
                if (!(c = strchr(s, '\n')))
                    c = s + strlen(s);
                if (c > s  &&  c[-1] == '\r')
                    c--;
                len = (size_t)(c - s);
                if ((t = (char*) malloc(len + 1)) != 0) {
                    memcpy(t, s, len);
                    t[len] = '\0';
                }
                if (!MIME_ComposeContentTypeEx(mime_t, mime_s, mime_e,
                                               c_t, sizeof(c_t))) {
                    *c_t = '\0';
                }
                CORE_LOGF_X(3, eLOG_Warning,
                            ("[%s]  Content-Type mismatch: "
                             "%s%s%s%s%s%s%s", service,
                             t  &&  *t           ? "specified=<"  : "",
                             t  &&  *t           ? t              : "",
                             t  &&  *t           ? ">"            : "",
                             t  &&  *t  &&  *c_t ? ", "           : "",
                             *c_t                ? "configured=<" : "",
                             *c_t                ? c_t            : "",
                             *c_t                ? ">"            : ""));
                if (t)
                    free(t);
            }
#endif
            return 1/*true*/;
        }
    }
    return 0/*false*/;
}
Пример #17
0
/* 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;
}
Пример #18
0
/* Try to fix connection parameters (called for an unconnected connector) */
static int/*bool*/ s_Adjust(SHttpConnector* uuu,
                            char**          retry,
                            EReadMode       read_mode)
{
    assert(!uuu->sock  &&  uuu->can_connect != eCC_None);

    /* we're here because something is going wrong */
    if (++uuu->failure_count >= uuu->net_info->max_try) {
        if (*retry)
            free(*retry);
        if (read_mode != eRM_DropUnread  &&  uuu->failure_count > 1) {
            CORE_LOGF_X(1, eLOG_Error,
                        ("[HTTP]  Too many failed attempts (%d),"
                         " giving up", uuu->failure_count));
        }
        uuu->can_connect = eCC_None;
        return 0/*failure*/;
    }

    /* adjust info before yet another connection attempt */
    if (*retry) {
        int fail/*parse*/;
        assert(**retry);
        if (**retry != '?') {
            if (uuu->net_info->req_method == eReqMethod_Get  ||  !uuu->w_len
                ||  (uuu->flags & fHCC_InsecureRedirect)) {
                int/*bool*/secure = uuu->net_info->scheme == eURL_Https ? 1 : 0;
                *uuu->net_info->args = '\0'/*arguments not inherited*/;
                fail = !ConnNetInfo_ParseURL(uuu->net_info, *retry);
                if (!fail  &&  secure  &&  uuu->net_info->scheme != eURL_Https
                    &&  !(uuu->flags & fHCC_InsecureRedirect)) {
                    fail = -1;
                }
            } else
                fail = -1;
        } else
            fail = 1;
        if (fail) {
            CORE_LOGF_X(2, eLOG_Error,
                        ("[HTTP]  %s to redirect to \"%s\"",
                         fail < 0 ? "Prohibited" : "Unable", *retry));
        }
        free(*retry);
        if (fail) {
            uuu->can_connect = eCC_None;
            return 0/*failure*/;
        }
    } else if (!uuu->adjust_net_info
               ||  uuu->adjust_net_info(uuu->net_info,
                                        uuu->adjust_data,
                                        uuu->failure_count) == 0) {
        if (read_mode != eRM_DropUnread  &&  uuu->failure_count > 1) {
            CORE_LOGF_X(3, eLOG_Error,
                        ("[HTTP]  Retry attempts (%d) exhausted,"
                         " giving up", uuu->failure_count));
        }
        uuu->can_connect = eCC_None;
        return 0/*failure*/;
    }

    ConnNetInfo_AdjustForHttpProxy(uuu->net_info);
    return 1/*success*/;
}
Пример #19
0
static int/*bool*/ s_Update(SERV_ITER iter, const char* text, int code)
{
    static const char server_info[] = "Server-Info-";
    struct SDISPD_Data* data = (struct SDISPD_Data*) iter->data;
    int/*bool*/ failure;

    if (strncasecmp(text, server_info, sizeof(server_info) - 1) == 0
        &&  isdigit((unsigned char) text[sizeof(server_info) - 1])) {
        const char* name;
        SSERV_Info* info;
        unsigned int d1;
        char* s;
        int d2;

        text += sizeof(server_info) - 1;
        if (sscanf(text, "%u: %n", &d1, &d2) < 1  ||  d1 < 1)
            return 0/*not updated*/;
        if (iter->ismask  ||  iter->reverse_dns) {
            char* c;
            if (!(s = strdup(text + d2)))
                return 0/*not updated*/;
            name = s;
            while (*name  &&  isspace((unsigned char)(*name)))
                name++;
            if (!*name) {
                free(s);
                return 0/*not updated*/;
            }
            for (c = s + (name - s);  *c;  c++) {
                if (isspace((unsigned char)(*c)))
                    break;
            }
            *c++ = '\0';
            d2 += (int)(c - s);
        } else {
            s = 0;
            name = "";
        }
        info = SERV_ReadInfoEx(text + d2, name);
        if (s)
            free(s);
        if (info) {
            if (info->time != NCBI_TIME_INFINITE)
                info->time += iter->time; /* expiration time now */
            if (s_AddServerInfo(data, info))
                return 1/*updated*/;
            free(info);
        }
    } else if (((failure = strncasecmp(text, HTTP_DISP_FAILURES,
                                       sizeof(HTTP_DISP_FAILURES) - 1) == 0)
                ||  strncasecmp(text, HTTP_DISP_MESSAGES,
                                sizeof(HTTP_DISP_MESSAGES) - 1) == 0)  &&
               isspace((unsigned char) text[sizeof(HTTP_DISP_FAILURES) - 1])) {
        assert(sizeof(HTTP_DISP_FAILURES) == sizeof(HTTP_DISP_MESSAGES));
#if defined(_DEBUG) && !defined(NDEBUG)
        if (data->net_info->debug_printout) {
            text += sizeof(HTTP_DISP_FAILURES) - 1;
            while (*text  &&  isspace((unsigned char)(*text)))
                text++;
            CORE_LOGF_X(2, failure ? eLOG_Warning : eLOG_Note,
                        ("[%s]  %s", data->net_info->svc, text));
        }
#endif /*_DEBUG && !NDEBUG*/
        if (failure) {
            if (code)
                data->fail = 1;
            return 1/*updated*/;
        }
        /* NB: a mere message does not constitute an update */
    }

    return 0/*not updated*/;
}
Пример #20
0
/* 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;
}
Пример #21
0
static HEAP s_GetHeapCopy(TNCBI_Time now)
{
    enum {
        eNone     = 0,
        eAgain    = 1,
        eFallback = 2
    } retry   = eNone;
    HEAP heap = 0;
    HEAP lbsm;

    for (;;) {
        const SLBSM_Version *c, *v;
        int serial = 0;

        CORE_LOCK_WRITE;

        if (s_Heap) {
            c = LBSM_GetVersion(s_Heap);
            assert(c  &&  c->major == LBSM_HEAP_VERSION_MAJ);
            assert((void*) c == (void*) HEAP_Base(s_Heap));
            if (c->entry.good < now) {
#ifdef LBSM_DEBUG
                CORE_LOGF(eLOG_Trace,
                          ("Cached LBSM heap[%p, %p, %d] expired, dropped",
                           s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap)));
#endif /*LBSM_DEBUG*/
                HEAP_Destroy(s_Heap);
                s_Heap = 0;
            }
#ifdef LBSM_DEBUG
            else {
                CORE_LOGF(eLOG_Trace,
                          ("Cached LBSM heap[%p, %p, %d] valid",
                           s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap)));
            }
#endif /*LBSM_DEBUG*/
        } else
            c = 0/*dummy for compiler not to complain*/;
        
        if (!(lbsm = LBSM_Shmem_Attach(retry == eFallback))
            ||  (serial = HEAP_Serial(lbsm)) <= 0) {
            if (lbsm) {
                CORE_LOGF_X(1, eLOG_Error,
                            ("Bad serial (%d) from LBSM heap attach", serial));
            } /* else, an error has already been posted */
            break;
        }

        if (!(v = LBSM_GetVersion(lbsm))
            ||  (v->major < LBSM_HEAP_VERSION_MAJ
                 ||  (v->major == LBSM_HEAP_VERSION_MAJ
                      &&  v->minor < LBSM_HEAP_VERSION_MIN))) {
            if (v) {
                CORE_LOGF_X(2, eLOG_Error,
                            ("LBSM heap[%d] version mismatch"
                             " (current=%hu.%hu, expected=%u.%u+)",
                             serial, v->major, v->minor,
                             LBSM_HEAP_VERSION_MAJ, LBSM_HEAP_VERSION_MIN));
            } else {
                CORE_LOGF_X(3, eLOG_Error,
                            ("LBSM heap[%d] has no version", serial));
            }
            break;
        }

        if (v->entry.good < now) {
            CORE_LOGF_X(4, eLOG_Warning,
                        ("LBSM heap[%d] is out-of-date"
                         " (current=%lu, expiry=%lu, delta=%lu)%s", serial,
                         (unsigned long) now, (unsigned long) v->entry.good,
                         (unsigned long) now -(unsigned long) v->entry.good,
                         !retry  &&  serial > 1 ? ", re-trying" : ""));
            if (!retry  &&   serial > 1) {
                LBSM_Shmem_Detach(heap);
                retry = eFallback;
                CORE_UNLOCK;
                continue;
            }
            if (s_Heap) {
#ifdef LBSM_DEBUG
                CORE_LOGF(eLOG_Trace,
                          ("Cached LBSM heap[%p, %p, %d] dropped",
                           s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap)));
#endif /*LBSM_DEBUG*/
                HEAP_Destroy(s_Heap);
                s_Heap = 0;
            }
            break;
        }
        assert((void*) v == (void*) HEAP_Base(lbsm));

        if (s_Heap) {
            if (c->count == v->count  &&  c->cksum == v->cksum) {
#ifdef LBSM_DEBUG
                CORE_LOGF(eLOG_Trace,
                          ("Cached LBSM heap[%p, %p, %d] used",
                           s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap)));
#endif /*LBSM_DEBUG*/
                heap = s_Heap;
                break;
            }
#ifdef LBSM_DEBUG
            CORE_LOGF(eLOG_Trace,
                      ("Cached LBSM heap[%p, %p, %d] is stale, dropped",
                       s_Heap, HEAP_Base(s_Heap), HEAP_Serial(s_Heap)));
#endif /*LBSM_DEBUG*/
            HEAP_Destroy(s_Heap);
            s_Heap = 0;
        }

        if (!(heap = HEAP_Copy(lbsm, 0, -serial))) {
            CORE_LOGF_ERRNO_X(6, eLOG_Error, errno,
                              ("Unable to copy LBSM heap[%d]", serial));
            break;
        }

        if (s_VerifyChecksum(heap, v->cksum)) {
#ifdef LBSM_DEBUG
            CORE_LOGF(eLOG_Trace,
                      ("Cached LBSM heap[%p, %p, %d] renewed",
                       heap, HEAP_Base(heap), HEAP_Serial(heap)));
#endif /*LBSM_DEBUG*/
            s_Heap = heap;
            break;
        }

        CORE_LOGF_X(7, retry ? eLOG_Error : eLOG_Warning,
                    ("LBSM heap[%p, %p, %d]%s checksum failure%s",
                     (void*) heap, HEAP_Base(heap), HEAP_Serial(heap),
                     retry == eAgain ? " persistent" : "",
                     retry           ? ""            : ", re-trying"));

        verify(HEAP_Destroy(heap) == 0);
        heap = 0;
        if (retry)
            break;

        LBSM_Shmem_Detach(lbsm);
        retry = eAgain;
        CORE_UNLOCK;
    }

    assert(!heap  ||  heap != lbsm);
    if (heap  &&  heap == s_Heap)
        verify(HEAP_AddRef(s_Heap) > 1);

    LBSM_Shmem_Detach(lbsm);
    CORE_UNLOCK;
    return heap;
}