Kea  1.5.0
connection.cc
Go to the documentation of this file.
1 // Copyright (C) 2017-2018 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
10 #include <http/connection.h>
11 #include <http/connection_pool.h>
12 #include <http/http_log.h>
13 #include <http/http_messages.h>
14 #include <boost/bind.hpp>
15 
16 using namespace isc::asiolink;
17 
18 namespace {
19 
23 constexpr size_t MAX_LOGGED_MESSAGE_SIZE = 1024;
24 
25 }
26 
27 namespace isc {
28 namespace http {
29 
30 void
31 HttpConnection::
32 SocketCallback::operator()(boost::system::error_code ec, size_t length) {
33  if (ec.value() == boost::asio::error::operation_aborted) {
34  return;
35  }
36  callback_(ec, length);
37 }
38 
39 HttpConnection:: HttpConnection(asiolink::IOService& io_service,
40  HttpAcceptor& acceptor,
41  HttpConnectionPool& connection_pool,
42  const HttpResponseCreatorPtr& response_creator,
43  const HttpAcceptorCallback& callback,
44  const long request_timeout,
45  const long idle_timeout)
46  : request_timer_(io_service),
47  request_timeout_(request_timeout),
48  idle_timeout_(idle_timeout),
49  socket_(io_service),
50  acceptor_(acceptor),
51  connection_pool_(connection_pool),
52  response_creator_(response_creator),
53  request_(response_creator_->createNewHttpRequest()),
54  parser_(new HttpRequestParser(*request_)),
55  acceptor_callback_(callback),
56  buf_(),
57  output_buf_() {
58  parser_->initModel();
59 }
60 
62  close();
63 }
64 
65 void
67  request_timer_.cancel();
68  socket_.close();
69 }
70 
71 void
72 HttpConnection::stopThisConnection() {
73  try {
75  HTTP_CONNECTION_STOP)
76  .arg(getRemoteEndpointAddressAsText());
77  connection_pool_.stop(shared_from_this());
78  } catch (...) {
79  LOG_ERROR(http_logger, HTTP_CONNECTION_STOP_FAILED);
80  }
81 }
82 
83 void
85  // Create instance of the callback. It is safe to pass the local instance
86  // of the callback, because the underlying boost functions make copies
87  // as needed.
88  HttpAcceptorCallback cb = boost::bind(&HttpConnection::acceptorCallback,
89  shared_from_this(),
90  boost::asio::placeholders::error);
91  try {
92  acceptor_.asyncAccept(socket_, cb);
93 
94  } catch (const std::exception& ex) {
95  isc_throw(HttpConnectionError, "unable to start accepting TCP "
96  "connections: " << ex.what());
97  }
98 }
99 
100 void
102  try {
103  TCPEndpoint endpoint;
104  // Create instance of the callback. It is safe to pass the local instance
105  // of the callback, because the underlying boost functions make copies
106  // as needed.
107  SocketCallback cb(boost::bind(&HttpConnection::socketReadCallback,
108  shared_from_this(),
109  boost::asio::placeholders::error,
110  boost::asio::placeholders::bytes_transferred));
111  socket_.asyncReceive(static_cast<void*>(buf_.data()), buf_.size(),
112  0, &endpoint, cb);
113 
114  } catch (...) {
115  stopThisConnection();
116  }
117 }
118 
119 void
120 HttpConnection::doWrite() {
121  try {
122  if (!output_buf_.empty()) {
123  // Create instance of the callback. It is safe to pass the local instance
124  // of the callback, because the underlying boost functions make copies
125  // as needed.
126  SocketCallback cb(boost::bind(&HttpConnection::socketWriteCallback,
127  shared_from_this(),
128  boost::asio::placeholders::error,
129  boost::asio::placeholders::bytes_transferred));
130  socket_.asyncSend(output_buf_.data(),
131  output_buf_.length(),
132  cb);
133  } else {
134  if (!request_->isPersistent()) {
135  stopThisConnection();
136 
137  } else {
138  reinitProcessingState();
139  doRead();
140  }
141  }
142  } catch (...) {
143  stopThisConnection();
144  }
145 }
146 
147 void
148 HttpConnection::asyncSendResponse(const ConstHttpResponsePtr& response) {
149  output_buf_ = response->toString();
150  doWrite();
151 }
152 
153 
154 void
155 HttpConnection::acceptorCallback(const boost::system::error_code& ec) {
156  if (!acceptor_.isOpen()) {
157  return;
158  }
159 
160  if (ec) {
161  stopThisConnection();
162  }
163 
164  acceptor_callback_(ec);
165 
166  if (!ec) {
168  HTTP_REQUEST_RECEIVE_START)
169  .arg(getRemoteEndpointAddressAsText())
170  .arg(static_cast<unsigned>(request_timeout_/1000));
171 
172  setupRequestTimer();
173  doRead();
174  }
175 }
176 
177 void
178 HttpConnection::socketReadCallback(boost::system::error_code ec, size_t length) {
179  if (ec) {
180  // IO service has been stopped and the connection is probably
181  // going to be shutting down.
182  if (ec.value() == boost::asio::error::operation_aborted) {
183  return;
184 
185  // EWOULDBLOCK and EAGAIN are special cases. Everything else is
186  // treated as fatal error.
187  } else if ((ec.value() != boost::asio::error::try_again) &&
188  (ec.value() != boost::asio::error::would_block)) {
189  stopThisConnection();
190 
191  // We got EWOULDBLOCK or EAGAIN which indicate that we may be able to
192  // read something from the socket on the next attempt. Just make sure
193  // we don't try to read anything now in case there is any garbage
194  // passed in length.
195  } else {
196  length = 0;
197  }
198  }
199 
200  // Receiving is in progress, so push back the timeout.
201  setupRequestTimer();
202 
203  if (length != 0) {
205  HTTP_DATA_RECEIVED)
206  .arg(length)
207  .arg(getRemoteEndpointAddressAsText());
208 
209  std::string s(&buf_[0], buf_[0] + length);
210  parser_->postBuffer(static_cast<void*>(buf_.data()), length);
211  parser_->poll();
212  }
213 
214  if (parser_->needData()) {
215  doRead();
216 
217  } else {
218  try {
219  request_->finalize();
220 
222  HTTP_CLIENT_REQUEST_RECEIVED)
223  .arg(getRemoteEndpointAddressAsText());
224 
226  HTTP_CLIENT_REQUEST_RECEIVED_DETAILS)
227  .arg(getRemoteEndpointAddressAsText())
228  .arg(parser_->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE));
229 
230  } catch (const std::exception& ex) {
232  HTTP_BAD_CLIENT_REQUEST_RECEIVED)
233  .arg(getRemoteEndpointAddressAsText())
234  .arg(ex.what());
235 
237  HTTP_BAD_CLIENT_REQUEST_RECEIVED_DETAILS)
238  .arg(getRemoteEndpointAddressAsText())
239  .arg(parser_->getBufferAsString(MAX_LOGGED_MESSAGE_SIZE));
240  }
241 
242  // Don't want to timeout if creation of the response takes long.
243  request_timer_.cancel();
244 
245  HttpResponsePtr response = response_creator_->createHttpResponse(request_);
247  HTTP_SERVER_RESPONSE_SEND)
248  .arg(response->toBriefString())
249  .arg(getRemoteEndpointAddressAsText());
250 
252  HTTP_SERVER_RESPONSE_SEND_DETAILS)
253  .arg(getRemoteEndpointAddressAsText())
254  .arg(HttpMessageParserBase::logFormatHttpMessage(response->toString(),
255  MAX_LOGGED_MESSAGE_SIZE));
256 
257  // Response created. Active timer again.
258  setupRequestTimer();
259 
260  asyncSendResponse(response);
261  }
262 }
263 
264 void
265 HttpConnection::socketWriteCallback(boost::system::error_code ec, size_t length) {
266  if (ec) {
267  // IO service has been stopped and the connection is probably
268  // going to be shutting down.
269  if (ec.value() == boost::asio::error::operation_aborted) {
270  return;
271 
272  // EWOULDBLOCK and EAGAIN are special cases. Everything else is
273  // treated as fatal error.
274  } else if ((ec.value() != boost::asio::error::try_again) &&
275  (ec.value() != boost::asio::error::would_block)) {
276  stopThisConnection();
277 
278  // We got EWOULDBLOCK or EAGAIN which indicate that we may be able to
279  // read something from the socket on the next attempt.
280  } else {
281  // Sending is in progress, so push back the timeout.
282  setupRequestTimer();
283 
284  doWrite();
285  }
286  }
287 
288 
289  if (length <= output_buf_.size()) {
290  // Sending is in progress, so push back the timeout.
291  setupRequestTimer();
292 
293  output_buf_.erase(0, length);
294  doWrite();
295 
296  } else {
297  output_buf_.clear();
298 
299  if (!request_->isPersistent()) {
300  stopThisConnection();
301 
302  } else {
303  reinitProcessingState();
304  doRead();
305  }
306  }
307 }
308 
309 void
310 HttpConnection::reinitProcessingState() {
311  request_ = response_creator_->createNewHttpRequest();
312  parser_.reset(new HttpRequestParser(*request_));
313  parser_->initModel();
314  setupIdleTimer();
315 }
316 
317 void
318 HttpConnection::setupRequestTimer() {
319  // Pass raw pointer rather than shared_ptr to this object,
320  // because IntervalTimer already passes shared pointer to the
321  // IntervalTimerImpl to make sure that the callback remains
322  // valid.
323  request_timer_.setup(boost::bind(&HttpConnection::requestTimeoutCallback,
324  this),
325  request_timeout_, IntervalTimer::ONE_SHOT);
326 }
327 
328 void
329 HttpConnection::setupIdleTimer() {
330  request_timer_.setup(boost::bind(&HttpConnection::idleTimeoutCallback,
331  this),
332  idle_timeout_, IntervalTimer::ONE_SHOT);
333 }
334 
335 void
336 HttpConnection::requestTimeoutCallback() {
338  HTTP_CLIENT_REQUEST_TIMEOUT_OCCURRED)
339  .arg(getRemoteEndpointAddressAsText());
340  HttpResponsePtr response =
341  response_creator_->createStockHttpResponse(request_,
343  asyncSendResponse(response);
344 }
345 
346 void
347 HttpConnection::idleTimeoutCallback() {
349  HTTP_IDLE_CONNECTION_TIMEOUT_OCCURRED)
350  .arg(getRemoteEndpointAddressAsText());
351  stopThisConnection();
352 }
353 
354 std::string
355 HttpConnection::getRemoteEndpointAddressAsText() const {
356  try {
357  if (socket_.getASIOSocket().is_open()) {
358  return (socket_.getASIOSocket().remote_endpoint().address().to_string());
359  }
360  } catch (...) {
361  }
362  return ("(unknown address)");
363 }
364 
365 
366 } // end of namespace isc::http
367 } // end of namespace isc
368 
isc::dhcp::PacketQueue
Interface for managing a queue of inbound DHCP packets.
Definition: packet_queue.h:50
isc::http::HttpConnectionPool::stop
void stop(const HttpConnectionPtr &connection)
Stops a connection and removes it from the pool.
Definition: connection_pool.cc:22
LOG_ERROR
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
isc::dhcp::PacketQueue::enqueuePacket
virtual void enqueuePacket(PacketTypePtr packet, const SocketInfo &source)=0
Adds a packet to the queue.
isc::log::DBGLVL_TRACE_DETAIL
const int DBGLVL_TRACE_DETAIL
Trace detailed operations.
Definition: log_dbglevels.h:71
isc::data::SimpleParser::getInteger
static int64_t getInteger(isc::data::ConstElementPtr scope, const std::string &name)
Returns an integer parameter from a scope.
Definition: lib/cc/simple_parser.cc:41
isc::http::HttpConnectionPool
Pool of active HTTP connections.
Definition: connection_pool.h:28
isc::http::HttpAcceptorCallback
boost::function< void(const boost::system::error_code &)> HttpAcceptorCallback
Type of the callback for the TCP acceptor used in this library.
Definition: http_acceptor.h:19
isc::http::HttpConnection::~HttpConnection
~HttpConnection()
Destructor.
Definition: connection.cc:61
unload
int unload()
This function is called when the library is unloaded.
Definition: ha_callouts.cc:239
isc::ha::HAServerType::DHCPv6
@ DHCPv6
isc::dhcp::InvalidQueueParameter
Invalid queue parameter exception.
Definition: packet_queue.h:27
isc::dhcp::PacketQueue6Ptr
boost::shared_ptr< PacketQueue< Pkt6Ptr > > PacketQueue6Ptr
Defines pointer to the DHCPv6 queue interface used at the application level.
Definition: packet_queue.h:139
isc::http::HttpResponsePtr
boost::shared_ptr< HttpResponse > HttpResponsePtr
Pointer to the HttpResponse object.
Definition: response.h:78
load
int load(LibraryHandle &handle)
This function is called when the library is loaded.
Definition: ha_callouts.cc:210
isc::http::ConstHttpResponsePtr
boost::shared_ptr< const HttpResponse > ConstHttpResponsePtr
Pointer to the const HttpResponse object.
Definition: response.h:84
connection_pool.h
isc::log::DBGLVL_TRACE_BASIC_DATA
const int DBGLVL_TRACE_BASIC_DATA
Trace data associated with the basic operations.
Definition: log_dbglevels.h:68
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc::Exception::what
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Definition: exceptions/exceptions.cc:32
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
http_log.h
isc::dhcp::IfaceMgr::instance
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
isc::http::http_logger
isc::log::Logger http_logger("http")
Defines the logger used within libkea-http library.
Definition: http_log.h:18
isc::dhcp::IfaceMgr::getPacketQueueMgr4
PacketQueueMgr4Ptr getPacketQueueMgr4()
Fetches the DHCPv4 packet queue manager.
Definition: iface_mgr.h:1022
isc::http::HttpResponseCreatorPtr
boost::shared_ptr< HttpResponseCreator > HttpResponseCreatorPtr
Pointer to the HttpResponseCreator object.
Definition: response_creator.h:17
isc::http::HttpRequestParser
A generic parser for HTTP requests.
Definition: request_parser.h:54
isc::http::HttpMessageParserBase::logFormatHttpMessage
static std::string logFormatHttpMessage(const std::string &message, const size_t limit=0)
Formats provided HTTP message for logging.
Definition: http_message_parser_base.cc:82
connection.h
isc::log::DBGLVL_TRACE_BASIC
const int DBGLVL_TRACE_BASIC
Trace basic operations.
Definition: log_dbglevels.h:65
isc::http::HttpStatusCode::REQUEST_TIMEOUT
@ REQUEST_TIMEOUT
isc::http::HttpConnectionError
Generic error reported within HttpConnection class.
Definition: connection.h:26
isc::log::DBGLVL_TRACE_DETAIL_DATA
const int DBGLVL_TRACE_DETAIL_DATA
Trace data associated with detailed operations.
Definition: log_dbglevels.h:74
isc::http::HttpConnection::doRead
void doRead()
Starts asynchronous read from the socket.
Definition: connection.cc:101
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
isc::http::HttpConnection::asyncAccept
void asyncAccept()
Asynchronously accepts new connection.
Definition: connection.cc:84
isc::data::SimpleParser::getString
static std::string getString(isc::data::ConstElementPtr scope, const std::string &name)
Returns a string parameter from a scope.
Definition: lib/cc/simple_parser.cc:24
asio_wrapper.h
isc::dhcp::IfaceMgr::getPacketQueueMgr6
PacketQueueMgr6Ptr getPacketQueueMgr6()
Fetches the DHCPv6 packet queue manager.
Definition: iface_mgr.h:1039
isc::dhcp::PacketQueue::dequeuePacket
virtual PacketTypePtr dequeuePacket()=0
Dequeues the next packet from the queue.
isc::http::HttpConnection::close
void close()
Closes the socket.
Definition: connection.cc:66
isc::dhcp::Pkt6Ptr
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
LOG_INFO
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20