From 790867d1e3a390760f9c85d1a51076fe571a6c64 Mon Sep 17 00:00:00 2001 From: antao Date: Mon, 3 Sep 2018 10:56:11 +0800 Subject: [PATCH] Add gzip compressing --- CMakeLists.txt | 3 +- examples/CMakeLists.txt | 2 +- lib/inc/drogon/HttpResponse.h | 5 +- lib/inc/drogon/utils/Utilities.h | 6 +- lib/src/HttpResponseImpl.h | 12 +++- lib/src/HttpServer.cc | 21 +++++++ lib/src/Utilities.cc | 104 +++++++++++++++++++++++-------- 7 files changed, 120 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05f2f5fe..8b964977 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,8 @@ if(OpenSSL_FOUND) include_directories(${OPENSSL_INCLUDE_DIR}) endif() message(STATUS "openssl inc path:" ${OPENSSL_INCLUDE_DIR}) - +find_package(ZLIB REQUIRED) +include_directories(${ZLIB_INCLUDE_DIR}) find_package(Boost) if(Boost_FOUND) # add_definitions(-DUSE_BOOST) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 668ffd6a..c2ff3fed 100755 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,4 @@ -link_libraries(drogon trantor uuid pthread jsoncpp dl) +link_libraries(drogon trantor uuid pthread jsoncpp dl z) if(OpenSSL_FOUND) link_libraries(ssl crypto) endif() diff --git a/lib/inc/drogon/HttpResponse.h b/lib/inc/drogon/HttpResponse.h index 6f4501c0..6b643827 100755 --- a/lib/inc/drogon/HttpResponse.h +++ b/lib/inc/drogon/HttpResponse.h @@ -94,6 +94,8 @@ namespace drogon virtual void setContentTypeCode(uint8_t type)=0; + virtual uint8_t getContentTypeCode()=0; + virtual std::string getHeader(const std::string& key) const =0; virtual void addHeader(const std::string& key, const std::string& value)=0; @@ -112,7 +114,8 @@ namespace drogon virtual void clear()=0; - virtual std::string getBody() const=0; + virtual const std::string & getBody() const=0; + virtual std::string & getBody() = 0; virtual const std::shared_ptr getJsonObject() const =0; static HttpResponsePtr newHttpResponse(); diff --git a/lib/inc/drogon/utils/Utilities.h b/lib/inc/drogon/utils/Utilities.h index ae6222d7..74a2ceb3 100755 --- a/lib/inc/drogon/utils/Utilities.h +++ b/lib/inc/drogon/utils/Utilities.h @@ -26,7 +26,11 @@ namespace drogon{ std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len); std::string base64_decode(std::string const& encoded_string); std::string urlDecode(const std::string& szToDecode); - // std::string base64Encode(char *in_str, int in_len) + int gzcompress(const char *data, const size_t ndata, + char *zdata, size_t *nzdata); + int gzdecompress(const char *zdata, const size_t nzdata, + char *data, size_t *ndata); + } diff --git a/lib/src/HttpResponseImpl.h b/lib/src/HttpResponseImpl.h index 501c54eb..313033b9 100755 --- a/lib/src/HttpResponseImpl.h +++ b/lib/src/HttpResponseImpl.h @@ -110,7 +110,10 @@ namespace drogon _contentType=type; setContentType(web_content_type_to_string(type)); } - + virtual uint8_t getContentTypeCode() override + { + return _contentType; + } // virtual uint8_t contentTypeCode() override // { // return _contentType; @@ -222,11 +225,14 @@ namespace drogon // receiveTime_ = t; // } - virtual std::string getBody() const override + virtual const std::string & getBody() const override + { + return _body; + } + virtual std::string & getBody() override { return _body; } - void swap(HttpResponseImpl &that) { _headers.swap(that._headers); diff --git a/lib/src/HttpServer.cc b/lib/src/HttpServer.cc index bb75af4c..0f4fe8ac 100755 --- a/lib/src/HttpServer.cc +++ b/lib/src/HttpServer.cc @@ -28,6 +28,7 @@ #include "HttpContext.h" #include #include +#include #include using namespace std::placeholders; @@ -162,11 +163,31 @@ void HttpServer::onRequest(const TcpConnectionPtr& conn, const HttpRequestPtr& r if(!response) return; response->setCloseConnection(_close); + + if(response->getContentTypeCode()getBody().length()>4096&& + req->getHeader("Accept-Encoding").find("gzip")!=std::string::npos) + { + //use gzip + LOG_TRACE<<"Use gzip to compress the body"; + char zbuf[response->getBody().length()]; + size_t zlen; + if(gzcompress(response->getBody().data(), + response->getBody().length(), + zbuf,&zlen)>=0) + { + response->setBody(std::string(zbuf,zlen)); + response->addHeader("Content-Encoding","gzip"); + } + } + std::dynamic_pointer_cast(response)->appendToBuffer(&buf); conn->send(std::move(buf)); if (_close) { conn->shutdown(); } + + }); diff --git a/lib/src/Utilities.cc b/lib/src/Utilities.cc index 772b98ec..fd9aa390 100755 --- a/lib/src/Utilities.cc +++ b/lib/src/Utilities.cc @@ -1,12 +1,7 @@ #include #include #include -//#ifdef USE_OPENSSL -//#include -////#include -////#include -////#include -//#endif +#include namespace drogon{ static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -227,24 +222,81 @@ namespace drogon{ return result; } -// std::string base64Encode(char *in_str, int in_len) -// { -// BIO *b64, *bio; -// BUF_MEM *bptr = NULL; -// size_t size = 0; -// -// assert(in_str); -// -// b64 = BIO_new(BIO_f_base64()); -// bio = BIO_new(BIO_s_mem()); -// bio = BIO_push(b64, bio); -// -// BIO_write(bio, in_str, in_len); -// BIO_flush(bio); -// -// BIO_get_mem_ptr(bio, &bptr); -// std::string ret(bptr->data,bptr->length); -// BIO_free_all(bio); -// return ret; -// } + +/* Compress gzip data */ +/* data 原数据 ndata 原数据长度 zdata 压缩后数据 nzdata 压缩后长度 */ + +int gzcompress(const char *data, const size_t ndata, + char *zdata, size_t *nzdata) +{ + + z_stream c_stream; + int err = 0; + + if(data && ndata > 0) { + c_stream.zalloc = NULL; + c_stream.zfree = NULL; + c_stream.opaque = NULL; + //只有设置为MAX_WBITS + 16才能在在压缩文本中带header和trailer + if(deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) return-1; + c_stream.next_in = (Bytef *)data; + c_stream.avail_in = ndata; + c_stream.next_out = (Bytef *)zdata; + c_stream.avail_out = *nzdata; + while(c_stream.avail_in != 0 && c_stream.total_out < *nzdata) { + if(deflate(&c_stream, Z_NO_FLUSH) != Z_OK) return-1; + } + if(c_stream.avail_in != 0) return c_stream.avail_in; + for(;;) { + if((err = deflate(&c_stream, Z_FINISH)) == Z_STREAM_END) break; + if(err != Z_OK) return -1; + } + if(deflateEnd(&c_stream) != Z_OK) return -1; + *nzdata = c_stream.total_out; + return 0; + } + return -1; +} + + +/* Uncompress gzip data */ +/* zdata 数据 nzdata 原数据长度 data 解压后数据 ndata 解压后长度 */ +int gzdecompress(const char *zdata, const size_t nzdata, + char *data, size_t *ndata) +{ + int err = 0; + z_stream d_stream = {0}; /* decompression stream */ + static char dummy_head[2] = { + 0x8 + 0x7 * 0x10, + (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, + }; + d_stream.zalloc = NULL; + d_stream.zfree = NULL; + d_stream.opaque = NULL; + d_stream.next_in = (Bytef *)zdata; + d_stream.avail_in = 0; + d_stream.next_out = (Bytef *)data; + //只有设置为MAX_WBITS + 16才能在解压带header和trailer的文本 + if(inflateInit2(&d_stream, MAX_WBITS + 16) != Z_OK) return-1; + //if(inflateInit2(&d_stream, 47) != Z_OK) return -1; + while(d_stream.total_out < *ndata && d_stream.total_in < nzdata) { + d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */ + if((err = inflate(&d_stream, Z_NO_FLUSH)) == Z_STREAM_END) break; + if(err != Z_OK) { + if(err == Z_DATA_ERROR) { + d_stream.next_in = (Bytef*) dummy_head; + d_stream.avail_in = sizeof(dummy_head); + if((err = inflate(&d_stream, Z_NO_FLUSH)) != Z_OK) { + return -1; + } + }else + return -1; + } + } + if(inflateEnd(&d_stream) != Z_OK) return -1; + *ndata = d_stream.total_out; + return 0; +} + }