Skip to content

Commit 282f96d

Browse files
committed
[http] add websocket handler tests
Introduce simple websocket handler and verify that response is received. Use 5 seconds timeout - curl has no option to break data receiving with other methods Requires newer curl - so test its version
1 parent 6624e86 commit 282f96d

3 files changed

Lines changed: 175 additions & 8 deletions

File tree

net/http/test/CMakeLists.txt

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,38 @@
55
# For the list of contributors see $ROOTSYS/README/CREDITS.
66

77
############################################################################
8-
# CMakeLists.txt file for building ROOT net/http package
8+
# CMakeLists.txt file for building ROOT net/http/test package
99
# @author Sergey Linev, GSI
1010
############################################################################
1111

12-
find_program(CURL_EXECUTABLE curl)
13-
1412
# http server does not work on default Windows configuration
1513
# so skip testing there
16-
if(CURL_EXECUTABLE AND NOT WIN32)
17-
ROOT_ADD_GTEST(testHttpServer test_server.cxx LIBRARIES RHTTP RHTTPSniff Hist)
14+
15+
if(WIN32)
16+
return()
17+
endif()
18+
19+
# curl is required for tests
20+
21+
find_program(CURL_EXECUTABLE curl)
22+
23+
if(NOT CURL_EXECUTABLE)
24+
return()
25+
endif()
26+
27+
ROOT_ADD_GTEST(testHttpServer test_server.cxx LIBRARIES RHTTP RHTTPSniff Hist)
28+
29+
execute_process(
30+
COMMAND "${CURL_EXECUTABLE}" --version
31+
OUTPUT_VARIABLE CURL_CLI_OUTPUT
32+
ERROR_QUIET
33+
OUTPUT_STRIP_TRAILING_WHITESPACE
34+
)
35+
36+
# only newer curl support websockets - so test version
37+
if(CURL_CLI_OUTPUT MATCHES "curl ([0-9]+\\.[0-9]+\\.[0-9]+)")
38+
set(CURL_CLI_VERSION "${CMAKE_MATCH_1}")
39+
if(CURL_CLI_VERSION VERSION_GREATER_EQUAL "8.11.0")
40+
ROOT_ADD_GTEST(testWebSocket test_websocket.cxx LIBRARIES RHTTP Hist)
41+
endif()
1842
endif()

net/http/test/test_server.cxx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
Int_t httpport = 0;
1616
TString server_url;
1717

18+
1819
// main http server
1920
std::string execute_request(const char *url, const char *post = nullptr)
2021
{
@@ -25,10 +26,12 @@ std::string execute_request(const char *url, const char *post = nullptr)
2526
pname = TString::Format("http_server_%d.post", httpport);
2627
std::ofstream f(pname.Data());
2728
f << post;
29+
}
30+
31+
if (post)
2832
exec = TString::Format("curl -sS -X POST '%s%s' --data-binary @%s -o %s", server_url.Data(), url, pname.Data(), fname.Data());
29-
} else {
33+
else
3034
exec = TString::Format("curl -sS '%s%s' -o %s", server_url.Data(), url, fname.Data());
31-
}
3235

3336
printf("Execute %s\n", exec.Data());
3437

@@ -56,7 +59,7 @@ TEST(THttpServer, main)
5659
for(int ntry = 0; ntry < 100; ++ntry) {
5760
Int_t port = (Int_t) (25000 + gRandom->Rndm() * 1000);
5861
// only two threads, bind to loopback address only
59-
TString arg = TString::Format("http:%d?loopback&thrds=2", port);
62+
TString arg = TString::Format("http:%d?loopback&thrds=3", port);
6063
if (serv.CreateEngine(arg)) {
6164
httpport = port;
6265
break;

net/http/test/test_websocket.cxx

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#include "gtest/gtest.h"
2+
3+
#include <string>
4+
#include <fstream>
5+
6+
#include "THttpServer.h"
7+
#include "THttpWSHandler.h"
8+
#include "TROOT.h"
9+
10+
#include "TSystem.h"
11+
#include "TRandom.h"
12+
13+
#include "ROOT/TestSupport.hxx"
14+
15+
Int_t httpport = 0;
16+
TString server_url;
17+
18+
class TUserHandler : public THttpWSHandler {
19+
public:
20+
UInt_t fWSId{0};
21+
Int_t fServCnt{0};
22+
23+
TUserHandler(const char *name = nullptr, const char *title = nullptr) : THttpWSHandler(name, title) {}
24+
25+
// load custom HTML page when open correspondent address
26+
TString GetDefaultPageContent() override { return "dummy_page_content"; }
27+
28+
// ignore threads safety - allow multi-threaded use of handler
29+
Bool_t AllowMTProcess() const override { return kFALSE; }
30+
31+
Bool_t ProcessWS(THttpCallArg *arg) override
32+
{
33+
if (!arg || (arg->GetWSId() == 0))
34+
return kTRUE;
35+
36+
if (arg->IsMethod("WS_CONNECT")) {
37+
// accept only if connection not established
38+
return fWSId == 0;
39+
}
40+
41+
if (arg->IsMethod("WS_READY")) {
42+
fWSId = arg->GetWSId();
43+
return kTRUE;
44+
}
45+
46+
if (arg->IsMethod("WS_CLOSE")) {
47+
fWSId = 0;
48+
return kTRUE;
49+
}
50+
51+
if (arg->IsMethod("WS_DATA")) {
52+
TString str;
53+
str.Append((const char *)arg->GetPostData(), arg->GetPostDataLength());
54+
if ((str == "DataRequest") && (arg->GetWSId() == fWSId))
55+
SendCharStarWS(arg->GetWSId(), "DataResponse");
56+
else
57+
SendCharStarWS(arg->GetWSId(), "DataRejected");
58+
return kTRUE;
59+
}
60+
61+
return kFALSE;
62+
}
63+
};
64+
65+
66+
// main http server
67+
std::string execute_request(const char *url, const char *post = nullptr, Bool_t ws = kFALSE)
68+
{
69+
TString fname = TString::Format("http_server_%d.output", httpport),
70+
pname, exec;
71+
72+
if (post) {
73+
pname = TString::Format("http_server_%d.post", httpport);
74+
std::ofstream f(pname.Data());
75+
f << post;
76+
}
77+
78+
if (post && ws)
79+
exec = TString::Format("curl --no-progress-meter --max-time 10 -T . -N ws://localhost:%d%s < %s > %s", httpport, url, pname.Data(), fname.Data());
80+
else if (post)
81+
exec = TString::Format("curl --no-progress-meter -X POST '%s%s' --data-binary @%s -o %s", server_url.Data(), url, pname.Data(), fname.Data());
82+
else
83+
exec = TString::Format("curl --no-progress-meter '%s%s' -o %s", server_url.Data(), url, fname.Data());
84+
85+
printf("Execute %s\n", exec.Data());
86+
87+
std::string res;
88+
89+
// websocket ends with timeout - ignore failure for it
90+
if ((gSystem->Exec(exec) != 0) && !ws)
91+
res = "<fail>";
92+
else
93+
res = THttpServer::ReadFileContent(fname.Data());
94+
95+
gSystem->Unlink(fname);
96+
if (!pname.IsNull())
97+
gSystem->Unlink(pname);
98+
99+
return res;
100+
}
101+
102+
// main http server
103+
TEST(THttpServer, main)
104+
{
105+
THttpServer serv("");
106+
107+
gRandom->SetSeed(0);
108+
109+
for(int ntry = 0; ntry < 100; ++ntry) {
110+
Int_t port = (Int_t) (25000 + gRandom->Rndm() * 1000);
111+
// only two threads, bind to loopback address only
112+
TString arg = TString::Format("http:%d?loopback&thrds=3", port);
113+
if (serv.CreateEngine(arg)) {
114+
httpport = port;
115+
break;
116+
}
117+
}
118+
119+
EXPECT_NE(httpport, 0);
120+
121+
if (!httpport)
122+
return;
123+
124+
server_url = TString::Format("http:/localhost:%d", httpport);
125+
126+
TUserHandler handler("ws", "Test WebSocket handler");
127+
128+
serv.Register("/", &handler);
129+
130+
// let process requests in separate thread
131+
serv.CreateServerThread();
132+
133+
// test reply on html page request
134+
std::string res = execute_request("/ws/index.htm");
135+
EXPECT_EQ(res, "dummy_page_content") << "default html page for webcosket handler";
136+
137+
// test reply on websocket request
138+
res = execute_request("/ws/root.websocket", "DataRequest", kTRUE);
139+
EXPECT_EQ(res, "DataResponse") << "check data after websocket communication";
140+
}

0 commit comments

Comments
 (0)