Add new type answer to hold status, headers and body of a request.
This commit is contained in:
parent
4a64bd5300
commit
63a14867c3
|
@ -10,6 +10,7 @@ project(curl_wrapper
|
|||
LANGUAGES CXX)
|
||||
|
||||
find_package(CURL 7.52 REQUIRED)
|
||||
find_package(Boost 1.54 REQUIRED COMPONENTS regex)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
|
|
|
@ -18,5 +18,7 @@ else()
|
|||
target_link_libraries(${PROJECT_NAME} PUBLIC ${CURL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Boost::regex)
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PUBLIC "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>")
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include "curl_wrapper.hpp"
|
||||
|
||||
#include "types.hpp"
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <atomic>
|
||||
|
@ -92,7 +94,7 @@ void CURLWrapper::set_useragent(string_view useragent)
|
|||
check(curl_easy_setopt(_connection, CURLOPT_USERAGENT, useragent.data()));
|
||||
}
|
||||
|
||||
string CURLWrapper::make_http_request(http_method method, string_view uri)
|
||||
answer CURLWrapper::make_http_request(http_method method, string_view uri)
|
||||
{
|
||||
_buffer_headers.clear();
|
||||
_buffer_body.clear();
|
||||
|
@ -171,10 +173,8 @@ string CURLWrapper::make_http_request(http_method method, string_view uri)
|
|||
long http_status{0}; // NOLINT(google-runtime-int)
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
|
||||
check(curl_easy_getinfo(_connection, CURLINFO_RESPONSE_CODE, &http_status));
|
||||
// status code is in http_status, headers are in _buffer_headers, body is in
|
||||
// _buffer_body.
|
||||
// TODO: Communicate the above.
|
||||
return _buffer_body;
|
||||
return {static_cast<std::uint16_t>(http_status), _buffer_headers,
|
||||
_buffer_body};
|
||||
}
|
||||
|
||||
size_t CURLWrapper::writer_body(char *data, size_t size, size_t nmemb)
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
#ifndef CURL_WRAPPER_HPP
|
||||
#define CURL_WRAPPER_HPP
|
||||
|
||||
#include "curl/curl.h"
|
||||
#include "types.hpp"
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
@ -29,16 +31,11 @@ namespace curl_wrapper
|
|||
using std::string;
|
||||
using std::string_view;
|
||||
|
||||
enum class http_method
|
||||
{
|
||||
DELETE,
|
||||
GET,
|
||||
HEAD,
|
||||
PATCH,
|
||||
POST,
|
||||
PUT
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Light wrapper atound libcurl.
|
||||
*
|
||||
* @since INSERT_VERSION
|
||||
*/
|
||||
class CURLWrapper
|
||||
{
|
||||
public:
|
||||
|
@ -69,8 +66,9 @@ public:
|
|||
/*!
|
||||
* @brief Cleans up curl and connection.
|
||||
*
|
||||
* May call `curl_global_cleanup`, which is not thread-safe. For more
|
||||
* information consult [curl_global_cleanup(3)]
|
||||
* Calls `curl_global_cleanup`, which is not thread-safe, when the last
|
||||
* instance of CURLWrapper is destroyed. For more information consult
|
||||
* [curl_global_cleanup(3)]
|
||||
* (https://curl.haxx.se/libcurl/c/curl_global_cleanup.html).
|
||||
*
|
||||
* @since INSERT_VERSION
|
||||
|
@ -92,7 +90,7 @@ public:
|
|||
*
|
||||
* @since INSERT_VERSION
|
||||
*/
|
||||
inline CURL *get_curl_easy_handle()
|
||||
[[nodiscard]] inline CURL *get_curl_easy_handle() const
|
||||
{
|
||||
return _connection;
|
||||
}
|
||||
|
@ -156,11 +154,11 @@ public:
|
|||
* @param method The HTTP method.
|
||||
* @param uri The full URI.
|
||||
*
|
||||
* @return The body of the page.
|
||||
* @return The status code, headers and body of the page.
|
||||
*
|
||||
* @since INSERT_VERSION
|
||||
*/
|
||||
[[nodiscard]] string make_http_request(http_method method, string_view uri);
|
||||
[[nodiscard]] answer make_http_request(http_method method, string_view uri);
|
||||
|
||||
private:
|
||||
CURL *_connection;
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/* This file is part of curl_wrapper.
|
||||
* Copyright © 2020 tastytea <tastytea@tastytea.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "types.hpp"
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace curl_wrapper
|
||||
{
|
||||
|
||||
std::string answer::get_header(const std::string_view field) const
|
||||
{
|
||||
const boost::regex re_field(string("^") + field.data()
|
||||
+ R"(:\s+([^\r\n]+))",
|
||||
boost::regex::icase);
|
||||
boost::cmatch match;
|
||||
|
||||
boost::regex_search(headers.c_str(), match, re_field);
|
||||
if (match[1].matched)
|
||||
{
|
||||
return match[1].str();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace curl_wrapper
|
|
@ -0,0 +1,105 @@
|
|||
/* This file is part of curl_wrapper.
|
||||
* Copyright © 2020 tastytea <tastytea@tastytea.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CURL_WRAPPER_TYPES_HPP
|
||||
#define CURL_WRAPPER_TYPES_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace curl_wrapper
|
||||
{
|
||||
|
||||
using std::ostream;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
|
||||
/*!
|
||||
* @brief The HTTP method.
|
||||
*
|
||||
* @since INSERT_VERSION
|
||||
*/
|
||||
enum class http_method
|
||||
{
|
||||
DELETE,
|
||||
GET,
|
||||
HEAD,
|
||||
PATCH,
|
||||
POST,
|
||||
PUT
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Return type for network requests.
|
||||
*
|
||||
* Currently only HTTP is considered.
|
||||
*
|
||||
* @since INSERT_VERSION
|
||||
*/
|
||||
struct answer
|
||||
{
|
||||
std::uint16_t status{0};
|
||||
string headers;
|
||||
string body;
|
||||
|
||||
/*!
|
||||
* @brief Returns true if #status is 200.
|
||||
*
|
||||
* @since INSERT_VERSION
|
||||
*/
|
||||
[[nodiscard]] inline explicit operator bool() const
|
||||
{
|
||||
return (status == 200);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns #body as const std::string_view.
|
||||
*
|
||||
* @since INSERT_VERSION
|
||||
*/
|
||||
[[nodiscard]] inline explicit operator string_view() const
|
||||
{
|
||||
return body;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns #body as std::ostream.
|
||||
*
|
||||
* @since INSERT_VERSION
|
||||
*/
|
||||
inline friend ostream &operator<<(ostream &out, const answer &answer)
|
||||
{
|
||||
out << answer.body;
|
||||
return out;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns the value of a header field.
|
||||
*
|
||||
* @param field Case insensitive, uses default locale.
|
||||
*
|
||||
* @return The value of the header field as std::string or {} if not found.
|
||||
*
|
||||
* @since INSERT_VERSION
|
||||
*/
|
||||
[[nodiscard]] string get_header(string_view field) const;
|
||||
};
|
||||
|
||||
} // namespace curl_wrapper
|
||||
|
||||
#endif // CURL_WRAPPER_TYPES_HPP
|
|
@ -22,7 +22,7 @@ SCENARIO("HTTP GET", "[http]")
|
|||
try
|
||||
{
|
||||
CURLWrapper curl;
|
||||
answer = curl.make_http_request(http_method::GET, uri);
|
||||
answer = curl.make_http_request(http_method::GET, uri).body;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
#include "types.hpp"
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
namespace curl_wrapper
|
||||
{
|
||||
|
||||
using std::string;
|
||||
|
||||
SCENARIO("Extract header")
|
||||
{
|
||||
answer ret;
|
||||
ret.headers = R"(HTTP/1.1 200 OK
|
||||
Server: nginx/1.18.0
|
||||
Date: Sat, 07 Nov 2020 22:26:13 GMT
|
||||
Content-Type: application/rss+xml; charset=utf-8
|
||||
Connection: keep-alive
|
||||
Keep-Alive: timeout=20
|
||||
Expires: Sat, 07 Nov 2020 22:56:13 GMT
|
||||
Cache-Control: max-age=1800
|
||||
X-Cache: HIT
|
||||
)";
|
||||
const string searchfor{"cache-control"};
|
||||
|
||||
bool exception = false;
|
||||
string value;
|
||||
|
||||
WHEN("We search for " + searchfor)
|
||||
{
|
||||
try
|
||||
{
|
||||
value = ret.get_header(searchfor);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
exception = true;
|
||||
}
|
||||
|
||||
THEN("No exception is thrown")
|
||||
AND_THEN("The value is successfully extracted")
|
||||
{
|
||||
REQUIRE_FALSE(exception);
|
||||
REQUIRE(value == "max-age=1800");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace curl_wrapper
|
Loading…
Reference in New Issue