Skip to content

Commit

Permalink
Http client (#522)
Browse files Browse the repository at this point in the history
* Ported http work over from smacc1

* Added example showasing http requests

* Fixed some typos in the HTTP example

* Made provision for HTTP GET/POST in the HTTP client

* Moved HTTP client into the client_libraries folder and made separate compilation units. Modified examples to use this new client

* Update smacc_action_client_base.hpp

Pretty sure this change was made accidently.

* Update smacc_action_client_base.hpp

Messed up first commit

* Update http_client.cpp

Added License to file to clean up format error

* Update http_client.cpp

Found better Copyright & License for CI Format error.

* Update cb_http_request.hpp

Another little format error

* Update sm_atomic_http.py

Somehow a bracket became a cNode

* - Fixed errors with HTTP requests not triggering,
- Fixed response callback not triggering correctly and causing a crash
- Fixed example not transitioning back to state 1 after a response has been received

* Ported http work over from smacc1

* Added example showasing http requests

* Fixed some typos in the HTTP example

* Made provision for HTTP GET/POST in the HTTP client

* Moved HTTP client into the client_libraries folder and made separate compilation units. Modified examples to use this new client

* Update http_client.cpp

Added License to file to clean up format error

* Update http_client.cpp

Found better Copyright & License for CI Format error.

* Update cb_http_request.hpp

Another little format error

* Update sm_atomic_http.py

Somehow a bracket became a cNode

* - Fixed errors with HTTP requests not triggering,
- Fixed response callback not triggering correctly and causing a crash
- Fixed example not transitioning back to state 1 after a response has been received

* Added helper class to handle server name related interactions

* Added SSL implementation, need to make provision for SSL and non-SSLD requests

* Added SLL certs

* Split the HTTp session class into own compile unit

* Added HTTP session base class and implemented the SSL version. The client can decide between which session to initialise at runtime depending on the server set

* Implemented non-ssl HTTP session for clients

* Removing hard-coded ssl certs and using the default OpenSSL certs for
verification

* Run through formatter

* Fix bad merge

* Remove noisy changelog

* Undo change that introduced and error

* Added missing copyrights

* Revert change to precommit config

* Fixing copyright, formatting

* Fixed whitespace blocking precommit

* Revert change to precommit config

---------

Co-authored-by: brettpac <[email protected]>
  • Loading branch information
yassiezar and brettpac committed Jun 28, 2024
1 parent 4c0b961 commit 682e73c
Show file tree
Hide file tree
Showing 24 changed files with 2,287 additions and 0 deletions.
50 changes: 50 additions & 0 deletions smacc2_client_library/http_client/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 3.5)
project(http_client)

# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(smacc2 REQUIRED)

include_directories(
include
${smacc2_INCLUDE_DIRS}
)

file(GLOB_RECURSE SRC_FILES src *.cpp)

add_library(http_session
src/http_client/ssl_http_session.cpp
src/http_client/http_session.cpp
)

add_library(${PROJECT_NAME}
src/http_client/http_client.cpp
)

target_link_libraries(${PROJECT_NAME}
http_session
${smacc2_LIBRARIES}
)
ament_target_dependencies(${PROJECT_NAME} smacc2)
ament_export_include_directories(include)
ament_export_libraries(${PROJECT_NAME} http_session)

install(
DIRECTORY include/
DESTINATION include)

install(TARGETS
${PROJECT_NAME}
http_session
DESTINATION lib/)

ament_package()
116 changes: 116 additions & 0 deletions smacc2_client_library/http_client/include/http_client/http_client.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2023 RobosoftAI Inc.
//
// 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.

/*****************************************************************************************************************
*
* Authors: Jaycee Lock
*
******************************************************************************************************************/

#pragma once

#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/version.hpp>
#include <http_client/http_session.hpp>
#include <http_client/ssl_http_session.hpp>
#include <iostream>
#include <memory>
#include <optional>
#include <smacc2/smacc.hpp>
#include <smacc2/smacc_client.hpp>
#include <string>
#include <thread>
#include <unordered_map>

namespace cl_http
{
class ClHttp : public smacc2::ISmaccClient
{
class Server
{
public:
explicit Server(const std::string & server_name) : server_name_{server_name}, ssl_{true}
{
if (!server_name_.substr(0, 7).compare("http://"))
{
ssl_ = false;
server_name_.erase(0, 7);
}
else if (!server_name_.substr(0, 8).compare("https://"))
{
server_name_.erase(0, 8);
ssl_ = true;
}
}

bool isSSL() const { return ssl_; }

std::string getPort() const { return ssl_ ? "443" : "80"; }

std::string getServerName() const { return server_name_; }

private:
std::string server_name_;
bool ssl_;
};

public:
enum class kHttpRequestMethod
{
GET = static_cast<int>(boost::beast::http::verb::get),
POST = static_cast<int>(boost::beast::http::verb::post),
PUT = static_cast<int>(boost::beast::http::verb::put),
};

using TResponse = http_session_base::TResponse;

template <typename T>
boost::signals2::connection onResponseReceived(void (T::*callback)(const TResponse &), T * object)
{
return this->getStateMachine()->createSignalConnection(onResponseReceived_, callback, object);
}

explicit ClHttp(const std::string & server, const int & timeout = 1500);

virtual ~ClHttp();

void onInitialize() override;

void makeRequest(const kHttpRequestMethod http_method, const std::string & path = "/");

private:
const int HTTP_VERSION = 11;

bool initialized_;
bool is_ssl_;
int timeout_;

Server server_;

boost::asio::io_context io_context_;
boost::asio::executor_work_guard<decltype(io_context_)::executor_type> worker_guard_;
std::thread tcp_connection_runner_;

boost::asio::ssl::context ssl_context_;

smacc2::SmaccSignal<void(const TResponse &)> onResponseReceived_;

std::function<void(TResponse)> callbackHandler = [&](const TResponse & res)
{ onResponseReceived_(res); };
};
} // namespace cl_http
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2021 RobosoftAI Inc.
//
// 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.

/*****************************************************************************************************************
*
Author: Jacobus Lock
******************************************************************************************************************/

#pragma once

#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <http_client/http_session_base.hpp>
#include <iostream>
#include <string>

namespace cl_http
{
class http_session : public std::enable_shared_from_this<http_session>, public http_session_base
{
public:
// Objects are constructed with a strand to
// ensure that handlers do not execute concurrently.
http_session(
boost::asio::any_io_executor ioc, const std::function<void(const TResponse &)> response);

virtual ~http_session() {}

// Start the asynchronous operation
void run(
const std::string & host, const std::string & target,
const boost::beast::http::verb http_method, const int & version) override;

std::string getPort() override { return kPort; }

private:
const std::string kPort = "80";

void on_resolve(
boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type results) override;
void fail(boost::beast::error_code ec, const char * what) override;
void on_connect(
boost::beast::error_code ec,
boost::asio::ip::tcp::resolver::results_type::endpoint_type) override;
void on_write(boost::beast::error_code ec, std::size_t bytes_transferred) override;
void on_read(boost::beast::error_code ec, std::size_t bytes_transferred) override;

std::function<void(const TResponse &)> onResponse;

boost::asio::ip::tcp::resolver resolver_;
boost::beast::tcp_stream stream_;
boost::beast::flat_buffer buffer_; // (Must persist between reads)
boost::beast::http::request<boost::beast::http::empty_body> req_;
boost::beast::http::response<boost::beast::http::string_body> res_;
};
} // namespace cl_http
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2021 RobosoftAI Inc.
//
// 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.

/*****************************************************************************************************************
*
Author: Jacobus Lock
******************************************************************************************************************/

#pragma once

#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/version.hpp>
#include <string>

namespace cl_http
{
class http_session_base
{
public:
virtual ~http_session_base() {}

using TResponse = boost::beast::http::response<boost::beast::http::string_body>;

// Start the asynchronous operation
virtual void run(
const std::string & host, const std::string & target,
const boost::beast::http::verb http_method, const int & version) = 0;

virtual std::string getPort() = 0;

protected:
virtual void on_resolve(
boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type results) = 0;
virtual void fail(boost::beast::error_code ec, const char * what) = 0;
virtual void on_connect(
boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type::endpoint_type) = 0;
virtual void on_write(boost::beast::error_code ec, std::size_t bytes_transferred) = 0;
virtual void on_read(boost::beast::error_code ec, std::size_t bytes_transferred) = 0;

// Optional, needed for SSL connections
virtual void on_handshake(boost::beast::error_code ec) {}
virtual void on_shutdown(boost::beast::error_code ec) {}
};
} // namespace cl_http
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2021 RobosoftAI Inc.
//
// 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.

/*****************************************************************************************************************
*
Author: Jacobus Lock
******************************************************************************************************************/

#pragma once

#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/version.hpp>
#include <http_client/http_session_base.hpp>
#include <iostream>
#include <string>

namespace cl_http
{
class ssl_http_session : public std::enable_shared_from_this<ssl_http_session>,
public http_session_base
{
public:
// Objects are constructed with a strand to
// ensure that handlers do not execute concurrently.
ssl_http_session(
boost::asio::any_io_executor ioc, boost::asio::ssl::context & ssl_context,
const std::function<void(const TResponse &)> response);

virtual ~ssl_http_session() {}

// Start the asynchronous operation
void run(
const std::string & host, const std::string & target,
const boost::beast::http::verb http_method, const int & version) override;

std::string getPort() override { return kPort; }

private:
const std::string kPort = "443";

void on_resolve(
boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type results) override;
void fail(boost::beast::error_code ec, const char * what) override;
void on_connect(
boost::beast::error_code ec,
boost::asio::ip::tcp::resolver::results_type::endpoint_type) override;
void on_handshake(boost::beast::error_code ec) override;
void on_write(boost::beast::error_code ec, std::size_t bytes_transferred) override;
void on_read(boost::beast::error_code ec, std::size_t bytes_transferred) override;
void on_shutdown(boost::beast::error_code ec) override;

std::function<void(const TResponse &)> onResponse;

boost::asio::ip::tcp::resolver resolver_;
// boost::beast::tcp_stream stream_;
boost::beast::ssl_stream<boost::beast::tcp_stream> stream_;
boost::beast::flat_buffer buffer_; // (Must persist between reads)
boost::beast::http::request<boost::beast::http::empty_body> req_;
boost::beast::http::response<boost::beast::http::string_body> res_;
};
} // namespace cl_http
17 changes: 17 additions & 0 deletions smacc2_client_library/http_client/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>http_client</name>
<version>2.3.18</version>
<description>The http_client package</description>
<maintainer email="[email protected]">Jaycee Lock</maintainer>
<license>Apache-2.0</license>

<buildtool_depend>ament_cmake</buildtool_depend>

<depend>smacc2</depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>
Loading

0 comments on commit 682e73c

Please sign in to comment.