Implementation:ClickHouse ClickHouse Poco HTTPSession Impl
base/poco/Net/src/HTTPSession.cpp:1-308
ClickHouse_ClickHouse
ClickHouse_ClickHouse_HTTP_Client_Communication
This page documents the implementation details of the `HTTPSession` class. The corresponding header is already documented under the ClickHouse_ClickHouse_HTTP_Client_Communication principle.
Purpose
Implements the `Poco::Net::HTTPSession` class, which manages the underlying TCP socket for HTTP client and server communication. It provides buffered reading, socket lifecycle management (connect, close, abort, detach, attach), timeout configuration, and data transfer hooks for throttling and monitoring.
Code Reference
Buffered Reading
The session maintains an internal buffer (`HTTP_DEFAULT_BUFFER_SIZE` bytes) for efficient character-at-a-time reading. The `get`, `peek`, and `read` methods consume from this buffer and refill it from the socket as needed:
int HTTPSession::get()
{
if (_pCurrent == _pEnd)
refill();
if (_pCurrent < _pEnd)
return *_pCurrent++;
else
return std::char_traits<char>::eof();
}
void HTTPSession::refill()
{
if (!_pBuffer)
{
_pBuffer = std::make_unique<char[]>(HTTP_DEFAULT_BUFFER_SIZE);
}
_pCurrent = _pEnd = _pBuffer.get();
int n = receive(_pBuffer.get(), HTTP_DEFAULT_BUFFER_SIZE);
_pEnd += n;
}
Socket Connection
The `connect` method establishes the TCP connection and configures socket parameters:
void HTTPSession::connect(const SocketAddress& address)
{
_socket.connect(address, _connectionTimeout);
_socket.setReceiveTimeout(_receiveTimeout);
_socket.setSendTimeout(_sendTimeout);
_socket.setReceiveThrottler(_receiveThrottler);
_socket.setSendThrottler(_sendThrottler);
_socket.setNoDelay(true);
// Clear leftover buffer data from previous (failed) request
_pCurrent = _pEnd = _pBuffer.get();
}
Data Transfer with Hooks
Both `write` and `receive` support data hooks for monitoring and throttling:
int HTTPSession::write(const char* buffer, std::streamsize length)
{
try
{
if (_sendDataHooks)
_sendDataHooks->atStart((int) length);
int result = _socket.sendBytes(buffer, (int) length);
if (_sendDataHooks)
_sendDataHooks->atFinish(result);
return result;
}
catch (Poco::Exception& exc)
{
if (_sendDataHooks)
_sendDataHooks->atFail();
setException(exc);
throw;
}
}
Timeout Configuration
The `setTimeout` method supports separate connection, send, and receive timeouts. It applies socket-level timeouts only if the value has changed and the socket is connected:
void HTTPSession::setTimeout(const Poco::Timespan& connectionTimeout,
const Poco::Timespan& sendTimeout,
const Poco::Timespan& receiveTimeout)
{
_connectionTimeout = connectionTimeout;
if (_sendTimeout.totalMicroseconds() != sendTimeout.totalMicroseconds()) {
_sendTimeout = sendTimeout;
if (connected())
_socket.setSendTimeout(_sendTimeout);
}
// similar for receiveTimeout
}
I/O Contract
| Input | Output | Side Effects |
|---|---|---|
| `SocketAddress` via `connect` | None | Establishes TCP connection; sets timeouts, throttlers, and TCP_NODELAY; clears buffer |
| Buffer via `get` / `peek` / `read` | Next character(s) or EOF | Reads from socket on buffer underflow; triggers receive hooks |
| Buffer via `write` | Bytes sent count | Sends to socket; triggers send hooks; stores exception on failure |
| `Poco::Timespan` via `setTimeout` | None | Updates socket-level timeouts if connected; silently catches exceptions in release builds |
| `abort` | None | Shuts down and closes the socket |
| `detachSocket` | Previous `StreamSocket` | Replaces internal socket with a new default socket |
| `attachSocket` | None | Replaces internal socket and configures throttlers |
| `drainBuffer` | Remaining buffer contents in `Poco::Buffer<char>&` | Resets buffer pointers |
Usage Examples
// Typical usage within HTTPClientSession (subclass of HTTPSession)
Poco::Net::HTTPClientSession session("www.example.com");
session.setTimeout(Poco::Timespan(5, 0)); // 5 second timeout for all operations
session.setKeepAlive(true);
// Low-level read
int ch = session.get(); // reads one character from buffered socket
// Socket detach/attach for connection pooling
Poco::Net::StreamSocket sock = session.detachSocket();
// ... reuse sock later ...
session.attachSocket(sock);
Internal Details
- The buffer is lazily allocated on first `refill` call using `std::make_unique<char[]>`.
- The `connected` method checks `_socket.impl()->initialized()` rather than attempting an I/O operation.
- Exception management uses `setException` / `clearException` with a cloned `Poco::Exception*` stored on the heap. The destructor deletes it.
- The `keepAlive` flag is advisory; it informs HTTP message framing but does not directly control socket-level `SO_KEEPALIVE`.
- In release builds (`NDEBUG` defined), `setTimeout` silently catches `NetException` from socket operations, as some socket implementations may reject timeout changes.
- The `drainBuffer` method copies remaining unread buffer contents into a `Poco::Buffer<char>` and resets the internal buffer pointers.