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

[feat] http client with cert #302

Merged
merged 1 commit into from
Dec 15, 2023
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
21 changes: 15 additions & 6 deletions net/http/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,24 @@ static constexpr char USERAGENT[] = "EASE/0.21.6";
class PooledDialer {
public:
net::TLSContext* tls_ctx = nullptr;
bool tls_ctx_ownership;
std::unique_ptr<ISocketClient> tcpsock;
std::unique_ptr<ISocketClient> tlssock;
std::unique_ptr<Resolver> resolver;

//etsocket seems not support multi thread very well, use tcp_socket now. need to find out why
PooledDialer() :
tls_ctx(new_tls_context(nullptr, nullptr, nullptr)),
PooledDialer(TLSContext *_tls_ctx) :
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need to add a new constructor; simply add a default parameter to the existing constructor. The constructor is involved in the initialization of multiple members, and it is not advisable to maintain multiple versions.

tls_ctx(_tls_ctx ? _tls_ctx : new_tls_context(nullptr, nullptr, nullptr)),
tls_ctx_ownership(_tls_ctx == nullptr),
tcpsock(new_tcp_socket_pool(new_tcp_socket_client(), -1, true)),
tlssock(new_tcp_socket_pool(new_tls_client(tls_ctx, new_tcp_socket_client(), true), -1, true)),
resolver(new_default_resolver(kDNSCacheLife)) {
}

~PooledDialer() { delete tls_ctx; }
~PooledDialer() {
if (tls_ctx_ownership)
delete tls_ctx;
}

ISocketStream* dial(std::string_view host, uint16_t port, bool secure,
uint64_t timeout = -1UL);
Expand Down Expand Up @@ -107,8 +112,10 @@ class ClientImpl : public Client {
PooledDialer m_dialer;
CommonHeaders<> m_common_headers;
ICookieJar *m_cookie_jar;
ClientImpl(ICookieJar *cookie_jar) :
m_cookie_jar(cookie_jar) {}
ClientImpl(ICookieJar *cookie_jar, TLSContext *tls_ctx) :
Copy link
Collaborator

Choose a reason for hiding this comment

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

It is also possible to reuse the above constructor.

m_cookie_jar(cookie_jar),
m_dialer(tls_ctx) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It might be necessary to consider ownership of tls_ctx?

}

using SocketStream_ptr = std::unique_ptr<ISocketStream>;
int redirect(Operation* op) {
Expand Down Expand Up @@ -258,7 +265,9 @@ class ClientImpl : public Client {
}
};

Client* new_http_client(ICookieJar *cookie_jar) { return new ClientImpl(cookie_jar); }
Client* new_http_client(ICookieJar *cookie_jar, TLSContext *tls_ctx) {
return new ClientImpl(cookie_jar, tls_ctx);
}

} // namespace http
} // namespace net
Expand Down
3 changes: 2 additions & 1 deletion net/http/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ limitations under the License.

namespace photon {
namespace net {
class TLSContext;
namespace http {

class ICookieJar : public Object {
Expand Down Expand Up @@ -133,7 +134,7 @@ class Client : public Object {
};

//A Client without cookie_jar would ignore all response-header "Set-Cookies"
Client* new_http_client(ICookieJar *cookie_jar = nullptr);
Client* new_http_client(ICookieJar *cookie_jar = nullptr, TLSContext *tls_ctx = nullptr);

ICookieJar* new_simple_cookie_jar();

Expand Down
4 changes: 4 additions & 0 deletions net/http/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ add_test(NAME cookie_jar_test COMMAND $<TARGET_FILE:cookie_jar_test>)
add_executable(headers_test headers_test.cpp)
target_link_libraries(headers_test PRIVATE photon_shared ${testing_libs})
add_test(NAME headers_test COMMAND $<TARGET_FILE:headers_test>)

add_executable(client_tls_test client_tls_test.cpp)
target_link_libraries(client_tls_test PRIVATE photon_shared ${testing_libs})
add_test(NAME client_tls_test COMMAND $<TARGET_FILE:client_tls_test>)
89 changes: 89 additions & 0 deletions net/http/test/client_tls_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
Copyright 2022 The Photon Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include <gtest/gtest.h>

#include <photon/photon.h>
#include <photon/common/alog.h>
#include <photon/common/alog-stdstring.h>
#include <photon/thread/thread.h>
#include <photon/net/socket.h>
#include <photon/net/security-context/tls-stream.h>
#include <photon/net/http/message.h>
#include <photon/net/http/server.h>
#include <photon/net/http/client.h>

#include "../../test/cert-key.cpp"

using namespace photon;

int idiot_handler(void*, net::http::Request &req, net::http::Response &resp, std::string_view) {
std::string str;
auto r = req.headers.range();
auto cl = r.second - r.first + 1;
if (cl > 4096) {
LOG_ERROR_RETURN(0, -1, "RetType failed test");
}
resp.set_result(200);
resp.headers.content_length(cl);
resp.headers.insert("Test_Handle", "test");

str.resize(cl);
memset((void*)str.data(), '0', cl);
resp.write((void*)str.data(), str.size());
return 0;
}

TEST(client_tls, basic) {
auto ctx = net::new_tls_context(cert_str, key_str, passphrase_str);
DEFER(delete ctx);
auto tcpserver = net::new_tls_server(ctx, net::new_tcp_socket_server(), true);
DEFER(delete tcpserver);
tcpserver->timeout(1000UL*1000);
tcpserver->setsockopt(SOL_SOCKET, SO_REUSEPORT, 1);
tcpserver->bind(19876, net::IPAddr("127.0.0.1"));
tcpserver->listen();

auto server = net::http::new_http_server();
DEFER(delete server);
server->add_handler({nullptr, &idiot_handler});

tcpserver->set_handler(server->get_connection_handler());
tcpserver->start_loop();

auto client = net::http::new_http_client(nullptr, ctx);
DEFER(delete client);
auto op = client->new_operation(net::http::Verb::GET, "https://localhost:19876/test");
DEFER(delete op);
auto exp_len = 20;
op->req.headers.range(0, exp_len - 1);
op->call();
EXPECT_EQ(200, op->resp.status_code());
char buf[4096];
auto ret = op->resp.read(buf, 4096);
EXPECT_EQ(exp_len, ret);
EXPECT_EQ(true, "test" == op->resp.headers["Test_Handle"]);
}

int main(int argc, char** arg) {
if (photon::init(photon::INIT_EVENT_DEFAULT, photon::INIT_IO_NONE))
return -1;
DEFER(photon::fini());
set_log_output_level(ALOG_DEBUG);
::testing::InitGoogleTest(&argc, arg);
LOG_DEBUG("test result:`", RUN_ALL_TESTS());
return 0;
}