Exemplo n.º 1
0
void HTTPAcceptor::_acceptConnection()
{
    // This function cannot be called on an invalid socket!

    PEGASUS_ASSERT(_rep != 0);

    // Accept the connection (populate the address):

    struct sockaddr* accept_address;
    SocketLength address_size;

    if (_connectionType == LOCAL_CONNECTION)
    {
#ifndef PEGASUS_DISABLE_LOCAL_DOMAIN_SOCKET
        accept_address =
            reinterpret_cast<struct sockaddr*>(new struct sockaddr_un);
        address_size = sizeof(struct sockaddr_un);
#else
        PEGASUS_ASSERT(false);
#endif
    }
    else
    {
#ifdef PEGASUS_ENABLE_IPV6
        accept_address =
           reinterpret_cast<struct sockaddr*>
           (new struct sockaddr_storage);
        address_size = sizeof(struct sockaddr_storage);
#else
        accept_address =
            reinterpret_cast<struct sockaddr*>(new struct sockaddr_in);
        address_size = sizeof(struct sockaddr_in);
#endif
    }

    // It is not necessary to handle EINTR errors from this accept() call.
    // An EINTR error should not occur on a non-blocking socket.  If the
    // listen socket is blocking and EINTR occurs, the new socket connection
    // is not accepted here.

    // EAGAIN errors are also not handled here.  An EAGAIN error should not
    // occur after select() indicates that the listen socket is available for
    // reading.  If the accept() fails with an EAGAIN error code, a new
    // connection is not accepted here.

    SocketHandle socket = accept(_rep->socket, accept_address, &address_size);

    if (socket == PEGASUS_SOCKET_ERROR)
    {
        // the remote connection is invalid, destroy client address.
        delete accept_address;

        // TCPIP is down reconnect this acceptor
        if (getSocketError() == PEGASUS_NETWORK_TCPIP_STOPPED)
        {
            PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1,
                "Socket has an IO error. TCP/IP down. Try to reconnect.");

            reconnectConnectionSocket();

            return;
        }
        PEG_TRACE((
            TRC_DISCARDED_DATA,
            Tracer::LEVEL1,
            "HTTPAcceptor: accept() failed.  errno: %u",
            errno));
        return;
    }

    // Use an AutoPtr to ensure the socket handle is closed on exception
    AutoPtr<SocketHandle, CloseSocketHandle> socketPtr(&socket);

#ifndef PEGASUS_OS_TYPE_WINDOWS
    // We need to ensure that the socket number is not higher than
    // what fits into FD_SETSIZE, because we else won't be able to select on it
    // and won't ever communicate correct on that socket.
    if (socket >= FD_SETSIZE)
    {
        // the remote connection is invalid, destroy client address.
        delete accept_address;

        PEG_TRACE(
            (TRC_DISCARDED_DATA,
             Tracer::LEVEL1,
             "HTTPAcceptor out of available sockets."
                 "accept() returned too large socket number %u."
                 "Closing connection to the new client.",
             socket));

        return;
    }
#endif

    String ipAddress;

    if (_connectionType == LOCAL_CONNECTION)
    {
        ipAddress = "localhost";
    }
    else
    {
#ifdef PEGASUS_ENABLE_IPV6
        char ipBuffer[PEGASUS_INET6_ADDRSTR_LEN];
        int rc;
        if ((rc = System::getNameInfo(accept_address,
                address_size,
                ipBuffer,
                PEGASUS_INET6_ADDRSTR_LEN,
                0,
                0,
                NI_NUMERICHOST)))
        {
            PEG_TRACE((
                TRC_DISCARDED_DATA,
                Tracer::LEVEL1,
                "HTTPAcceptor: getnameinfo() failed.  rc: %d",
                rc));
            delete accept_address;
            return;
        }
        ipAddress = ipBuffer;
#else
        unsigned char* sa = reinterpret_cast<unsigned char*>(
            &reinterpret_cast<struct sockaddr_in*>(
                accept_address)->sin_addr.s_addr);
        char ipBuffer[32];
        sprintf(ipBuffer, "%u.%u.%u.%u", sa[0], sa[1], sa[2], sa[3]);
        ipAddress = ipBuffer;
#endif
    }

    delete accept_address;

// set the close on exec flag
#if !defined(PEGASUS_OS_TYPE_WINDOWS) && !defined(PEGASUS_OS_VMS)
    int sock_flags;
    if ((sock_flags = fcntl(socket, F_GETFD, 0)) < 0)
    {
        PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1,
            "HTTPAcceptor: fcntl(F_GETFD) failed");
    }
    else
    {
        sock_flags |= FD_CLOEXEC;
        if (fcntl(socket, F_SETFD, sock_flags) < 0)
        {
            PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1,
                "HTTPAcceptor: fcntl(F_SETFD) failed");
        }
    }
#endif


    PEG_TRACE((
        TRC_HTTP,
        Tracer::LEVEL3,
        "HTTPAcceptor - accept() success.  Socket: %u",
        socket));

    SharedPtr<MP_Socket> mp_socket(new MP_Socket(
        socket, _sslcontext, _sslContextObjectLock, ipAddress));
    // mp_socket now has responsibility for closing the socket handle
    socketPtr.release();

    mp_socket->disableBlocking();

    {
#ifndef PEGASUS_INTEGERS_BOUNDARY_ALIGNED
        AutoMutex lock(_socketWriteTimeoutMutex);
#endif
        mp_socket->setSocketWriteTimeout(_socketWriteTimeout);
    }

    // Perform the SSL handshake, if applicable.

    Sint32 socketAcceptStatus = mp_socket->accept();

    if (socketAcceptStatus < 0)
    {
        PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1,
            "HTTPAcceptor: SSL_accept() failed");
        return;
    }

    // Create a new connection and add it to the connection list:

    AutoPtr<HTTPConnection> connection(new HTTPConnection(
        _monitor,
        mp_socket,
        ipAddress,
        this,
        _outputMessageQueue));

    if (HTTPConnection::getIdleConnectionTimeout())
    {
        Time::gettimeofday(&connection->_idleStartTime);
    }

    if (socketAcceptStatus == 0)
    {
        PEG_TRACE_CSTRING(TRC_HTTP, Tracer::LEVEL1,
            "HTTPAcceptor: SSL_accept() pending");
        connection->_acceptPending = true;
        Time::gettimeofday(&connection->_acceptPendingStartTime);
    }

    // Solicit events on this new connection's socket:
    int index;

    if (-1 ==  (index = _monitor->solicitSocketMessages(
            connection->getSocket(),
            SocketMessage::READ | SocketMessage::EXCEPTION,
            connection->getQueueId(), MonitorEntry::TYPE_CONNECTION)) )
    {
        PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1,
            "HTTPAcceptor::_acceptConnection: Attempt to allocate entry in "
                "_entries table failed.");
        return;
    }

    connection->_entry_index = index;
    AutoMutex autoMut(_rep->_connection_mut);
    _rep->connections.append(connection.get());
    connection.release();
}
Exemplo n.º 2
0
HTTPConnection* HTTPConnector::connect(
    const String& host,
    const Uint32 portNumber,
    SSLContext * sslContext,
    Uint32 timeoutMilliseconds,
    MessageQueue* outputMessageQueue)
{
    PEG_METHOD_ENTER(TRC_HTTP, "HTTPConnector::connect()");

#ifdef PEGASUS_OS_PASE
    AutoPtr<PaseCcsid> ccsid;
#endif

    SocketHandle socket = PEGASUS_INVALID_SOCKET;
    // Use an AutoPtr to ensure the socket handle is closed on exception
    AutoPtr<SocketHandle, CloseSocketHandle> socketPtr(&socket);

#ifndef PEGASUS_DISABLE_LOCAL_DOMAIN_SOCKET
    if (0 == host.size())
    {
        // Set up the domain socket for a local connection

        sockaddr_un address;

#ifdef PEGASUS_OS_PASE
        // PASE needs ccsid 819 to perform domain socket operation
        int orig_ccsid;
        orig_ccsid = _SETCCSID(-1);
        if (orig_ccsid == -1)
        {
            PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL2,
                    "HTTPConnector::connect() Can not get current PASE CCSID.");
            orig_ccsid = 1208;
        }
        ccsid.reset(new PaseCcsid(819, orig_ccsid));
#endif

        memset(&address, 0, sizeof(address));
        address.sun_family = AF_UNIX;
        strcpy(address.sun_path, PEGASUS_LOCAL_DOMAIN_SOCKET_PATH);

        socket = Socket::createSocket(AF_UNIX, SOCK_STREAM, 0);
        if (socket == PEGASUS_INVALID_SOCKET)
        {
            PEG_METHOD_EXIT();
            throw CannotCreateSocketException();
        }

        Socket::disableBlocking(socket);

        // Connect the socket to the address:

        if (!Socket::timedConnect(
                socket,
                reinterpret_cast<sockaddr*>(&address),
                sizeof(address),
                timeoutMilliseconds))
        {
            MessageLoaderParms parms(
                "Common.HTTPConnector.CONNECTION_FAILED_LOCAL_CIM_SERVER",
                "Cannot connect to local CIM server. Connection failed.");
            PEG_METHOD_EXIT();
            throw CannotConnectException(parms);
        }
    }
    else
#endif
    {
        // Set up the IP socket connection

        // Make the internet address:
#ifdef PEGASUS_ENABLE_IPV6
        struct addrinfo *addrInfo, *addrInfoRoot = NULL;
#else
        sockaddr_in address;
#endif
#ifdef PEGASUS_ENABLE_IPV6
        if (!_MakeAddress(
                 (const char*)host.getCString(),
                 portNumber,
                 (void**)(void*)&addrInfoRoot))
#else
        if (!_MakeAddress((const char*)host.getCString(), portNumber, address))
#endif
        {
            char scratch[22];
            Uint32 n;
            const char * portStr = Uint32ToString(scratch, portNumber, n);
            PEG_METHOD_EXIT();
            throw InvalidLocatorException(host+":"+String(portStr,n));
        }

#ifdef PEGASUS_ENABLE_IPV6
        addrInfo = addrInfoRoot;
        while (addrInfo)
        {
            // Create the socket:
            socket = Socket::createSocket(addrInfo->ai_family,
                addrInfo->ai_socktype, addrInfo->ai_protocol);
#else
            socket = Socket::createSocket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
#endif
            if (socket == PEGASUS_INVALID_SOCKET)
            {
#ifdef PEGASUS_ENABLE_IPV6
                freeaddrinfo(addrInfoRoot);
#endif
                PEG_METHOD_EXIT();
                throw CannotCreateSocketException();
            }

#ifndef PEGASUS_OS_TYPE_WINDOWS
            // We need to ensure that the socket number is not higher than
            // what fits into FD_SETSIZE,because we else won't be able to select
            // on it and won't ever communicate correct on that socket.
            if (socket >= FD_SETSIZE)
            {
# ifdef PEGASUS_ENABLE_IPV6
                freeaddrinfo(addrInfoRoot);
# endif
                // the socket is useless to us, close it
                Socket::close(socket);

                PEG_TRACE(
                    (TRC_DISCARDED_DATA,
                     Tracer::LEVEL1,
                     "createSocket() returned too large socket number %d."
                         "Cannot connect to %s:%d. Connection failed.",
                     socket,
                     (const char*) host.getCString(),
                     portNumber));

                PEG_METHOD_EXIT();
                throw CannotCreateSocketException();
    }
#endif

            Socket::disableBlocking(socket);

            // Connect the socket to the address:
            if (!Socket::timedConnect(
                    socket,
#ifdef PEGASUS_ENABLE_IPV6
                    reinterpret_cast<sockaddr*>(addrInfo->ai_addr),
                    addrInfo->ai_addrlen,
#else
                    reinterpret_cast<sockaddr*>(&address),
                    sizeof(address),
#endif
                    timeoutMilliseconds))
            {
#ifdef PEGASUS_ENABLE_IPV6
                addrInfo = addrInfo->ai_next;
                if (addrInfo)
                {
                    Socket::close(socket);
                    continue;
                }
#endif
                char scratch[22];
                Uint32 n;
                const char * portStr = Uint32ToString(scratch, portNumber, n);
                MessageLoaderParms parms(
                    "Common.HTTPConnector.CONNECTION_FAILED_TO",
                    "Cannot connect to $0:$1. Connection failed.",
                    host,
                    portStr);
#ifdef PEGASUS_ENABLE_IPV6
                freeaddrinfo(addrInfoRoot);
#endif
                PEG_METHOD_EXIT();
                throw CannotConnectException(parms);
            }
#ifdef PEGASUS_ENABLE_IPV6
            break;
        }
        freeaddrinfo(addrInfoRoot);
#endif
    }

    // Create HTTPConnection object:

    SharedPtr<MP_Socket> mp_socket(new MP_Socket(socket, sslContext, 0));
    // mp_socket now has responsibility for closing the socket handle
    socketPtr.release();

    if (mp_socket->connect(timeoutMilliseconds) < 0)
    {
        char scratch[22];
        Uint32 n;
        const char * portStr = Uint32ToString(scratch, portNumber, n);
        MessageLoaderParms parms(
            "Common.HTTPConnector.CONNECTION_FAILED_TO",
            "Cannot connect to $0:$1. Connection failed.",
            host,
            portStr);
        PEG_METHOD_EXIT();
        throw CannotConnectException(parms);
    }

    AutoPtr<HTTPConnection> connection(new HTTPConnection(
        _monitor,
        mp_socket,
        String::EMPTY,
        0,
        outputMessageQueue));

    // Solicit events on this new connection's socket:
    int index;

    if (-1 == (index = _monitor->solicitSocketMessages(
            connection->getSocket(),
            connection->getQueueId(), MonitorEntry::TYPE_CONNECTION)))
    {
        PEG_TRACE_CSTRING(TRC_DISCARDED_DATA, Tracer::LEVEL1,
            "HTTPConnector::connect: Attempt to allocate entry in "
                "_entries table failed.");
        (connection->getMPSocket()).close();
    }

    connection->_entry_index = index;
    _rep->connections.append(connection.get());
    PEG_METHOD_EXIT();
    return connection.release();
}