Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sending sync request through http_client #448

Merged
merged 26 commits into from
Jan 26, 2021

Conversation

lalitb
Copy link
Member

@lalitb lalitb commented Dec 14, 2020

Currently, http_client api supports sending async http requests, with error and response received through callback. For exporters, we need to have the status of http request returned from export function ( refer - #444 (comment) ) . Adding a method to send sync request in client interface, along with it's curl implementation.

Summary of changes:
2. Split SessionManager to HttpClient ( default async http client ) and HttpClientSync ( sync http client )
2. Sync Request Example:

GET:

  HttpClientSync httpClient;
  auto result = httpClient.Get(url);
  if (result){
    auto response = result.GetResponse();
  } else {
    std::cout << result.GetSessionState();
  }

POST:

  HttpClientSync httpClient;
  auto result = httpClient.Post(url, data, headers); // GET request
  if (result){
    auto response = result.GetResponse();
  } else {
    std::cout << result.GetSessionState();
  }

Pending Change:

  1. Add test case for HTTP POST sync request.

@lalitb lalitb requested a review from a team December 14, 2020 15:29
@codecov
Copy link

codecov bot commented Dec 14, 2020

Codecov Report

Merging #448 (d43d5ae) into main (8fd86ed) will decrease coverage by 0.29%.
The diff coverage is 78.88%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #448      +/-   ##
==========================================
- Coverage   94.46%   94.16%   -0.30%     
==========================================
  Files         194      195       +1     
  Lines        8509     8571      +62     
==========================================
+ Hits         8038     8071      +33     
- Misses        471      500      +29     
Impacted Files Coverage Δ
...t/src/http/client/curl/http_client_factory_curl.cc 60.00% <33.33%> (-40.00%) ⬇️
ext/src/http/client/curl/http_client_curl.cc 55.55% <55.55%> (ø)
...ntelemetry/ext/http/client/curl/http_client_curl.h 82.07% <65.78%> (-13.05%) ⬇️
...lemetry/ext/http/client/curl/http_operation_curl.h 78.52% <100.00%> (-3.91%) ⬇️
...nclude/opentelemetry/ext/http/client/http_client.h 95.45% <100.00%> (+4.54%) ⬆️
ext/test/http/curl_http_test.cc 94.50% <100.00%> (+0.49%) ⬆️
sdk/test/metrics/counter_aggregator_test.cc 98.21% <0.00%> (-1.79%) ⬇️
sdk/src/logs/batch_log_processor.cc 93.82% <0.00%> (-1.24%) ⬇️
... and 1 more

@lalitb
Copy link
Member Author

lalitb commented Dec 18, 2020

@maxgolov Need you review for this : ) Thanks.

received_requests_.clear();
curl::SessionManager session_manager;

auto session =
Copy link
Contributor

@maxgolov maxgolov Dec 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I need to send 2 synchronous requests from two different threads, do I need to create two separate sessions?

Why do I have to specify session IP, port, and Uri separately - is there a reason to not having something like:

oReq.open("GET", "http://www.example.org/example.txt")

Copy link
Member Author

@lalitb lalitb Jan 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I need to send 2 synchronous requests from two different threads, do I need to create two separate sessions?

Session is a logical entity here for maintaining session id, ssl cert ( if needed ), callback object ( for async request) etc. HTTP is stateless protocol, so it doesn't makes sense to have ongoing session, and sending HTTP Requests over that.

Why do I have to specify session IP, port, and Uri separately - is there a reason to not having something like:
oReq.open("GET", "http://www.example.org/example.txt")

Yeah, its doable as separate PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I have done the modified sync requests as below:

  HttpClient httpClient;
  auto result = httpClient.Get(url);
  if (result){
    auto response = result.GetResponse();
  } else {
    std::cout << result.GetSessionState();
  }

auto session = sessionManager.createSession("localhost", 8000);
auto request = session->CreateRequest();
request->AddHeader(..);
SessionState session_state;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is not a session state - this is given request state?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct.

@@ -153,6 +153,33 @@ class Session : public http_client::Session
});
}

virtual std::unique_ptr<http_client::Response> SendRequestSync(
Copy link
Contributor

@maxgolov maxgolov Dec 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it would be much cleaner if you operate only on Response object rather than on SessionState. Let me elaborate - right now you have two possible states:

  • success - in this case you return Response and status code; or
  • failure - in this case you force the user to test if Response ptr is nullptr, and then inspect the SessionState parameter passed by reference.. which is not really Session state (because Session may have multiple requests), but rather this exact Request state.

So since in reality it is Request state, can you have this aggregated on Response object - making sure that you always return Response: failed or succeeded, always return it. It may be successful response or empty response, failed response, with an indication on Response object that it's a failed response. Then you avoid the nullptr entirely, you avoid checking on two separate objects, and you can just propagate the object elsewhere to the place that needs to make a decision what to do next with that failed request-response pair.

Could you please clarify - if the original intent of SessionManager was to support more than one concurrent request at a time? If so, then SessionState is a misleading name for a synchronous request resultt. Session may have more than one request, one failed, one successful, and SessionState name does not represent the actual SessionManager Session state.

Copy link
Member Author

@lalitb lalitb Jan 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Response object represents http-response containing - body, header and code. Any error during http handshake are returned in SessionState. This is to keep it consistent with how we achieve it in async requests:

EventHandler::OnResponse(Response &);
EventHandler::OnError(SessionState);
Session::SendRequest(EventHandler &); // async
unique_ptr<Request> Session::SendRequestSync(SessionState &); // sync

If it's fine, we can always return empty response in case of error, and the Response::status_code would be 0 in that case.

Session is more of logical entity, and doesn't represent multiple ongoing http requests.
SessionManager -> Multiple Sessions.
Single Session -> Single Request

{
// we have a http response
auto response = std::unique_ptr<Response>(new Response());
response->headers_ = curl_operation_->GetResponseHeaders();
Copy link
Contributor

@maxgolov maxgolov Dec 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you propagate Response code? If you are using int for that, then in Java it'd typically allocate -1 for failed response status code, which can then be used to determine if request was actually processed, as in connected and got some response code from server.

Copy link
Member Author

@lalitb lalitb Jan 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently we use uint16_t for status_code. If it's fine, we can always return empty response in case of error, and the Response::status_code would be 0 in that case ?

{
is_session_active_ = true;
std::string url = host_ + std::string(http_request_->uri_);
curl_operation_.reset(new HttpOperation(http_request_->method_, url, nullptr, RequestMode::Sync,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we set RequestMode::Sync in SendSync() or just deduce it from SendSync() and SendAsync(). Is the inconsistency between ctor and SendSync() handled?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do need to know the actual sync mode in ctor. There are some initializations done in ctor, and the sync mode information is used to propagate the error/results to clients.

Is the inconsistency between ctor and SendSync() handled?

What kind of inconsistency we are talking about.

@@ -117,7 +117,7 @@ class ElasticsearchLogExporter final : public sdklogs::LogExporter
ElasticsearchExporterOptions options_;

// Object that stores the HTTP sessions that have been created
std::unique_ptr<ext::http::client::SessionManager> session_manager_;
std::unique_ptr<ext::http::client::HttpClient> session_manager_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename the variable name to http_client_ too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, this needs to be done in both cc and h. I was thinking to change it once @maxgolov is fine with the current changes ( to avoid reverting back). But thanks for noticing it. I will add this as pending change in PR description.

private:
std::shared_ptr<Request> http_request_;
std::string host_;
std::unique_ptr<HttpOperation> curl_operation_;
uint64_t session_id_;
SessionManager &session_manager_;
HttpClient &session_manager_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename the variable to http_client_ as well?

Copy link
Contributor

@ThomsonTan ThomsonTan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with some minor comments.

Base automatically changed from master to main January 26, 2021 00:46
@lalitb lalitb merged commit 7c6220a into open-telemetry:main Jan 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants