1 // Copyright The OpenTelemetry Authors
2 // SPDX-License-Identifier: Apache-2.0
4 #include "opentelemetry/ext//http/client/curl/http_client_curl.h"
5 #include "opentelemetry/ext/http/client/http_client_factory.h"
6 #include "opentelemetry/ext/http/server/http_server.h"
11 #include <condition_variable>
17 #define HTTP_PORT 19000
19 #include <gtest/gtest.h>
21 namespace curl
= opentelemetry::ext::http::client::curl
;
22 namespace http_client
= opentelemetry::ext::http::client
;
23 namespace nostd
= opentelemetry::nostd
;
25 class CustomEventHandler
: public http_client::EventHandler
28 virtual void OnResponse(http_client::Response
&response
) noexcept override
{};
29 virtual void OnEvent(http_client::SessionState state
, nostd::string_view reason
) noexcept override
31 virtual void OnConnecting(const http_client::SSLCertificate
&) noexcept
{}
32 virtual ~CustomEventHandler() = default;
33 bool is_called_
= false;
36 class GetEventHandler
: public CustomEventHandler
38 void OnResponse(http_client::Response
&response
) noexcept override
40 ASSERT_EQ(200, response
.GetStatusCode());
41 ASSERT_EQ(response
.GetBody().size(), 0);
46 class PostEventHandler
: public CustomEventHandler
48 void OnResponse(http_client::Response
&response
) noexcept override
50 ASSERT_EQ(200, response
.GetStatusCode());
51 std::string
body(response
.GetBody().begin(), response
.GetBody().end());
52 ASSERT_EQ(body
, "{'k1':'v1', 'k2':'v2', 'k3':'v3'}");
57 class BasicCurlHttpTests
: public ::testing::Test
, public HTTP_SERVER_NS::HttpRequestCallback
60 HTTP_SERVER_NS::HttpServer server_
;
61 std::string server_address_
;
62 std::atomic
<bool> is_setup_
;
63 std::atomic
<bool> is_running_
;
64 std::vector
<HTTP_SERVER_NS::HttpRequest
> received_requests_
;
65 std::mutex mtx_requests
;
66 std::condition_variable cv_got_events
;
70 BasicCurlHttpTests() : is_setup_(false), is_running_(false){};
72 virtual void SetUp() override
74 if (is_setup_
.exchange(true))
78 int port
= server_
.addListeningPort(HTTP_PORT
);
79 std::ostringstream os
;
80 os
<< "localhost:" << port
;
81 server_address_
= "http://" + os
.str() + "/simple/";
82 server_
.setServerName(os
.str());
83 server_
.setKeepalive(false);
84 server_
.addHandler("/simple/", *this);
85 server_
.addHandler("/get/", *this);
86 server_
.addHandler("/post/", *this);
91 virtual void TearDown() override
93 if (!is_setup_
.exchange(false))
99 virtual int onHttpRequest(HTTP_SERVER_NS::HttpRequest
const &request
,
100 HTTP_SERVER_NS::HttpResponse
&response
) override
102 int response_status
= 404;
103 if (request
.uri
== "/get/")
106 std::unique_lock
<std::mutex
> lk(mtx_requests
);
107 received_requests_
.push_back(request
);
108 response
.headers
["Content-Type"] = "text/plain";
109 response_status
= 200;
111 if (request
.uri
== "/post/")
113 std::unique_lock
<std::mutex
> lk(mtx_requests
);
114 received_requests_
.push_back(request
);
115 response
.headers
["Content-Type"] = "application/json";
116 response
.body
= "{'k1':'v1', 'k2':'v2', 'k3':'v3'}";
117 response_status
= 200;
120 cv_got_events
.notify_one();
122 return response_status
;
125 bool waitForRequests(unsigned timeOutSec
, unsigned expected_count
= 1)
127 std::unique_lock
<std::mutex
> lk(mtx_requests
);
128 if (cv_got_events
.wait_for(lk
, std::chrono::milliseconds(1000 * timeOutSec
),
129 [&] { return received_requests_
.size() >= expected_count
; }))
137 TEST_F(BasicCurlHttpTests
, DoNothing
) {}
139 TEST_F(BasicCurlHttpTests
, HttpRequest
)
142 const char *b
= "test-data";
143 http_client::Body body
= {b
, b
+ strlen(b
)};
144 http_client::Body body1
= body
;
146 ASSERT_EQ(req
.body_
, body1
);
147 req
.AddHeader("name1", "value1");
148 req
.AddHeader("name2", "value2");
149 ASSERT_TRUE(req
.headers_
.find("name1")->second
== "value1");
150 ASSERT_TRUE(req
.headers_
.find("name2")->second
== "value2");
152 req
.ReplaceHeader("name1", "value3");
153 ASSERT_EQ(req
.headers_
.find("name1")->second
, "value3");
155 req
.SetTimeoutMs(std::chrono::duration
<int>(2000));
156 ASSERT_EQ(req
.timeout_ms_
, std::chrono::duration
<int>(2000));
159 TEST_F(BasicCurlHttpTests
, HttpResponse
)
162 http_client::Headers m1
= {
163 {"name1", "value1_1"}, {"name1", "value1_2"}, {"name2", "value3"}, {"name3", "value3"}};
166 const char *b
= "test-data";
167 http_client::Body body
= {b
, b
+ strlen(b
)};
169 res
.ForEachHeader("name1", [&count
](nostd::string_view name
, nostd::string_view value
) {
172 if (value
!= "value1_1" && value
!= "value1_2")
179 res
.ForEachHeader([&count
](nostd::string_view name
, nostd::string_view value
) {
180 if (name
!= "name1" && name
!= "name2" && name
!= "name3")
182 if (value
!= "value1_1" && value
!= "value1_2" && value
!= "value2" && value
!= "value3")
190 TEST_F(BasicCurlHttpTests
, SendGetRequest
)
192 received_requests_
.clear();
193 auto session_manager
= http_client::HttpClientFactory::Create();
194 EXPECT_TRUE(session_manager
!= nullptr);
196 auto session
= session_manager
->CreateSession("http://127.0.0.1:19000");
197 auto request
= session
->CreateRequest();
198 request
->SetUri("get/");
199 GetEventHandler
*handler
= new GetEventHandler();
200 session
->SendRequest(*handler
);
201 ASSERT_TRUE(waitForRequests(30, 1));
202 session
->FinishSession();
203 ASSERT_TRUE(handler
->is_called_
);
207 TEST_F(BasicCurlHttpTests
, SendPostRequest
)
209 received_requests_
.clear();
210 auto session_manager
= http_client::HttpClientFactory::Create();
211 EXPECT_TRUE(session_manager
!= nullptr);
213 auto session
= session_manager
->CreateSession("http://127.0.0.1:19000");
214 auto request
= session
->CreateRequest();
215 request
->SetUri("post/");
216 request
->SetMethod(http_client::Method::Post
);
218 const char *b
= "test-data";
219 http_client::Body body
= {b
, b
+ strlen(b
)};
220 request
->SetBody(body
);
221 request
->AddHeader("Content-Type", "text/plain");
222 PostEventHandler
*handler
= new PostEventHandler();
223 session
->SendRequest(*handler
);
224 ASSERT_TRUE(waitForRequests(30, 1));
225 session
->FinishSession();
226 ASSERT_TRUE(handler
->is_called_
);
228 session_manager
->CancelAllSessions();
229 session_manager
->FinishAllSessions();
234 TEST_F(BasicCurlHttpTests
, RequestTimeout
)
236 received_requests_
.clear();
237 auto session_manager
= http_client::HttpClientFactory::Create();
238 EXPECT_TRUE(session_manager
!= nullptr);
240 auto session
= session_manager
->CreateSession("222.222.222.200:19000"); // Non Existing address
241 auto request
= session
->CreateRequest();
242 request
->SetUri("get/");
243 GetEventHandler
*handler
= new GetEventHandler();
244 session
->SendRequest(*handler
);
245 session
->FinishSession();
246 ASSERT_FALSE(handler
->is_called_
);
250 TEST_F(BasicCurlHttpTests
, CurlHttpOperations
)
252 GetEventHandler
*handler
= new GetEventHandler();
254 const char *b
= "test-data";
255 http_client::Body body
= {b
, b
+ strlen(b
)};
257 http_client::Headers headers
= {
258 {"name1", "value1_1"}, {"name1", "value1_2"}, {"name2", "value3"}, {"name3", "value3"}};
260 curl::HttpOperation
http_operations1(http_client::Method::Head
, "/get", handler
,
261 curl::RequestMode::Async
, headers
, body
, true);
262 http_operations1
.Send();
264 curl::HttpOperation
http_operations2(http_client::Method::Get
, "/get", handler
,
265 curl::RequestMode::Async
, headers
, body
, true);
266 http_operations2
.Send();
268 curl::HttpOperation
http_operations3(http_client::Method::Get
, "/get", handler
,
269 curl::RequestMode::Async
, headers
, body
, false);
270 http_operations3
.Send();
274 TEST_F(BasicCurlHttpTests
, SendGetRequestSync
)
276 received_requests_
.clear();
277 curl::HttpClientSync http_client
;
279 http_client::Headers m1
= {};
280 auto result
= http_client
.Get("http://127.0.0.1:19000/get/", m1
);
281 EXPECT_EQ(result
, true);
282 EXPECT_EQ(result
.GetSessionState(), http_client::SessionState::Response
);
285 TEST_F(BasicCurlHttpTests
, SendGetRequestSyncTimeout
)
287 received_requests_
.clear();
288 curl::HttpClientSync http_client
;
290 http_client::Headers m1
= {};
291 auto result
= http_client
.Get("http://222.222.222.200:19000/get/", m1
);
292 EXPECT_EQ(result
, false);
294 // When network is under proxy, it may connect success but closed by peer when send data
295 EXPECT_TRUE(result
.GetSessionState() == http_client::SessionState::ConnectFailed
||
296 result
.GetSessionState() == http_client::SessionState::SendFailed
);
299 TEST_F(BasicCurlHttpTests
, SendPostRequestSync
)
301 received_requests_
.clear();
302 curl::HttpClientSync http_client
;
304 http_client::Headers m1
= {};
305 http_client::Body body
= {};
306 auto result
= http_client
.Post("http://127.0.0.1:19000/post/", body
, m1
);
307 EXPECT_EQ(result
, true);
308 EXPECT_EQ(result
.GetSessionState(), http_client::SessionState::Response
);
311 TEST_F(BasicCurlHttpTests
, GetBaseUri
)
313 curl::HttpClient session_manager
;
315 auto session
= session_manager
.CreateSession("127.0.0.1:80");
316 ASSERT_EQ(std::static_pointer_cast
<curl::Session
>(session
)->GetBaseUri(), "http://127.0.0.1:80/");
318 session
= session_manager
.CreateSession("https://127.0.0.1:443");
319 ASSERT_EQ(std::static_pointer_cast
<curl::Session
>(session
)->GetBaseUri(),
320 "https://127.0.0.1:443/");
322 session
= session_manager
.CreateSession("http://127.0.0.1:31339");
323 ASSERT_EQ(std::static_pointer_cast
<curl::Session
>(session
)->GetBaseUri(),
324 "http://127.0.0.1:31339/");