int tunnel()

in modules/http/mod-ssltunnel.cpp [141:252]


int tunnel(conn_rec* const conn, const string& ca, const string& cert, const string& key, const string& url, const string& preamble, const gc_pool& p, unused ap_filter_t* const ifilter, ap_filter_t* const ofilter) {

    // Create input/output bucket brigades
    apr_bucket_brigade* const ib = apr_brigade_create(pool(p), conn->bucket_alloc);
    apr_bucket_brigade* const ob = apr_brigade_create(pool(p), conn->bucket_alloc);

    // Get client connection socket
    apr_socket_t* const csock = (apr_socket_t*)ap_get_module_config(conn->conn_config, &core_module);

    // Open connection to target
    const http::CURLSession cs(ca, cert, key, emptyString, 0);
    const failable<bool> crc = http::connect(url, cs);
    if (!hasContent(crc))
        return abort(conn, csock, reason(crc));
    apr_socket_t* const tsock = http::sock(cs);

    // Send preamble
    if (length(preamble) != 0) {
        debug(preamble, "modssltunnel::tunnel::sendPreambleToTarget");
        const failable<bool> src = http::send(c_str(preamble), length(preamble), cs);
        if (!hasContent(src))
            return abort(conn, csock, string("Couldn't send to target: ") + reason(src));
    }

    // Create a pollset for the client and target sockets
    apr_pollset_t* pollset;
    const apr_status_t cprc = apr_pollset_create(&pollset, 2, pool(p), 0);
    if (cprc != APR_SUCCESS)
        return abort(conn, csock, http::apreason(cprc));
    const apr_pollfd_t* cpollfd = http::pollfd(csock, APR_POLLIN, p);
    apr_pollset_add(pollset, cpollfd);
    const apr_pollfd_t* tpollfd = http::pollfd(tsock, APR_POLLIN, p);
    apr_pollset_add(pollset, tpollfd);

    // Relay traffic in both directions until end of stream
    const apr_pollfd_t* pollfds = cpollfd;
    apr_int32_t pollcount = 1;
    for(;;) {
        for (; pollcount > 0; pollcount--, pollfds++) {
            if (pollfds->rtnevents & APR_POLLIN) {
                if (pollfds->desc.s == csock) {

                    // Receive buckets from client
                    const apr_status_t getrc = ap_get_brigade(conn->input_filters, ib, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
                    if (getrc != APR_SUCCESS)
                        return abort(conn, csock, string("Couldn't receive from client"));

                    for (apr_bucket* bucket = APR_BRIGADE_FIRST(ib); bucket != APR_BRIGADE_SENTINEL(ib); bucket = APR_BUCKET_NEXT(bucket)) {
                        if (APR_BUCKET_IS_FLUSH(bucket))
                            continue;

                        // Client connection closed
                        if (APR_BUCKET_IS_EOS(bucket))
                            return close(conn, csock);

                        const char *data;
                        apr_size_t rl;
                        apr_bucket_read(bucket, &data, &rl, APR_BLOCK_READ);
                        if (rl > 0) {
                            debug(string(data, rl), "modssltunnel::tunnel::sendToTarget");

                            // Send to target
                            const failable<bool> src = http::send(data, rl, cs);
                            if (!hasContent(src))
                                return abort(conn, csock, string("Couldn't send to target: ") + reason(src));
                        }
                    }
                    apr_brigade_cleanup(ib);
                } else {

                    // Receive from target
                    char data[8192];
                    const failable<size_t> frl = http::recv(data, sizeof(data), cs);
                    if (!hasContent(frl))
                        return abort(conn, csock, string("Couldn't receive from target") + reason(frl));
                    const size_t rl = content(frl);

                    // Target connection closed
                    if (rl == 0)
                        return close(conn, csock);


                    // Send bucket to client
                    debug(string(data, rl), "modssltunnel::tunnel::sendToClient");
                    APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_transient_create(data, rl, conn->bucket_alloc));
                    APR_BRIGADE_INSERT_TAIL(ob, apr_bucket_flush_create(conn->bucket_alloc));
                    if (ap_pass_brigade(ofilter, ob) != APR_SUCCESS)
                        return abort(conn, csock, "Couldn't send data bucket to client");
                    apr_brigade_cleanup(ob);
                }
            }

            // Error
            if (pollfds->rtnevents & (APR_POLLERR | APR_POLLHUP | APR_POLLNVAL)) {
                if (pollfds->desc.s == csock) 
                    return abort(conn, csock, "Couldn't receive from client");
                else
                    return abort(conn, csock, "Couldn't receive from target");
            }
        }

        // Poll the client and target sockets
        debug("modssltunnel::tunnel::poll");
        const apr_status_t pollrc = apr_pollset_poll(pollset, -1, &pollcount, &pollfds);
        if (pollrc != APR_SUCCESS)
            return abort(conn, csock, "Couldn't poll sockets");
        debug(pollcount, "modssltunnel::tunnel::pollfds");
    }

    // Close client connection
    return close(conn, csock);
}