2012-10-29 07:19:47 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
2016-02-11 21:40:51 +00:00
|
|
|
|
#include <experimental/optional>
|
|
|
|
|
|
2012-10-29 07:19:47 +00:00
|
|
|
|
#include <Poco/Net/HTTPServerResponse.h>
|
2016-02-11 21:40:51 +00:00
|
|
|
|
#include <Poco/DeflatingStream.h>
|
2012-10-29 07:19:47 +00:00
|
|
|
|
|
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>
|
2015-10-05 01:26:43 +00:00
|
|
|
|
#include <DB/Common/NetException.h>
|
2012-10-29 07:19:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
2016-01-11 21:46:36 +00:00
|
|
|
|
namespace ErrorCodes
|
|
|
|
|
{
|
|
|
|
|
extern const int CANNOT_WRITE_TO_OSTREAM;
|
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
|
|
|
|
|
|
|
|
|
/** Отличается от WriteBufferFromOStream тем, что инициализируется не std::ostream, а Poco::Net::HTTPServerResponse.
|
|
|
|
|
* При первом сбросе данных, получает из него std::ostream (с помощью метода send).
|
|
|
|
|
* Это нужно в HTTP серверах, чтобы после передачи в какой-нибудь метод WriteBuffer-а,
|
|
|
|
|
* но до вывода первых данных клиенту, можно было изменить какие-нибудь HTTP заголовки (например, код ответа).
|
|
|
|
|
* (После вызова Poco::Net::HTTPServerResponse::send() изменить заголовки уже нельзя.)
|
|
|
|
|
* То есть, суть в том, чтобы вызывать метод Poco::Net::HTTPServerResponse::send() не сразу.
|
2016-02-11 21:40:51 +00:00
|
|
|
|
*
|
|
|
|
|
* Дополнительно, позволяет сжимать тело HTTP-ответа, выставив соответствующий заголовок Content-Encoding.
|
2012-10-29 07:19:47 +00:00
|
|
|
|
*/
|
|
|
|
|
class WriteBufferFromHTTPServerResponse : public BufferWithOwnMemory<WriteBuffer>
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
Poco::Net::HTTPServerResponse & response;
|
2016-08-23 18:35:23 +00:00
|
|
|
|
bool add_cors_header;
|
2016-02-11 21:40:51 +00:00
|
|
|
|
bool compress;
|
|
|
|
|
Poco::DeflatingStreamBuf::StreamType compression_method;
|
2016-02-12 02:26:04 +00:00
|
|
|
|
int compression_level = Z_DEFAULT_COMPRESSION;
|
2012-10-29 07:19:47 +00:00
|
|
|
|
|
2016-02-11 21:40:51 +00:00
|
|
|
|
std::ostream * response_ostr = nullptr; /// Сюда записывается тело HTTP ответа, возможно, сжатое.
|
|
|
|
|
std::experimental::optional<Poco::DeflatingOutputStream> deflating_stream;
|
|
|
|
|
std::ostream * ostr = nullptr; /// Куда записывать несжатое тело HTTP ответа. Указывает туда же, куда response_ostr или на deflating_stream.
|
|
|
|
|
|
|
|
|
|
void sendHeaders()
|
2012-10-29 07:19:47 +00:00
|
|
|
|
{
|
|
|
|
|
if (!ostr)
|
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-02-16 16:33:12 +00:00
|
|
|
|
if (compress && offset()) /// Пустой ответ сжимать не нужно.
|
2016-02-11 21:40:51 +00:00
|
|
|
|
{
|
|
|
|
|
if (compression_method == Poco::DeflatingStreamBuf::STREAM_GZIP)
|
|
|
|
|
response.set("Content-Encoding", "gzip");
|
|
|
|
|
else if (compression_method == Poco::DeflatingStreamBuf::STREAM_ZLIB)
|
|
|
|
|
response.set("Content-Encoding", "deflate");
|
|
|
|
|
else
|
|
|
|
|
throw Exception("Logical error: unknown compression method passed to WriteBufferFromHTTPServerResponse",
|
|
|
|
|
ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
|
|
|
|
|
response_ostr = &response.send();
|
2016-02-12 02:26:04 +00:00
|
|
|
|
deflating_stream.emplace(*response_ostr, compression_method, compression_level);
|
2016-02-11 23:37:16 +00:00
|
|
|
|
ostr = &deflating_stream.value();
|
2016-02-11 21:40:51 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
response_ostr = &response.send();
|
|
|
|
|
ostr = response_ostr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-19 01:54:23 +00:00
|
|
|
|
void nextImpl() override
|
2016-02-11 21:40:51 +00:00
|
|
|
|
{
|
2012-10-29 07:19:47 +00:00
|
|
|
|
if (!offset())
|
|
|
|
|
return;
|
2016-02-11 23:37:16 +00:00
|
|
|
|
|
2016-02-16 16:33:12 +00:00
|
|
|
|
sendHeaders();
|
|
|
|
|
|
2012-10-29 07:19:47 +00:00
|
|
|
|
ostr->write(working_buffer.begin(), offset());
|
|
|
|
|
ostr->flush();
|
|
|
|
|
|
|
|
|
|
if (!ostr->good())
|
2015-03-26 08:47:02 +00:00
|
|
|
|
throw NetException("Cannot write to ostream", ErrorCodes::CANNOT_WRITE_TO_OSTREAM);
|
2012-10-29 07:19:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2016-02-11 21:40:51 +00:00
|
|
|
|
WriteBufferFromHTTPServerResponse(
|
|
|
|
|
Poco::Net::HTTPServerResponse & response_,
|
|
|
|
|
bool compress_ = false, /// Если true - выставить заголовок Content-Encoding и сжимать результат.
|
|
|
|
|
Poco::DeflatingStreamBuf::StreamType compression_method_ = Poco::DeflatingStreamBuf::STREAM_GZIP, /// Как сжимать результат (gzip, deflate).
|
|
|
|
|
size_t size = DBMS_DEFAULT_BUFFER_SIZE)
|
|
|
|
|
: BufferWithOwnMemory<WriteBuffer>(size), response(response_),
|
|
|
|
|
compress(compress_), compression_method(compression_method_) {}
|
2012-10-29 07:19:47 +00:00
|
|
|
|
|
2013-12-17 19:45:18 +00:00
|
|
|
|
/** Если данные ещё не были отправлены - отправить хотя бы HTTP заголовки.
|
|
|
|
|
* Используйте эту функцию после того, как данные, возможно, были отправлены,
|
|
|
|
|
* и не было ошибок (вы не планируете поменять код ответа).
|
|
|
|
|
*/
|
|
|
|
|
void finalize()
|
|
|
|
|
{
|
2016-02-11 21:40:51 +00:00
|
|
|
|
sendHeaders();
|
2013-12-17 19:45:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-02-19 19:02:20 +00:00
|
|
|
|
/** Включить или отключить сжатие.
|
|
|
|
|
* Работает только перед тем, как были отправлены HTTP заголовки.
|
|
|
|
|
* Иначе - не имеет эффекта.
|
|
|
|
|
*/
|
|
|
|
|
void setCompression(bool enable_compression)
|
|
|
|
|
{
|
|
|
|
|
compress = enable_compression;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-12 02:26:04 +00:00
|
|
|
|
/** Установить уровень сжатия, если данные будут сжиматься.
|
|
|
|
|
* Работает только перед тем, как были отправлены HTTP заголовки.
|
|
|
|
|
* Иначе - не имеет эффекта.
|
|
|
|
|
*/
|
|
|
|
|
void setCompressionLevel(int level)
|
|
|
|
|
{
|
|
|
|
|
compression_level = level;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-23 18:35:23 +00:00
|
|
|
|
/** Включить или отключить CORS.
|
|
|
|
|
* Работает только перед тем, как были отправлены HTTP заголовки.
|
|
|
|
|
* Иначе - не имеет эффекта.
|
|
|
|
|
*/
|
|
|
|
|
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();
|
2016-02-11 21:40:51 +00:00
|
|
|
|
|
|
|
|
|
if (deflating_stream)
|
|
|
|
|
deflating_stream->close();
|
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
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|