Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions net/http/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,37 @@
# For the list of contributors see $ROOTSYS/README/CREDITS.

############################################################################
# CMakeLists.txt file for building ROOT net/http package
# CMakeLists.txt file for building ROOT net/http/test package
# @author Sergey Linev, GSI
############################################################################

# http server does not work on Windows without extra firewall configuration
if(WIN32)
return()
endif()

# curl is required for tests
find_program(CURL_EXECUTABLE curl)

# http server does not work on default Windows configuration
# so skip testing there
if(CURL_EXECUTABLE AND NOT WIN32)
ROOT_ADD_GTEST(testHttpServer test_server.cxx LIBRARIES RHTTP RHTTPSniff Hist)
if(NOT CURL_EXECUTABLE)
return()
endif()

ROOT_ADD_GTEST(testHttpServer test_server.cxx LIBRARIES RHTTP RHTTPSniff Hist)

execute_process(
COMMAND "${CURL_EXECUTABLE}" --version
OUTPUT_VARIABLE CURL_CLI_OUTPUT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)

# only newer curl support websockets - so test version
# curl introduce support from 8.11 but it works reliably only with 8.18
if(CURL_CLI_OUTPUT MATCHES "curl ([0-9]+\\.[0-9]+\\.[0-9]+)")
set(CURL_CLI_VERSION "${CMAKE_MATCH_1}")
message(STATUS "Find curl ${CURL_CLI_VERSION} for http server tests")
if(CURL_CLI_VERSION VERSION_GREATER_EQUAL "8.18.0")
ROOT_ADD_GTEST(testWebSocket test_websocket.cxx LIBRARIES RHTTP Hist)
endif()
endif()
9 changes: 6 additions & 3 deletions net/http/test/test_server.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
Int_t httpport = 0;
TString server_url;


// main http server
std::string execute_request(const char *url, const char *post = nullptr)
{
Expand All @@ -25,10 +26,12 @@ std::string execute_request(const char *url, const char *post = nullptr)
pname = TString::Format("http_server_%d.post", httpport);
std::ofstream f(pname.Data());
f << post;
}

if (post)
exec = TString::Format("curl -sS -X POST '%s%s' --data-binary @%s -o %s", server_url.Data(), url, pname.Data(), fname.Data());
} else {
else
exec = TString::Format("curl -sS '%s%s' -o %s", server_url.Data(), url, fname.Data());
}

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

Expand Down Expand Up @@ -56,7 +59,7 @@ TEST(THttpServer, main)
for(int ntry = 0; ntry < 100; ++ntry) {
Int_t port = (Int_t) (25000 + gRandom->Rndm() * 1000);
// only two threads, bind to loopback address only
TString arg = TString::Format("http:%d?loopback&thrds=2", port);
TString arg = TString::Format("http:%d?loopback&thrds=3", port);
if (serv.CreateEngine(arg)) {
httpport = port;
break;
Expand Down
140 changes: 140 additions & 0 deletions net/http/test/test_websocket.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include "gtest/gtest.h"

#include <string>
#include <fstream>

#include "THttpServer.h"
#include "THttpWSHandler.h"
#include "TROOT.h"

#include "TSystem.h"
#include "TRandom.h"

#include "ROOT/TestSupport.hxx"

Int_t httpport = 0;
TString server_url;

class TUserHandler : public THttpWSHandler {
public:
UInt_t fWSId{0};
Int_t fServCnt{0};

TUserHandler(const char *name = nullptr, const char *title = nullptr) : THttpWSHandler(name, title) {}

// load custom HTML page when open correspondent address
TString GetDefaultPageContent() override { return "dummy_page_content"; }

// ignore threads safety - allow multi-threaded use of handler
Bool_t AllowMTProcess() const override { return kFALSE; }

Bool_t ProcessWS(THttpCallArg *arg) override
{
if (!arg || (arg->GetWSId() == 0))
return kTRUE;

if (arg->IsMethod("WS_CONNECT")) {
// accept only if connection not established
return fWSId == 0;
}

if (arg->IsMethod("WS_READY")) {
fWSId = arg->GetWSId();
return kTRUE;
}

if (arg->IsMethod("WS_CLOSE")) {
fWSId = 0;
return kTRUE;
}

if (arg->IsMethod("WS_DATA")) {
TString str;
str.Append((const char *)arg->GetPostData(), arg->GetPostDataLength());
if ((str == "DataRequest") && (arg->GetWSId() == fWSId))
SendCharStarWS(arg->GetWSId(), "DataResponse");
else
SendCharStarWS(arg->GetWSId(), "DataRejected");
return kTRUE;
}

return kFALSE;
}
};


// main http server
std::string execute_request(const char *url, const char *post = nullptr, Bool_t ws = kFALSE)
{
TString fname = TString::Format("http_server_%d.output", httpport),
pname, exec;

if (post) {
pname = TString::Format("http_server_%d.post", httpport);
std::ofstream f(pname.Data());
f << post;
}

if (post && ws)
exec = TString::Format("curl --no-progress-meter --max-time 10 -T . -N ws://localhost:%d%s < %s > %s", httpport, url, pname.Data(), fname.Data());
else if (post)
exec = TString::Format("curl --no-progress-meter -X POST '%s%s' --data-binary @%s -o %s", server_url.Data(), url, pname.Data(), fname.Data());
else
exec = TString::Format("curl --no-progress-meter '%s%s' -o %s", server_url.Data(), url, fname.Data());

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

std::string res;

// websocket ends with timeout - ignore failure for it
if ((gSystem->Exec(exec) != 0) && !ws)
res = "<fail>";
else
res = THttpServer::ReadFileContent(fname.Data());

gSystem->Unlink(fname);
if (!pname.IsNull())
gSystem->Unlink(pname);

return res;
}

// main http server
TEST(THttpServer, main)
{
THttpServer serv("");

gRandom->SetSeed(0);

for(int ntry = 0; ntry < 100; ++ntry) {
Int_t port = (Int_t) (25000 + gRandom->Rndm() * 1000);
// only two threads, bind to loopback address only
TString arg = TString::Format("http:%d?loopback&thrds=3", port);
if (serv.CreateEngine(arg)) {
httpport = port;
break;
}
}

EXPECT_NE(httpport, 0);

if (!httpport)
return;

server_url = TString::Format("http:/localhost:%d", httpport);

TUserHandler handler("ws", "Test WebSocket handler");

serv.Register("/", &handler);

// let process requests in separate thread
serv.CreateServerThread();

// test reply on html page request
std::string res = execute_request("/ws/index.htm");
EXPECT_EQ(res, "dummy_page_content") << "default html page for webcosket handler";

// test reply on websocket request
res = execute_request("/ws/root.websocket", "DataRequest", kTRUE);
EXPECT_EQ(res, "DataResponse") << "check data after websocket communication";
}
Loading