MCPcopy Create free account
hub / github.com/ClickHouse/ClickHouse / trySendExceptionWithoutConnectionBuffers

Method trySendExceptionWithoutConnectionBuffers

src/Server/TCPHandler.cpp:3014–3075  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

3012
3013
3014void TCPHandler::trySendExceptionWithoutConnectionBuffers(const Exception & e)
3015{
3016 try
3017 {
3018 /// The connection failed to initialize most likely because the server memory limit
3019 /// is reached. In this state every tracked allocation throws, so the error handling
3020 /// path must be exempt from the memory limit: it allocates a small bounded amount
3021 /// (the strings of the exception message).
3022 LockMemoryExceptionInThread lock_memory_tracker(VariableContext::Global);
3023
3024 /// The client has not received 'Hello' yet, so the communication is not chunked,
3025 /// and the client is able to receive an exception packet instead of the 'Hello'
3026 /// response (this is also how authentication errors are delivered). The stack
3027 /// trace is not sent: it is of little use, while it noticeably grows the packet.
3028 char stack_memory[4096];
3029 WriteBufferFromPocoSocket socket_out(socket(), sizeof(stack_memory), stack_memory);
3030 writeVarUInt(Protocol::Server::Exception, socket_out);
3031 writeException(e, socket_out, false /*with_stack_trace*/);
3032 socket_out.finalize();
3033
3034 /// The client's 'Hello' packet is still unread in the socket receive queue.
3035 /// Closing the socket with pending unread data makes the kernel send RST,
3036 /// which can discard the exception packet written above before the client
3037 /// reads it. Read the pending data out, so the connection is terminated
3038 /// gracefully with FIN. There is no need to wait for more data: the client
3039 /// sends 'Hello' in a single packet before reading the response.
3040 /// The amount of drained data is limited in case the client keeps sending:
3041 /// such a connection is closed with RST, and that is fine.
3042 ssize_t max_bytes_to_drain = 65536;
3043 if (socket().secure())
3044 {
3045 /// For secure sockets available is `SSL_pending`, which only reports decrypted
3046 /// data buffered inside OpenSSL and misses the data in the socket receive queue,
3047 /// so the readiness of the underlying fd is polled instead. receiveBytes can
3048 /// block on a partially received TLS record, so the receive timeout is limited.
3049 socket().setReceiveTimeout(Poco::Timespan(0, 100'000 /*microseconds*/));
3050 while (max_bytes_to_drain > 0 && socket().poll(Poco::Timespan(), Poco::Net::Socket::SELECT_READ))
3051 {
3052 int bytes_received = socket().receiveBytes(stack_memory, sizeof(stack_memory));
3053 if (bytes_received <= 0)
3054 break;
3055 max_bytes_to_drain -= bytes_received;
3056 }
3057 }
3058 else
3059 {
3060 /// For plain sockets available reflects the socket receive queue,
3061 /// and receiveBytes never blocks.
3062 while (max_bytes_to_drain > 0 && socket().available() > 0)
3063 {
3064 int bytes_received = socket().receiveBytes(stack_memory, sizeof(stack_memory));
3065 if (bytes_received <= 0)
3066 break;
3067 max_bytes_to_drain -= bytes_received;
3068 }
3069 }
3070 }
3071 catch (...)

Callers

nothing calls this directly

Calls 10

writeVarUIntFunction · 0.85
writeExceptionFunction · 0.50
TimespanClass · 0.50
tryLogCurrentExceptionFunction · 0.50
finalizeMethod · 0.45
secureMethod · 0.45
setReceiveTimeoutMethod · 0.45
pollMethod · 0.45
receiveBytesMethod · 0.45
availableMethod · 0.45

Tested by

no test coverage detected