2012-10-29 07:19:47 +00:00
|
|
|
#pragma once
|
|
|
|
|
2016-02-11 21:40:51 +00:00
|
|
|
#include <experimental/optional>
|
2017-01-22 15:03:55 +00:00
|
|
|
#include <mutex>
|
2016-02-11 21:40:51 +00:00
|
|
|
|
2012-10-29 07:19:47 +00:00
|
|
|
#include <Poco/Net/HTTPServerResponse.h>
|
|
|
|
|
2015-10-05 01:35:28 +00:00
|
|
|
#include <DB/Common/Exception.h>
|
2012-10-29 07:19:47 +00:00
|
|
|
|
|
|
|
#include <DB/IO/WriteBuffer.h>
|
|
|
|
#include <DB/IO/BufferWithOwnMemory.h>
|
2017-01-07 16:11:30 +00:00
|
|
|
#include <DB/IO/WriteBufferFromOStream.h>
|
2017-01-22 15:03:55 +00:00
|
|
|
#include <DB/IO/WriteBufferFromString.h>
|
2017-01-07 16:11:30 +00:00
|
|
|
#include <DB/IO/ZlibDeflatingWriteBuffer.h>
|
2016-12-30 20:52:56 +00:00
|
|
|
#include <DB/IO/HTTPCommon.h>
|
2015-10-05 01:26:43 +00:00
|
|
|
#include <DB/Common/NetException.h>
|
2017-01-22 15:03:55 +00:00
|
|
|
#include <DB/Core/Progress.h>
|
2012-10-29 07:19:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
2016-01-11 21:46:36 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
2016-02-11 21:40:51 +00:00
|
|
|
extern const int LOGICAL_ERROR;
|
2016-01-11 21:46:36 +00:00
|
|
|
}
|
|
|
|
|
2012-10-29 07:19:47 +00:00
|
|
|
|
2017-01-07 16:11:30 +00:00
|
|
|
/// The difference from WriteBufferFromOStream is that this buffer gets the underlying std::ostream
|
|
|
|
/// (using response.send()) only after data is flushed for the first time. This is needed in HTTP
|
|
|
|
/// servers to change some HTTP headers (e.g. response code) before any data is sent to the client
|
|
|
|
/// (headers can't be changed after response.send() is called).
|
|
|
|
///
|
|
|
|
/// In short, it allows delaying the call to response.send().
|
|
|
|
///
|
|
|
|
/// Additionally, supports HTTP response compression (in this case corresponding Content-Encoding
|
|
|
|
/// header will be set).
|
2012-10-29 07:19:47 +00:00
|
|
|
class WriteBufferFromHTTPServerResponse : public BufferWithOwnMemory<WriteBuffer>
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
Poco::Net::HTTPServerResponse & response;
|
2017-01-07 16:11:30 +00:00
|
|
|
|
2016-08-23 18:35:23 +00:00
|
|
|
bool add_cors_header;
|
2016-02-11 21:40:51 +00:00
|
|
|
bool compress;
|
2017-01-07 16:11:30 +00:00
|
|
|
ZlibCompressionMethod compression_method;
|
2016-02-12 02:26:04 +00:00
|
|
|
int compression_level = Z_DEFAULT_COMPRESSION;
|
2012-10-29 07:19:47 +00:00
|
|
|
|
2017-01-22 15:42:42 +00:00
|
|
|
std::ostream * response_body_ostr = nullptr;
|
|
|
|
std::ostream * response_header_ostr = nullptr;
|
|
|
|
|
2017-01-22 15:03:55 +00:00
|
|
|
std::experimental::optional<WriteBufferFromOStream> out_raw;
|
2017-01-07 16:11:30 +00:00
|
|
|
std::experimental::optional<ZlibDeflatingWriteBuffer> deflating_buf;
|
|
|
|
|
|
|
|
WriteBuffer * out = nullptr; /// Uncompressed HTTP body is written to this buffer. Points to out_raw or possibly to deflating_buf.
|
2016-02-11 21:40:51 +00:00
|
|
|
|
2017-01-22 15:03:55 +00:00
|
|
|
bool body_started_sending = false; /// If true, you could not add any headers.
|
|
|
|
|
|
|
|
std::mutex mutex; /// progress callback could be called from different threads.
|
|
|
|
|
|
|
|
|
|
|
|
/// Must be called under locked mutex.
|
2017-01-22 15:42:42 +00:00
|
|
|
/// This method send headers, if this was not done already,
|
|
|
|
/// but not finish them with \r\n, allowing to send more headers subsequently.
|
2017-01-22 15:20:37 +00:00
|
|
|
void startSendHeaders()
|
2012-10-29 07:19:47 +00:00
|
|
|
{
|
2017-01-07 16:11:30 +00:00
|
|
|
if (!out)
|
2016-02-11 21:40:51 +00:00
|
|
|
{
|
2016-08-23 18:35:23 +00:00
|
|
|
if (add_cors_header)
|
|
|
|
{
|
|
|
|
response.set("Access-Control-Allow-Origin","*");
|
|
|
|
}
|
|
|
|
|
2016-12-30 20:52:56 +00:00
|
|
|
setResponseDefaultHeaders(response);
|
|
|
|
|
2017-01-07 16:11:30 +00:00
|
|
|
if (compress && offset()) /// Empty response need not be compressed.
|
2016-02-11 21:40:51 +00:00
|
|
|
{
|
2017-01-07 16:11:30 +00:00
|
|
|
if (compression_method == ZlibCompressionMethod::Gzip)
|
2016-02-11 21:40:51 +00:00
|
|
|
response.set("Content-Encoding", "gzip");
|
2017-01-07 16:11:30 +00:00
|
|
|
else if (compression_method == ZlibCompressionMethod::Zlib)
|
2016-02-11 21:40:51 +00:00
|
|
|
response.set("Content-Encoding", "deflate");
|
|
|
|
else
|
|
|
|
throw Exception("Logical error: unknown compression method passed to WriteBufferFromHTTPServerResponse",
|
|
|
|
ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
2017-01-22 15:42:42 +00:00
|
|
|
response.beginSend(response_header_ostr, response_body_ostr);
|
|
|
|
out_raw.emplace(*response_body_ostr);
|
2017-01-07 16:11:30 +00:00
|
|
|
/// Use memory allocated for the outer buffer in the buffer pointed to by out. This avoids extra allocation and copy.
|
2017-01-22 15:11:34 +00:00
|
|
|
deflating_buf.emplace(out_raw.value(), compression_method, compression_level, working_buffer.size(), working_buffer.begin());
|
2017-01-07 16:11:30 +00:00
|
|
|
out = &deflating_buf.value();
|
2016-02-11 21:40:51 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-01-22 15:42:42 +00:00
|
|
|
response.beginSend(response_header_ostr, response_body_ostr);
|
|
|
|
out_raw.emplace(*response_body_ostr, working_buffer.size(), working_buffer.begin());
|
2017-01-22 15:12:18 +00:00
|
|
|
out = &out_raw.value();
|
2016-02-11 21:40:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-22 15:42:42 +00:00
|
|
|
/// This method send headers, if this was not done already, and finish them with \r\n, allowing to start to send body.
|
2017-01-22 15:20:37 +00:00
|
|
|
void sendAllHeaders()
|
2016-02-11 21:40:51 +00:00
|
|
|
{
|
2017-01-22 15:20:37 +00:00
|
|
|
startSendHeaders();
|
2016-02-16 16:33:12 +00:00
|
|
|
|
2017-01-22 15:03:55 +00:00
|
|
|
if (!body_started_sending)
|
|
|
|
{
|
|
|
|
body_started_sending = true;
|
|
|
|
/// Send end of headers delimiter.
|
2017-01-22 15:42:42 +00:00
|
|
|
*response_header_ostr << "\r\n" << std::flush;
|
2017-01-22 15:03:55 +00:00
|
|
|
}
|
2017-01-22 15:20:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nextImpl() override
|
|
|
|
{
|
|
|
|
if (!offset())
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
|
|
|
|
sendAllHeaders();
|
2017-01-22 15:03:55 +00:00
|
|
|
|
2017-01-07 16:11:30 +00:00
|
|
|
out->position() = position();
|
|
|
|
out->next();
|
2012-10-29 07:19:47 +00:00
|
|
|
}
|
|
|
|
|
2017-01-22 15:13:50 +00:00
|
|
|
public:
|
|
|
|
WriteBufferFromHTTPServerResponse(
|
|
|
|
Poco::Net::HTTPServerResponse & response_,
|
|
|
|
bool compress_ = false, /// If true - set Content-Encoding header and compress the result.
|
|
|
|
ZlibCompressionMethod compression_method_ = ZlibCompressionMethod::Gzip,
|
|
|
|
size_t size = DBMS_DEFAULT_BUFFER_SIZE)
|
|
|
|
: BufferWithOwnMemory<WriteBuffer>(size), response(response_),
|
|
|
|
compress(compress_), compression_method(compression_method_) {}
|
|
|
|
|
2017-01-22 15:03:55 +00:00
|
|
|
/// Writes progess in repeating HTTP headers.
|
|
|
|
void onProgress(const Progress & progress)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
|
|
|
|
/// Cannot add new headers if body was started to send.
|
|
|
|
if (body_started_sending)
|
|
|
|
return;
|
|
|
|
|
2017-01-22 15:42:42 +00:00
|
|
|
/// Send all common headers before our special progress headers.
|
2017-01-22 15:20:37 +00:00
|
|
|
startSendHeaders();
|
2017-01-22 15:10:09 +00:00
|
|
|
|
2017-01-22 15:03:55 +00:00
|
|
|
std::string progress_string;
|
|
|
|
{
|
2017-01-22 15:13:04 +00:00
|
|
|
WriteBufferFromString progress_string_writer(progress_string);
|
2017-01-22 15:03:55 +00:00
|
|
|
progress.writeJSON(progress_string_writer);
|
|
|
|
}
|
|
|
|
|
2017-01-22 15:42:42 +00:00
|
|
|
*response_header_ostr << "X-ClickHouse-Progress: " << progress_string << "\r\n" << std::flush;
|
2017-01-22 15:03:55 +00:00
|
|
|
}
|
|
|
|
|
2017-01-07 16:11:30 +00:00
|
|
|
/// Send at least HTTP headers if no data has been sent yet.
|
|
|
|
/// Use after the data has possibly been sent and no error happened (and thus you do not plan
|
|
|
|
/// to change response HTTP code.
|
2013-12-17 19:45:18 +00:00
|
|
|
void finalize()
|
|
|
|
{
|
2017-01-22 15:03:55 +00:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
2017-01-22 15:20:37 +00:00
|
|
|
sendAllHeaders();
|
2013-12-17 19:45:18 +00:00
|
|
|
}
|
|
|
|
|
2017-01-07 16:11:30 +00:00
|
|
|
/// Turn compression on or off.
|
|
|
|
/// The setting has any effect only if HTTP headers haven't been sent yet.
|
2016-02-19 19:02:20 +00:00
|
|
|
void setCompression(bool enable_compression)
|
|
|
|
{
|
|
|
|
compress = enable_compression;
|
|
|
|
}
|
|
|
|
|
2017-01-07 16:11:30 +00:00
|
|
|
/// Set compression level if the compression is turned on.
|
|
|
|
/// The setting has any effect only if HTTP headers haven't been sent yet.
|
2016-02-12 02:26:04 +00:00
|
|
|
void setCompressionLevel(int level)
|
|
|
|
{
|
|
|
|
compression_level = level;
|
|
|
|
}
|
|
|
|
|
2017-01-07 16:11:30 +00:00
|
|
|
/// Turn CORS on or off.
|
|
|
|
/// The setting has any effect only if HTTP headers haven't been sent yet.
|
2016-08-23 18:35:23 +00:00
|
|
|
void addHeaderCORS(bool enable_cors)
|
|
|
|
{
|
|
|
|
add_cors_header = enable_cors;
|
|
|
|
}
|
|
|
|
|
2012-10-29 07:19:47 +00:00
|
|
|
~WriteBufferFromHTTPServerResponse()
|
|
|
|
{
|
2013-11-28 20:05:54 +00:00
|
|
|
if (!offset())
|
|
|
|
return;
|
|
|
|
|
2013-11-18 17:17:45 +00:00
|
|
|
try
|
2012-11-19 04:16:00 +00:00
|
|
|
{
|
2012-10-29 07:19:47 +00:00
|
|
|
next();
|
2012-11-19 04:16:00 +00:00
|
|
|
}
|
2013-11-18 17:17:45 +00:00
|
|
|
catch (...)
|
|
|
|
{
|
2013-11-18 19:18:03 +00:00
|
|
|
tryLogCurrentException(__PRETTY_FUNCTION__);
|
2013-11-18 17:17:45 +00:00
|
|
|
}
|
2012-10-29 07:19:47 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|