diff options
author | Daniil Cherednik <dan.cherednik@gmail.com> | 2022-11-24 13:14:34 +0300 |
---|---|---|
committer | Daniil Cherednik <dan.cherednik@gmail.com> | 2022-11-24 14:46:00 +0300 |
commit | 87f7fceed34bcafb8aaff351dd493a35c916986f (patch) | |
tree | 26809ec8f550aba8eb019e59adc3d48e51913eb2 /library/cpp/http/simple/ut | |
parent | 11bc4015b8010ae201bf3eb33db7dba425aca35e (diff) | |
download | ydb-38c0b87ea9b8ab54a793f4246ecdee802a8227dc.tar.gz |
Ydb stable 22-4-4322.4.43
x-stable-origin-commit: 8d49d46cc834835bf3e50870516acd7376a63bcf
Diffstat (limited to 'library/cpp/http/simple/ut')
-rw-r--r-- | library/cpp/http/simple/ut/http_ut.cpp | 439 | ||||
-rw-r--r-- | library/cpp/http/simple/ut/https_server/http_server.crt | 19 | ||||
-rw-r--r-- | library/cpp/http/simple/ut/https_server/http_server.key | 28 | ||||
-rw-r--r-- | library/cpp/http/simple/ut/https_server/main.go | 70 | ||||
-rw-r--r-- | library/cpp/http/simple/ut/https_ut.cpp | 97 |
5 files changed, 653 insertions, 0 deletions
diff --git a/library/cpp/http/simple/ut/http_ut.cpp b/library/cpp/http/simple/ut/http_ut.cpp new file mode 100644 index 0000000000..bf7e767428 --- /dev/null +++ b/library/cpp/http/simple/ut/http_ut.cpp @@ -0,0 +1,439 @@ +#include <library/cpp/http/simple/http_client.h> + +#include <library/cpp/http/server/response.h> + +#include <library/cpp/testing/mock_server/server.h> +#include <library/cpp/testing/unittest/registar.h> +#include <library/cpp/testing/unittest/tests_data.h> + +#include <util/system/event.h> +#include <util/system/thread.h> + +#include <thread> + +Y_UNIT_TEST_SUITE(SimpleHttp) { + static THttpServerOptions createOptions(ui16 port, bool keepAlive) { + THttpServerOptions o; + o.AddBindAddress("localhost", port); + o.SetThreads(1); + o.SetMaxConnections(1); + o.SetMaxQueueSize(1); + o.EnableKeepAlive(keepAlive); + return o; + } + + class TPong: public TRequestReplier { + TDuration Sleep_; + ui16 Port_; + + public: + TPong(TDuration sleep = TDuration(), ui16 port = 80) + : Sleep_(sleep) + , Port_(port) + { + } + + bool DoReply(const TReplyParams& params) override { + TStringBuf path = TParsedHttpFull(params.Input.FirstLine()).Path; + params.Input.ReadAll(); + if (path == "/redirect") { + params.Output << "HTTP/1.1 307 Internal Redirect\r\n" + "Location: http://localhost:" + << Port_ + << "/redirect2?some_param=qwe\r\n" + "Non-Authoritative-Reason: HSTS\r\n\r\n" + "must be missing"; + return true; + } + + if (path == "/redirect2") { + UNIT_ASSERT_VALUES_EQUAL("some_param=qwe", TParsedHttpFull(params.Input.FirstLine()).Cgi); + params.Output << "HTTP/1.1 307 Internal Redirect\r\n" + "Location: http://localhost:" + << Port_ + << "/ping\r\n" + "Non-Authoritative-Reason: HSTS\r\n\r\n" + "must be missing too"; + return true; + } + + if (path != "/ping") { + UNIT_ASSERT_C(false, "path is incorrect: '" << path << "'"); + } + + Sleep(Sleep_); + + THttpResponse resp(HTTP_OK); + resp.SetContent("pong"); + resp.OutTo(params.Output); + + return true; + } + }; + + class TCodedPong: public TRequestReplier { + HttpCodes Code_; + + public: + TCodedPong(HttpCodes code) + : Code_(code) + { + } + + bool DoReply(const TReplyParams& params) override { + if (TParsedHttpFull(params.Input.FirstLine()).Path != "/ping") { + UNIT_ASSERT(false); + } + + THttpResponse resp(Code_); + resp.SetContent("pong"); + resp.OutTo(params.Output); + + return true; + } + }; + + class T500: public TRequestReplier { + ui16 Port_; + + public: + T500(ui16 port) + : Port_(port) + { + } + + bool DoReply(const TReplyParams& params) override { + TStringBuf path = TParsedHttpFull(params.Input.FirstLine()).Path; + + if (path == "/bad_redirect") { + params.Output << "HTTP/1.1 500 Internal Redirect\r\n" + "Location: http://localhost:1/qwerty\r\n" + "Non-Authoritative-Reason: HSTS\r\n\r\n"; + return true; + } + + if (path == "/redirect_to_500") { + params.Output << "HTTP/1.1 307 Internal Redirect\r\n" + "Location: http://localhost:" + << Port_ + << "/500\r\n" + "Non-Authoritative-Reason: HSTS\r\n\r\n"; + return true; + } + + THttpResponse resp(HTTP_INTERNAL_SERVER_ERROR); + resp.SetContent("bang"); + resp.OutTo(params.Output); + + return true; + } + }; + + Y_UNIT_TEST(simpleSuccessful) { + TPortManager pm; + ui16 port = pm.GetPort(80); + NMock::TMockServer server(createOptions(port, false), []() { return new TPong; }); + + TSimpleHttpClient cl("localhost", port); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + + { + TStringStream s; + UNIT_ASSERT_NO_EXCEPTION(cl.DoGet("/ping", &s)); + UNIT_ASSERT_VALUES_EQUAL("pong", s.Str()); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + { + TStringStream s; + UNIT_ASSERT_NO_EXCEPTION(cl.DoGet("/ping", &s)); + UNIT_ASSERT_VALUES_EQUAL("pong", s.Str()); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + + { + TStringStream s; + UNIT_ASSERT_NO_EXCEPTION(cl.DoPost("/ping", "", &s)); + UNIT_ASSERT_VALUES_EQUAL("pong", s.Str()); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + { + TStringStream s; + UNIT_ASSERT_NO_EXCEPTION(cl.DoPost("/ping", "", &s)); + UNIT_ASSERT_VALUES_EQUAL("pong", s.Str()); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + } + + Y_UNIT_TEST(simpleMessages) { + TPortManager pm; + ui16 port = pm.GetPort(80); + NMock::TMockServer server(createOptions(port, false), []() { return new TPong; }); + + TSimpleHttpClient cl("localhost", port); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + + { + TStringStream s; + UNIT_ASSERT_NO_EXCEPTION(cl.DoGet("/ping", &s)); + UNIT_ASSERT_VALUES_EQUAL("pong", s.Str()); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + { + UNIT_ASSERT_NO_EXCEPTION(cl.DoGet("/ping", nullptr)); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + + server.SetGenerator([]() { return new TCodedPong(HTTP_CONTINUE); }); + { + TStringStream s; + UNIT_ASSERT_EXCEPTION_CONTAINS(cl.DoPost("/ping", "", &s), + THttpRequestException, + "Got 100 at localhost/ping\n" + "Full http response:\n"); + UNIT_ASSERT_VALUES_EQUAL("pong", s.Str()); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + { + UNIT_ASSERT_EXCEPTION_CONTAINS(cl.DoPost("/ping", "", nullptr), + THttpRequestException, + "Got 100 at localhost/ping\n" + "Full http response:\n" + "pong"); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + } + + Y_UNIT_TEST(simpleTimeout) { + TPortManager pm; + ui16 port = pm.GetPort(80); + NMock::TMockServer server(createOptions(port, true), []() { return new TPong(TDuration::MilliSeconds(300)); }); + + TSimpleHttpClient cl("localhost", port, TDuration::MilliSeconds(50), TDuration::MilliSeconds(50)); + + TStringStream s; + UNIT_ASSERT_EXCEPTION_CONTAINS(cl.DoGet("/ping", &s), + TSystemError, + "Resource temporarily unavailable"); + UNIT_ASSERT_EXCEPTION_CONTAINS(cl.DoPost("/ping", "", &s), + TSystemError, + "Resource temporarily unavailable"); + } + + Y_UNIT_TEST(simpleError) { + TPortManager pm; + ui16 port = pm.GetPort(80); + NMock::TMockServer server(createOptions(port, true), []() { return new TPong; }); + + TSimpleHttpClient cl("localhost", port); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + + { + TStringStream s; + server.SetGenerator([]() { return new TCodedPong(HTTP_CONTINUE); }); + UNIT_ASSERT_EXCEPTION_CONTAINS(cl.DoGet("/ping", &s), + THttpRequestException, + "Got 100 at localhost/ping\n" + "Full http response:"); + UNIT_ASSERT_VALUES_EQUAL("pong", s.Str()); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + + { + TStringStream s; + server.SetGenerator([]() { return new TCodedPong(HTTP_OK); }); + UNIT_ASSERT_NO_EXCEPTION(cl.DoGet("/ping", &s)); + UNIT_ASSERT_VALUES_EQUAL("pong", s.Str()); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + + server.SetGenerator([]() { return new TCodedPong(HTTP_PARTIAL_CONTENT); }); + UNIT_ASSERT_NO_EXCEPTION(cl.DoGet("/ping", &s)); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + + { + TStringStream s; + server.SetGenerator([]() { return new TCodedPong(HTTP_MULTIPLE_CHOICES); }); + UNIT_ASSERT_EXCEPTION_CONTAINS(cl.DoGet("/ping", &s), + THttpRequestException, + "Got 300 at localhost/ping\n" + "Full http response:"); + UNIT_ASSERT_VALUES_EQUAL("pong", s.Str()); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + } + + Y_UNIT_TEST(redirectable) { + TPortManager pm; + ui16 port = pm.GetPort(80); + NMock::TMockServer server(createOptions(port, true), [port]() { return new TPong(TDuration(), port); }); + + TRedirectableHttpClient cl("localhost", port); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + + { + TStringStream s; + UNIT_ASSERT_NO_EXCEPTION(cl.DoGet("/redirect", &s)); + UNIT_ASSERT_VALUES_EQUAL("pong", s.Str()); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + + server.SetGenerator([port]() { return new T500(port); }); + + TStringStream s; + UNIT_ASSERT_EXCEPTION_CONTAINS(cl.DoGet("/bad_redirect", &s), + THttpRequestException, + "can not connect to "); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + + UNIT_ASSERT_EXCEPTION_CONTAINS(cl.DoGet("/redirect_to_500", &s), + THttpRequestException, + "Got 500 at http://localhost/500\n" + "Full http response:\n"); + UNIT_ASSERT_VALUES_EQUAL("bang", s.Str()); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + } + + Y_UNIT_TEST(keepaliveSuccessful) { + auto test = [](bool keepalive, i64 clientCount) { + TPortManager pm; + ui16 port = pm.GetPort(80); + NMock::TMockServer server(createOptions(port, keepalive), []() { return new TPong; }); + + TKeepAliveHttpClient cl("localhost", port); + UNIT_ASSERT_VALUES_EQUAL(0, server.GetClientCount()); + { + TStringStream s; + int code = -1; + UNIT_ASSERT_NO_EXCEPTION_C(code = cl.DoGet("/ping", &s), keepalive); + UNIT_ASSERT_VALUES_EQUAL_C(200, code, keepalive); + UNIT_ASSERT_VALUES_EQUAL_C("pong", s.Str(), keepalive); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(clientCount, server.GetClientCount()); + } + { + TStringStream s; + int code = -1; + UNIT_ASSERT_NO_EXCEPTION_C(code = cl.DoGet("/ping", &s), keepalive); + UNIT_ASSERT_VALUES_EQUAL_C(200, code, keepalive); + UNIT_ASSERT_VALUES_EQUAL_C("pong", s.Str(), keepalive); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(clientCount, server.GetClientCount()); + } + + { + TStringStream s; + int code = -1; + UNIT_ASSERT_NO_EXCEPTION_C(code = cl.DoPost("/ping", "", &s), keepalive); + UNIT_ASSERT_VALUES_EQUAL_C(200, code, keepalive); + UNIT_ASSERT_VALUES_EQUAL_C("pong", s.Str(), keepalive); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(clientCount, server.GetClientCount()); + } + { + TStringStream s; + int code = -1; + UNIT_ASSERT_NO_EXCEPTION_C(code = cl.DoPost("/ping", "", &s), keepalive); + UNIT_ASSERT_VALUES_EQUAL_C(200, code, keepalive); + UNIT_ASSERT_VALUES_EQUAL_C("pong", s.Str(), keepalive); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_VALUES_EQUAL(clientCount, server.GetClientCount()); + } + }; + + test(true, 1); + test(false, 0); + } + + Y_UNIT_TEST(keepaliveTimeout) { + TPortManager pm; + ui16 port = pm.GetPort(80); + NMock::TMockServer server(createOptions(port, true), []() { return new TPong(TDuration::MilliSeconds(300)); }); + + TKeepAliveHttpClient cl("localhost", port, TDuration::MilliSeconds(50), TDuration::MilliSeconds(50)); + + TStringStream s; + UNIT_ASSERT_EXCEPTION_CONTAINS(cl.DoGet("/ping", &s), + TSystemError, + "Resource temporarily unavailable"); + UNIT_ASSERT_EXCEPTION_CONTAINS(cl.DoPost("/ping", "", &s), + TSystemError, + "Resource temporarily unavailable"); + } + + Y_UNIT_TEST(keepaliveHeaders) { + TPortManager pm; + ui16 port = pm.GetPort(80); + NMock::TMockServer server(createOptions(port, true), []() { return new TPong; }); + + TKeepAliveHttpClient cl("localhost", port); + + TStringStream s; + THttpHeaders h; + UNIT_ASSERT_VALUES_EQUAL(200, cl.DoGet("/ping", &s, {}, &h)); + TStringStream hs; + h.OutTo(&hs); + UNIT_ASSERT_VALUES_EQUAL("Content-Length: 4\r\nConnection: Keep-Alive\r\n", hs.Str()); + } + + Y_UNIT_TEST(keepaliveRaw) { + TPortManager pm; + ui16 port = pm.GetPort(80); + NMock::TMockServer server(createOptions(port, true), []() { return new TPong; }); + + TKeepAliveHttpClient cl("localhost", port); + + TStringStream s; + THttpHeaders h; + + TString raw = "POST /ping HTTP/1.1\r\n" + "Connection: Keep-Alive\r\n" + "Accept-Encoding: gzip, deflate\r\n" + "Content-Length: 9\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "User-Agent: Python-urllib/2.6\r\n" + "\r\n" + "some body"; + + UNIT_ASSERT_VALUES_EQUAL(200, cl.DoRequestRaw(raw, &s, &h)); + TStringStream hs; + h.OutTo(&hs); + UNIT_ASSERT_VALUES_EQUAL("Content-Length: 4\r\nConnection: Keep-Alive\r\n", hs.Str()); + + raw = "GET /ping HT TP/1.1\r\n"; + UNIT_ASSERT_EXCEPTION_CONTAINS(cl.DoRequestRaw(raw, &s, &h), TSystemError, "can not read from socket input stream"); + } + + Y_UNIT_TEST(keepaliveWithClosedByPeer) { + TPortManager pm; + ui16 port = pm.GetPort(80); + NMock::TMockServer::TGenerator gen = []() { return new TPong; }; + THolder<NMock::TMockServer> server = MakeHolder<NMock::TMockServer>(createOptions(port, true), gen); + + TKeepAliveHttpClient cl("localhost", port); + UNIT_ASSERT_NO_EXCEPTION(cl.DoGet("/ping")); + + server.Reset(); + server = MakeHolder<NMock::TMockServer>(createOptions(port, true), gen); + UNIT_ASSERT_NO_EXCEPTION(cl.DoGet("/ping")); + + TKeepAliveHttpClient cl2("localhost", port); + UNIT_ASSERT_NO_EXCEPTION(cl2.DoGet("/ping")); + Sleep(TDuration::MilliSeconds(500)); + UNIT_ASSERT_NO_EXCEPTION(cl.DoGet("/ping")); + } +} diff --git a/library/cpp/http/simple/ut/https_server/http_server.crt b/library/cpp/http/simple/ut/https_server/http_server.crt new file mode 100644 index 0000000000..74d74fafea --- /dev/null +++ b/library/cpp/http/simple/ut/https_server/http_server.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDATCCAemgAwIBAgIJAKnfUOUcLEqUMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV +BAMMDGxvY2FsaG9zdC5teTAeFw0xODA1MDgwOTIxMDZaFw0xOTA1MDgwOTIxMDZa +MBcxFTATBgNVBAMMDGxvY2FsaG9zdC5teTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMVe3pFwlPCrniAAsDyhoolnwv0gOQ4SX81nA0NggabKbUBJwwfN +nKP5dvFNHCo100fzoiWbFmZnu9pUMtjeucQzaA38i501rXCkiPmTkE+tDdIJqO8J +lLV+oaNvFtaAVcRIiuU9fTp/MdZhG3tLj/AXx9dcc1xHRjg/tngepAsvZ2oRoBVU +ijvkOSCm1xwew+ZTzazLARnLOvHok1tJPepMCVlGaEaL9r1aJ86hMUSg+sli2ayW +myI4Pt7ZrsyrHpHDYF9ecWWGbmHfgLdaAdyulrPuvtwavl6KtgSuy3SxwigOfdBI +h4Xw2u6gq4v40OuZGWgkNdJ000ddwurWfosCAwEAAaNQME4wHQYDVR0OBBYEFAd+ +0uv5elelwrjB/0C7EDO7VauqMB8GA1UdIwQYMBaAFAd+0uv5elelwrjB/0C7EDO7 +VauqMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEauDMNWqCIIZXmY +HLqkoPmy+BDX7N4F2ZuWntes8D/igFhZOYQfD+ksJEv3zgs6N5Qd8HbSCbZR0Hh+ +1g+RjVBu8T67h6+vIDZuu0jORjknUp2XbD+aWG+7UcuUjDY8KF9St50ZniSieiSA +dV09VrJ/JFwxaeFzgOHnk9oP5eggwZjEZJqSc4qzL0JlhFcxV8R4OVUCjRyHG73p +cN7nUDL9xN5XZY+6t6+rzdYi4UAhEW0odFVfyXqhOhupSgQkBBdIjxVuov+3h/aV +D2YweTg6cKtuaISsFmDEPht7cVQuy5z3PPkV6kQBeECA9vTFP3wCxA0n7Iyyn2IK +8gvWZXk= +-----END CERTIFICATE----- diff --git a/library/cpp/http/simple/ut/https_server/http_server.key b/library/cpp/http/simple/ut/https_server/http_server.key new file mode 100644 index 0000000000..f58ab049fd --- /dev/null +++ b/library/cpp/http/simple/ut/https_server/http_server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDFXt6RcJTwq54g +ALA8oaKJZ8L9IDkOEl/NZwNDYIGmym1AScMHzZyj+XbxTRwqNdNH86IlmxZmZ7va +VDLY3rnEM2gN/IudNa1wpIj5k5BPrQ3SCajvCZS1fqGjbxbWgFXESIrlPX06fzHW +YRt7S4/wF8fXXHNcR0Y4P7Z4HqQLL2dqEaAVVIo75DkgptccHsPmU82sywEZyzrx +6JNbST3qTAlZRmhGi/a9WifOoTFEoPrJYtmslpsiOD7e2a7Mqx6Rw2BfXnFlhm5h +34C3WgHcrpaz7r7cGr5eirYErst0scIoDn3QSIeF8NruoKuL+NDrmRloJDXSdNNH +XcLq1n6LAgMBAAECggEAdN+wvD8Gc12szRabRcwRC3y+IlYqcwK+aEtPy14iaDoG +Z8NGEiDXWOIoZMtcmkI1Uq4anlov8YQL4UVqtrFtH5mxTFb39agLhGBqHCAdnJDF +VlMSDjqGLNNHtBfcVji4kPrEBOtcdH9Na70lIOWl3m62j/jW9xXdpwFTc93xFg14 +Ivtjtv7KHZAPgN0pdgsqen1js6Z3O5tkcy4yFLldBl+8/ZbYSMM+Rh4GbR5qvWfA +23vBu9EprJKPhFQlNZPbesEKe8EA+SCuLo0RzAZq1E2nZRH0HasKT2hhr/kobkN6 +oLIo2dNgIYL7xMhHLcBt1/08CXKZIqEAfA9Tx/eVgQKBgQD7/oN/XA0pMVCqS8f6 +8Z9VI4NxHJoPFLskrXjXhbxgLgUjuz28cuoyaIKcv8f9Qo7f+hOwR2F3zjwyVexB +G+0fuyIbqD8Po43F+SBJCVSE3EV5k0AQJJN74a+UuKC39NhGaXsmj+s6ArWrURV5 +thay+308pF5HvYCnmQD3UfOJiQKBgQDIghDarcID6/Q0Nv8xvfd8p9kUu5vX/Tw0 +W22JDDMxpUoYCGXvOEx+IoVzqLOTw+NcEXSmDA41VqXlphYopwZkfNV6kIXVymdu +oNKisgfe4Hrfrq9BUl5p8gvU/Ev5zY7N4kVirUJgNvRHDElp8h6Ek/KRTv8Q0xRX +ZW6UqmKGcwKBgDsQZ7/1UnxiO7b+tivicGcjQM7FVnLMeCTbqCRUC1g70SaT35+J +C82u41ZcOULqU9S5p928jWLoawGdVBfatNSoJxF2ePlwa22IvAGCd1YAzyP02KIw +AIWb22yvbbRQZlTyqlPajdb2BaDXC4KQpHdlLPCG0jZce4hM+4X8pmmJAoGALW4S +5YlTGVJf7Wi8n4ecSJk7PVBYujJ9bpt8kP27p7b8t79HYVFPO5EUzaTes09B931Z +AbpficRNKGBeSu21LBWAxRlzyYHnt5AmyYgu8lfIX2AUA2fnTnfyKFrV2A60GX/4 +GqiJDoXFCUgGZkPemElxP203q5c316l6yaJlWnMCgYAqk1G65THRmdTKcnUEOqo8 +pD3SWuBvbOHYLyg+f0zNAqpnTFbaPVmsWfx3CsX2m8WdH3dD28SGfvepQlWj1yp/ +TmXs14nFUuJWir2VbPgp8W/uZl8bQ0YlI8UPUbN3XbLkVIno+jXuUopcgrXmi7Gb +Y2QnQfHePgpszWR0o+WiYg== +-----END PRIVATE KEY----- diff --git a/library/cpp/http/simple/ut/https_server/main.go b/library/cpp/http/simple/ut/https_server/main.go new file mode 100644 index 0000000000..4282810675 --- /dev/null +++ b/library/cpp/http/simple/ut/https_server/main.go @@ -0,0 +1,70 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +type Opts struct { + Port uint16 + KeyFile string + CertFile string +} + +func handler(writer http.ResponseWriter, request *http.Request) { + res := "pong.my" + + writer.Header().Set("Content-Type", "text/plain") + writer.WriteHeader(http.StatusOK) + + _, _ = writer.Write([]byte(res)) +} + +func runServer(opts *Opts) error { + mainMux := http.NewServeMux() + mainMux.Handle("/ping", http.HandlerFunc(handler)) + + server := &http.Server{ + Addr: fmt.Sprintf("localhost:%d", opts.Port), + Handler: mainMux, + ErrorLog: log.New(os.Stdout, "", log.LstdFlags), + } + + return server.ListenAndServeTLS(opts.CertFile, opts.KeyFile) +} + +func markFlagRequired(flags *pflag.FlagSet, names ...string) { + for _, n := range names { + name := n + if err := cobra.MarkFlagRequired(flags, name); err != nil { + panic(err) + } + } +} + +func main() { + opts := Opts{} + + cmd := cobra.Command{ + RunE: func(cmd *cobra.Command, args []string) error { + return runServer(&opts) + }, + } + + flags := cmd.Flags() + flags.Uint16Var(&opts.Port, "port", 0, "") + flags.StringVar(&opts.KeyFile, "keyfile", "", "path to key file") + flags.StringVar(&opts.CertFile, "certfile", "", "path to cert file") + + markFlagRequired(flags, "port", "keyfile", "certfile") + + if err := cmd.Execute(); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "Exit with err: %s", err) + os.Exit(1) + } +} diff --git a/library/cpp/http/simple/ut/https_ut.cpp b/library/cpp/http/simple/ut/https_ut.cpp new file mode 100644 index 0000000000..3849b9ac9a --- /dev/null +++ b/library/cpp/http/simple/ut/https_ut.cpp @@ -0,0 +1,97 @@ +#include <library/cpp/http/simple/http_client.h> + +#include <library/cpp/http/server/response.h> + +#include <library/cpp/testing/unittest/registar.h> +#include <library/cpp/testing/unittest/tests_data.h> + +#include <util/system/shellcommand.h> + +Y_UNIT_TEST_SUITE(Https) { + using TShellCommandPtr = std::unique_ptr<TShellCommand>; + + static TShellCommandPtr start(ui16 port) { + const TString data = ArcadiaSourceRoot() + "/library/cpp/http/simple/ut/https_server"; + + const TString command = + TStringBuilder() + << BuildRoot() << "/library/cpp/http/simple/ut/https_server/https_server" + << " --port " << port + << " --keyfile " << data << "/http_server.key" + << " --certfile " << data << "/http_server.crt"; + + auto res = std::make_unique<TShellCommand>( + command, + TShellCommandOptions() + .SetAsync(true) + .SetLatency(50) + .SetErrorStream(&Cerr)); + + res->Run(); + + i32 tries = 100000; + while (tries-- > 0) { + try { + TKeepAliveHttpClient client("https://localhost", port); + client.DisableVerificationForHttps(); + client.DoGet("/ping"); + break; + } catch (const std::exception& e) { + Cout << "== failed to connect to new server: " << e.what() << Endl; + Sleep(TDuration::MilliSeconds(1)); + } + } + + return res; + } + + static void get(TKeepAliveHttpClient & client) { + TStringStream out; + ui32 code = 0; + + UNIT_ASSERT_NO_EXCEPTION(code = client.DoGet("/ping", &out)); + UNIT_ASSERT_VALUES_EQUAL_C(code, 200, out.Str()); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), "pong.my"); + } + + Y_UNIT_TEST(keepAlive) { + TPortManager pm; + ui16 port = pm.GetPort(443); + TShellCommandPtr httpsServer = start(port); + + TKeepAliveHttpClient client("https://localhost", + port, + TDuration::Seconds(40), + TDuration::Seconds(40)); + client.DisableVerificationForHttps(); + + get(client); + get(client); + + httpsServer->Terminate().Wait(); + httpsServer = start(port); + + get(client); + } + + static void get(TSimpleHttpClient & client) { + TStringStream out; + + UNIT_ASSERT_NO_EXCEPTION_C(client.DoGet("/ping", &out), out.Str()); + UNIT_ASSERT_VALUES_EQUAL(out.Str(), "pong.my"); + } + + Y_UNIT_TEST(simple) { + TPortManager pm; + ui16 port = pm.GetPort(443); + TShellCommandPtr httpsServer = start(port); + + TSimpleHttpClient client("https://localhost", + port, + TDuration::Seconds(40), + TDuration::Seconds(40)); + + get(client); + get(client); + } +} |