1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright (C) 2019-2025 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>
#include <asiolink/asio_wrapper.h>
#include <dhcp/iface_mgr.h>
#include <http/connection_pool.h>
#include <http/listener.h>
#include <http/listener_impl.h>

using namespace isc::asiolink;
using namespace isc::dhcp;
namespace ph = std::placeholders;

namespace isc {
namespace http {

HttpListenerImpl::HttpListenerImpl(const IOServicePtr& io_service,
                                   const asiolink::IOAddress& server_address,
                                   const unsigned short server_port,
                                   const TlsContextPtr& tls_context,
                                   const HttpResponseCreatorFactoryPtr& creator_factory,
                                   const long request_timeout,
                                   const long idle_timeout)
    : io_service_(io_service), tls_context_(tls_context), acceptor_(),
      endpoint_(), connections_(new HttpConnectionPool()),
      creator_factory_(creator_factory),
      request_timeout_(request_timeout), idle_timeout_(idle_timeout),
      use_external_(false) {
    // Create the TCP or TLS acceptor.
    if (!tls_context) {
        acceptor_.reset(new HttpAcceptor(io_service));
    } else {
        acceptor_.reset(new HttpsAcceptor(io_service));
    }

    // Try creating an endpoint. This may cause exceptions.
    try {
        endpoint_.reset(new TCPEndpoint(server_address, server_port));

    } catch (...) {
        isc_throw(HttpListenerError, "unable to create TCP endpoint for "
                  << server_address << ":" << server_port);
    }

    // The factory must not be null.
    if (!creator_factory_) {
        isc_throw(HttpListenerError, "HttpResponseCreatorFactory must not"
                  " be null");
    }

    // Request timeout is signed and must be greater than 0.
    if (request_timeout_ <= 0) {
        isc_throw(HttpListenerError, "Invalid desired HTTP request timeout "
                  << request_timeout_);
    }

    // Idle persistent connection timeout is signed and must be greater than 0.
    if (idle_timeout_ <= 0) {
        isc_throw(HttpListenerError, "Invalid desired HTTP idle persistent connection"
                  " timeout " << idle_timeout_);
    }
}

const TCPEndpoint&
HttpListenerImpl::getEndpoint() const {
    return (*endpoint_);
}

const TlsContextPtr&
HttpListenerImpl::getTlsContext() const {
    return (tls_context_);
}

void
HttpListenerImpl::setTlsContext(const TlsContextPtr& context) {
    tls_context_ = context;
}

int
HttpListenerImpl::getNative() const {
    return (acceptor_ ? acceptor_->getNative() : -1);
}

void
HttpListenerImpl::addExternalSockets(bool use_external) {
    use_external_ = use_external;
}

void
HttpListenerImpl::start() {
    try {
        acceptor_->open(*endpoint_);
        if (use_external_) {
            IfaceMgr::instance().addExternalSocket(acceptor_->getNative(), 0);
        }
        acceptor_->setOption(HttpAcceptor::ReuseAddress(true));
        acceptor_->bind(*endpoint_);
        acceptor_->listen();

    } catch (const boost::system::system_error& ex) {
        stop();
        isc_throw(HttpListenerError, "unable to setup TCP acceptor for "
                  "listening to the incoming HTTP requests: " << ex.what());
    }

    accept();
}

void
HttpListenerImpl::stop() {
    connections_->stopAll();
    if (use_external_) {
        IfaceMgr::instance().deleteExternalSocket(acceptor_->getNative());
    }
    acceptor_->close();
}

void
HttpListenerImpl::accept() {
    // In some cases we may need HttpResponseCreator instance per connection.
    // But, the factory may also return the same instance each time. It
    // depends on the use case.
    HttpResponseCreatorPtr response_creator = creator_factory_->create();
    HttpAcceptorCallback acceptor_callback =
        std::bind(&HttpListenerImpl::acceptHandler, shared_from_this(), ph::_1);
    HttpConnectionPtr conn = createConnection(response_creator,
                                              acceptor_callback);
    // Transmit the use external sockets flag.
    if (use_external_) {
        conn->addExternalSockets(true);
    }
    // Add this new connection to the pool.
    connections_->start(conn);
}

void
HttpListenerImpl::acceptHandler(const boost::system::error_code&) {
    // The new connection has arrived. Set the acceptor to continue
    // accepting new connections.
    accept();
}

HttpConnectionPtr
HttpListenerImpl::createConnection(const HttpResponseCreatorPtr& response_creator,
                                   const HttpAcceptorCallback& callback) {
    HttpConnectionPtr
        conn(new HttpConnection(io_service_, acceptor_, tls_context_,
                                connections_, response_creator, callback,
                                request_timeout_, idle_timeout_));
    return (conn);
}

} // end of namespace isc::http
} // end of namespace isc