From 0e447a1f9eccd94c13f5d6f063c66370385cc3f8 Mon Sep 17 00:00:00 2001 From: an-tao Date: Wed, 13 Mar 2019 23:44:53 +0800 Subject: [PATCH] Adapt ipv6 --- examples/simple_example/main.cc | 6 +- examples/simple_example_test/main.cc | 57 ++++++++------- lib/inc/drogon/HttpClient.h | 5 ++ lib/inc/drogon/utils/Utilities.h | 5 +- lib/src/HttpAppFrameworkImpl.cc | 10 ++- lib/src/HttpClientImpl.cc | 22 +++++- lib/src/HttpServer.cc | 11 ++- lib/src/LocalHostFilter.cc | 3 +- lib/src/Utilities.cc | 104 +++++---------------------- lib/tests/GzipTest.cc | 12 ++-- 10 files changed, 93 insertions(+), 142 deletions(-) diff --git a/examples/simple_example/main.cc b/examples/simple_example/main.cc index f6a0826c..204109ab 100755 --- a/examples/simple_example/main.cc +++ b/examples/simple_example/main.cc @@ -98,10 +98,12 @@ int main() { std::cout << banner << std::endl; - app().addListener("0.0.0.0", 8848); + app().addListener("::1", 8848); + //app().addListener("0.0.0.0", 8848); #ifdef USE_OPENSSL - //https + //https drogon::HttpAppFramework::instance().setSSLFiles("server.pem", "server.pem"); + //drogon::HttpAppFramework::instance().addListener("::1", 8849, true); drogon::HttpAppFramework::instance().addListener("0.0.0.0", 8849, true); #endif //app().setThreadNum(4); diff --git a/examples/simple_example_test/main.cc b/examples/simple_example_test/main.cc index e130094c..94e1886a 100644 --- a/examples/simple_example_test/main.cc +++ b/examples/simple_example_test/main.cc @@ -50,10 +50,35 @@ void outputGood(const HttpRequestPtr &req, bool isHttps) } void doTest(const HttpClientPtr &client, std::promise &pro, bool isHttps = false) { + /// Test gzip + auto req = HttpRequest::newHttpRequest(); + req->setMethod(drogon::Get); + req->addHeader("accept-encoding", "gzip"); + req->setPath("/api/v1/apitest/get/111"); + client->sendRequest(req, [=](ReqResult result, const HttpResponsePtr &resp) { + if (result == ReqResult::Ok) + { + if (resp->getBody().length() == 5123) + { + outputGood(req, isHttps); + } + else + { + LOG_DEBUG << resp->getBody().length(); + LOG_ERROR << "Error!"; + exit(1); + } + } + else + { + LOG_ERROR << "Error!"; + exit(1); + } + }); /// Post json Json::Value json; json["request"] = "json"; - auto req = HttpRequest::newHttpJsonRequest(json); + req = HttpRequest::newHttpJsonRequest(json); req->setMethod(drogon::Post); req->setPath("/api/v1/apitest/json"); client->sendRequest(req, [=](ReqResult result, const HttpResponsePtr &resp) { @@ -346,32 +371,6 @@ void doTest(const HttpClientPtr &client, std::promise &pro, bool isHttps = } }); - /// Test gzip - req = HttpRequest::newHttpRequest(); - req->setMethod(drogon::Get); - req->addHeader("accept-encoding", "gzip"); - req->setPath("/api/v1/apitest/get/111"); - client->sendRequest(req, [=](ReqResult result, const HttpResponsePtr &resp) { - if (result == ReqResult::Ok) - { - if (resp->getBody().length() == 5123) - { - outputGood(req, isHttps); - } - else - { - LOG_DEBUG << resp->getBody().length(); - LOG_ERROR << "Error!"; - exit(1); - } - } - else - { - LOG_ERROR << "Error!"; - exit(1); - } - }); - /// Test static function req = HttpRequest::newHttpRequest(); req->setMethod(drogon::Get); @@ -629,11 +628,11 @@ int main(int argc, char *argv[]) do { std::promise pro1; - auto client = HttpClient::newHttpClient("http://127.0.0.1:8848", loop[0].getLoop()); + auto client = HttpClient::newHttpClient("::1", 8848, false, loop[0].getLoop()); doTest(client, pro1); #ifdef USE_OPENSSL std::promise pro2; - auto sslClient = HttpClient::newHttpClient("https://127.0.0.1:8849", loop[1].getLoop()); + auto sslClient = HttpClient::newHttpClient("127.0.0.1", 8849, true, loop[1].getLoop()); doTest(sslClient, pro2, true); auto f2 = pro2.get_future(); f2.get(); diff --git a/lib/inc/drogon/HttpClient.h b/lib/inc/drogon/HttpClient.h index 8ae5ece0..30334ee3 100644 --- a/lib/inc/drogon/HttpClient.h +++ b/lib/inc/drogon/HttpClient.h @@ -68,6 +68,8 @@ class HttpClient : public trantor::NonCopyable * If the loop parameter is set to nullptr, the client * uses the HttpAppFramework's event loop, otherwise it * runs in the loop identified by the parameter. + * + * Note: The @param ip support for both ipv4 and ipv6 address */ static HttpClientPtr newHttpClient(const std::string &ip, uint16_t port, @@ -85,6 +87,9 @@ class HttpClient : public trantor::NonCopyable * https://127.0.0.1:8080/ * http://127.0.0.1 * + * The @param hostString must be prefixed by 'http://' or 'https://' + * and doesn't support for ipv6 address if the host is in ip format + * * If the loop parameter is set to nullptr, the client * uses the HttpAppFramework's event loop, otherwise it * runs in the loop identified by the parameter. diff --git a/lib/inc/drogon/utils/Utilities.h b/lib/inc/drogon/utils/Utilities.h index 4009895e..a4591a26 100755 --- a/lib/inc/drogon/utils/Utilities.h +++ b/lib/inc/drogon/utils/Utilities.h @@ -65,10 +65,7 @@ std::string urlEncode(const std::string &str); * @param zdata: Data after compressing or before decompressing * @param nzdata: Data length after compressing or before decompressing */ -int gzipCompress(const char *data, const size_t ndata, - char *zdata, size_t *nzdata); -int gzipDecompress(const char *zdata, const size_t nzdata, - char *data, size_t *ndata); +std::shared_ptr gzipCompress(const char *data, const size_t ndata); std::shared_ptr gzipDecompress(const std::shared_ptr &compressedData); /// Get the http full date string diff --git a/lib/src/HttpAppFrameworkImpl.cc b/lib/src/HttpAppFrameworkImpl.cc index 2efb91c7..27236f13 100755 --- a/lib/src/HttpAppFrameworkImpl.cc +++ b/lib/src/HttpAppFrameworkImpl.cc @@ -293,8 +293,11 @@ void HttpAppFrameworkImpl::run() ioLoops.push_back(loopThreadPtr->getLoop()); for (auto const &listener : _listeners) { + auto ip = std::get<0>(listener); + bool isIpv6 = ip.find(":") == std::string::npos ? false : true; auto serverPtr = std::make_shared(loopThreadPtr->getLoop(), - InetAddress(std::get<0>(listener), std::get<1>(listener)), "drogon"); + InetAddress(ip, std::get<1>(listener), isIpv6), + "drogon"); if (std::get<2>(listener)) { //enable ssl; @@ -327,8 +330,11 @@ void HttpAppFrameworkImpl::run() auto loopThreadPtr = std::make_shared("DrogonListeningLoop"); loopThreadPtr->run(); loopThreads.push_back(loopThreadPtr); + auto ip = std::get<0>(listener); + bool isIpv6 = ip.find(":") == std::string::npos ? false : true; auto serverPtr = std::make_shared(loopThreadPtr->getLoop(), - InetAddress(std::get<0>(listener), std::get<1>(listener)), "drogon"); + InetAddress(ip, std::get<1>(listener), isIpv6), + "drogon"); if (std::get<2>(listener)) { //enable ssl; diff --git a/lib/src/HttpClientImpl.cc b/lib/src/HttpClientImpl.cc index fd40a8f6..305044a1 100644 --- a/lib/src/HttpClientImpl.cc +++ b/lib/src/HttpClientImpl.cc @@ -122,7 +122,21 @@ void HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req, if (!_tcpClient) { - if (_server.ipNetEndian() == 0 && + bool hasIpv6Address = false; + if (_server.isIpV6()) + { + auto ipaddr = _server.ip6NetEndian(); + for (int i = 0; i < 4; i++) + { + if (ipaddr[i] != 0) + { + hasIpv6Address = true; + break; + } + } + } + + if (_server.ipNetEndian() == 0 && !hasIpv6Address && !_domain.empty() && _server.portNetEndian() != 0) { @@ -136,7 +150,8 @@ void HttpClientImpl::sendRequestInLoop(const drogon::HttpRequestPtr &req, } LOG_TRACE << "dns:domain=" << _domain << ";ip=" << _server.toIp(); } - if (_server.ipNetEndian() != 0 && _server.portNetEndian() != 0) + + if ((_server.ipNetEndian() != 0 || hasIpv6Address) && _server.portNetEndian() != 0) { LOG_TRACE << "New TcpClient," << _server.toIpPort(); _tcpClient = std::make_shared(_loop, _server, "httpClient"); @@ -288,7 +303,8 @@ void HttpClientImpl::onRecvMessage(const trantor::TcpConnectionPtr &connPtr, tra HttpClientPtr HttpClient::newHttpClient(const std::string &ip, uint16_t port, bool useSSL, trantor::EventLoop *loop) { - return std::make_shared(loop == nullptr ? drogon::app().loop() : loop, trantor::InetAddress(ip, port), useSSL); + bool isIpv6 = ip.find(":") == std::string::npos ? false : true; + return std::make_shared(loop == nullptr ? drogon::app().loop() : loop, trantor::InetAddress(ip, port, isIpv6), useSSL); } HttpClientPtr HttpClient::newHttpClient(const std::string &hostString, trantor::EventLoop *loop) diff --git a/lib/src/HttpServer.cc b/lib/src/HttpServer.cc index 1e2eea99..e8b297cc 100755 --- a/lib/src/HttpServer.cc +++ b/lib/src/HttpServer.cc @@ -308,21 +308,21 @@ void HttpServer::onRequest(const TcpConnectionPtr &conn, const HttpRequestImplPt { //use gzip LOG_TRACE << "Use gzip to compress the body"; - char *zbuf = new char[response->getBody().length()]; size_t zlen = response->getBody().length(); - if (gzipCompress(response->getBody().data(), - response->getBody().length(), - zbuf, &zlen) >= 0) + auto strCompress = gzipCompress(response->getBody().data(), + response->getBody().length()); + if (strCompress) { if (zlen > 0) { + LOG_TRACE << "length after compressing:" << zlen; if (response->expiredTime() >= 0) { //cached response,we need to make a clone newResp = std::make_shared(*std::dynamic_pointer_cast(response)); newResp->setExpiredTime(-1); } - newResp->setBody(std::string(zbuf, zlen)); + newResp->setBody(std::move(*strCompress)); newResp->addHeader("Content-Encoding", "gzip"); } else @@ -330,7 +330,6 @@ void HttpServer::onRequest(const TcpConnectionPtr &conn, const HttpRequestImplPt LOG_ERROR << "gzip got 0 length result"; } } - delete[] zbuf; } if (conn->getLoop()->isInLoopThread()) { diff --git a/lib/src/LocalHostFilter.cc b/lib/src/LocalHostFilter.cc index 7ec9ca3a..563434e6 100644 --- a/lib/src/LocalHostFilter.cc +++ b/lib/src/LocalHostFilter.cc @@ -19,7 +19,8 @@ void LocalHostFilter::doFilter(const HttpRequestPtr &req, const FilterCallback &fcb, const FilterChainCallback &fccb) { - if (req->peerAddr().toIp() == "127.0.0.1") + + if (req->peerAddr().isLoopbackIp()) { fccb(); return; diff --git a/lib/src/Utilities.cc b/lib/src/Utilities.cc index 24b9ea20..6fbde323 100755 --- a/lib/src/Utilities.cc +++ b/lib/src/Utilities.cc @@ -516,98 +516,28 @@ std::string urlDecode(const char *begin, const char *end) } /* Compress gzip data */ -/* data 原数据 ndata 原数据长度 zdata 压缩后数据 nzdata 压缩后长度 */ - -int gzipCompress(const char *data, const size_t ndata, - char *zdata, size_t *nzdata) +std::shared_ptr gzipCompress(const char *data, const size_t ndata) { - - z_stream c_stream; - + z_stream strm = {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, + if (deflateInit2(&strm, 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) - { - LOG_ERROR << "compress err:" << c_stream.msg; - return -1; - } - } - if (c_stream.avail_in != 0) - return c_stream.avail_in; - for (;;) - { - int err = 0; - 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 nullptr; + auto outstr = std::make_shared(); + outstr->resize(compressBound(ndata)); + strm.next_in = (Bytef *)data; + strm.avail_in = ndata; + strm.next_out = (Bytef *)outstr->data(); + strm.avail_out = outstr->length(); + if (deflate(&strm, Z_FINISH) != Z_STREAM_END) + return nullptr; + if (deflateEnd(&strm) != Z_OK) + return nullptr; + outstr->resize(strm.total_out); + return outstr; } - return -1; -} - -/* Uncompress gzip data */ -/* zdata 数据 nzdata 原数据长度 data 解压后数据 ndata 解压后长度 */ -int gzipDecompress(const char *zdata, const size_t nzdata, - char *data, size_t *ndata) -{ - 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) - { - int err = 0; - 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; + return nullptr; } std::shared_ptr gzipDecompress(const std::shared_ptr &compressedData) diff --git a/lib/tests/GzipTest.cc b/lib/tests/GzipTest.cc index 7b50dcce..ad8ffe4c 100644 --- a/lib/tests/GzipTest.cc +++ b/lib/tests/GzipTest.cc @@ -304,16 +304,12 @@ int main() "mobile\n" "origin\n" "rawpacket"; - std::string out; - out.resize(inStr.length()); - size_t len = out.length(); - auto ret=gzipCompress(inStr.c_str(), inStr.length(), (char *)(out.data()), &len); - if(ret==0) + auto ret=gzipCompress(inStr.c_str(), inStr.length()); + if(ret) { - out.resize(len); - std::cout << "origin length=" << inStr.length() << " compressing length=" << out.length() << std::endl; + std::cout << "origin length=" << inStr.length() << " compressing length=" << ret->length() << std::endl; - auto decompressStr = gzipDecompress(std::make_shared(out)); + auto decompressStr = gzipDecompress(ret); if (decompressStr) { std::cout << "decompressing length=" << decompressStr->length() << std::endl;