Skip to content

Commit fe86864

Browse files
committed
[http] introduce civetweb testing
If curl utility found - start http server on local port and run several tests Do not run test on windows - it require firewall changes before
1 parent b88eecb commit fe86864

3 files changed

Lines changed: 178 additions & 1 deletion

File tree

net/http/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (C) 1995-2025, Rene Brun and Fons Rademakers.
1+
# Copyright (C) 1995-2026, Rene Brun and Fons Rademakers.
22
# All rights reserved.
33
#
44
# For the licensing terms see $ROOTSYS/LICENSE.
@@ -117,3 +117,5 @@ if(FASTCGI_FOUND)
117117
else()
118118
target_compile_definitions(RHTTP PUBLIC -DHTTP_WITHOUT_FASTCGI)
119119
endif()
120+
121+
ROOT_ADD_TEST_SUBDIRECTORY(test)

net/http/test/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright (C) 1995-2026, Rene Brun and Fons Rademakers.
2+
# All rights reserved.
3+
#
4+
# For the licensing terms see $ROOTSYS/LICENSE.
5+
# For the list of contributors see $ROOTSYS/README/CREDITS.
6+
7+
############################################################################
8+
# CMakeLists.txt file for building ROOT net/http package
9+
# @author Sergey Linev, GSI
10+
############################################################################
11+
12+
find_program(CURL_EXECUTABLE curl)
13+
14+
# http server does not work on default Windows configuration
15+
# so skip testing there
16+
if(CURL_EXECUTABLE AND NOT WIN32)
17+
ROOT_ADD_GTEST(testHttpServer test_server.cxx LIBRARIES RHTTP RHTTPSniff Hist)
18+
endif()

net/http/test/test_server.cxx

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#include "gtest/gtest.h"
2+
3+
#include <string>
4+
#include <fstream>
5+
6+
#include "THttpServer.h"
7+
#include "TROOT.h"
8+
9+
#include "TSystem.h"
10+
#include "TNamed.h"
11+
#include "TRandom.h"
12+
13+
#include "ROOT/TestSupport.hxx"
14+
15+
Int_t httpport = 0;
16+
TString server_url;
17+
18+
// main http server
19+
std::string execute_request(const char *url, const char *post = nullptr)
20+
{
21+
TString fname = TString::Format("http_server_%d.output", httpport),
22+
pname, exec;
23+
24+
if (post) {
25+
pname = TString::Format("http_server_%d.post", httpport);
26+
std::ofstream f(pname.Data());
27+
f << post;
28+
exec = TString::Format("curl -sS -X POST '%s%s' --data-binary @%s -o %s", server_url.Data(), url, pname.Data(), fname.Data());
29+
} else {
30+
exec = TString::Format("curl -sS '%s%s' -o %s", server_url.Data(), url, fname.Data());
31+
}
32+
33+
printf("Execute %s\n", exec.Data());
34+
35+
std::string res;
36+
37+
if (gSystem->Exec(exec) != 0)
38+
res = "<fail>";
39+
else
40+
res = THttpServer::ReadFileContent(fname.Data());
41+
42+
gSystem->Unlink(fname);
43+
if (!pname.IsNull())
44+
gSystem->Unlink(pname);
45+
46+
return res;
47+
}
48+
49+
// main http server
50+
TEST(THttpServer, main)
51+
{
52+
THttpServer serv("");
53+
54+
gRandom->SetSeed(0);
55+
56+
for(int ntry = 0; ntry < 100; ++ntry) {
57+
Int_t port = (Int_t) (25000 + gRandom->Rndm() * 1000);
58+
// only two threads, bind to loopback address only
59+
TString arg = TString::Format("http:%d?loopback&thrds=2", port);
60+
if (serv.CreateEngine(arg)) {
61+
httpport = port;
62+
break;
63+
}
64+
}
65+
66+
EXPECT_NE(httpport, 0);
67+
68+
if (!httpport)
69+
return;
70+
71+
server_url = TString::Format("http:/localhost:%d", httpport);
72+
73+
TNamed obj1("obj1", "title1");
74+
TNamed obj2("obj2", "title2");
75+
76+
serv.Register("/", &obj1);
77+
serv.Register("/", &obj2);
78+
79+
// let process requests in separate thread
80+
serv.CreateServerThread();
81+
82+
// check JSON representation for the object
83+
std::string res = execute_request("/obj1/root.json");
84+
EXPECT_EQ(res, "{\n"
85+
" \"_typename\" : \"TNamed\",\n"
86+
" \"fUniqueID\" : 0,\n"
87+
" \"fBits\" : 8,\n"
88+
" \"fName\" : \"obj1\",\n"
89+
" \"fTitle\" : \"title1\"\n"
90+
"}");
91+
92+
// check XML representation for the object
93+
res = execute_request("/obj1/root.xml");
94+
EXPECT_EQ(res, "<Object class=\"TNamed\">\n"
95+
" <TNamed version=\"1\">\n"
96+
" <TObject fUniqueID=\"0\" fBits=\"8\"/>\n"
97+
" <fName str=\"obj1\"/>\n"
98+
" <fTitle str=\"title1\"/>\n"
99+
" </TNamed>\n"
100+
"</Object>\n");
101+
102+
103+
// check BINARY representation for the object
104+
res = execute_request("/obj1/root.bin");
105+
// keep minimal margin for binary format change
106+
EXPECT_NEAR(res.length(), 28, 4);
107+
108+
// check ROOT file creation
109+
res = execute_request("/obj1/file.root");
110+
// TODO: anlyze why so big size for small object
111+
EXPECT_NEAR(res.length(), 2097152, 10000);
112+
113+
// check item request hierarchy request
114+
res = execute_request("/obj1/item.json");
115+
116+
EXPECT_EQ(res, "{\n"
117+
" \"_name\" : \"obj1\",\n"
118+
" \"_root_version\" : " + std::to_string(gROOT->GetVersionCode()) + ",\n"
119+
" \"_kind\" : \"ROOT.TNamed\",\n"
120+
" \"_title\" : \"title1\"\n"
121+
"}");
122+
123+
124+
// check multi request to several objects
125+
res = execute_request("/multi.json?number=2", "/obj1/root.json\n/obj2/root.json\n");
126+
EXPECT_EQ(res, "[{\n"
127+
" \"_typename\" : \"TNamed\",\n"
128+
" \"fUniqueID\" : 0,\n"
129+
" \"fBits\" : 8,\n"
130+
" \"fName\" : \"obj1\",\n"
131+
" \"fTitle\" : \"title1\"\n"
132+
"}, {\n"
133+
" \"_typename\" : \"TNamed\",\n"
134+
" \"fUniqueID\" : 0,\n"
135+
" \"fBits\" : 8,\n"
136+
" \"fName\" : \"obj2\",\n"
137+
" \"fTitle\" : \"title2\"\n"
138+
"}]");
139+
140+
141+
// by default methods execution is not allowed
142+
res = execute_request("/obj1/exe.json?method=GetTitle");
143+
EXPECT_EQ(res, "");
144+
145+
146+
// disable readonly to get extra functionality
147+
serv.SetReadOnly(kFALSE);
148+
149+
// only now one can execute method
150+
res = execute_request("/obj1/exe.json?method=GetTitle");
151+
EXPECT_EQ(res, "\"title1\"");
152+
153+
res = execute_request("/obj1/exe.json?method=SetTitle&title=NewTitle");
154+
EXPECT_EQ(res, "null");
155+
EXPECT_EQ(std::string("NewTitle"), obj1.GetTitle());
156+
obj1.SetTitle("title1");
157+
}

0 commit comments

Comments
 (0)