Initial client API V2
This commit is contained in:
+16
-11
@@ -8,6 +8,7 @@ endif()
|
|||||||
|
|
||||||
project(webcc)
|
project(webcc)
|
||||||
|
|
||||||
|
option(WEBCC_ENABLE_REST "Enable REST support?" ON)
|
||||||
option(WEBCC_ENABLE_SOAP "Enable SOAP support (need pugixml)?" ON)
|
option(WEBCC_ENABLE_SOAP "Enable SOAP support (need pugixml)?" ON)
|
||||||
option(WEBCC_ENABLE_SSL "Enable SSL/HTTPS support (need OpenSSL)?" OFF)
|
option(WEBCC_ENABLE_SSL "Enable SSL/HTTPS support (need OpenSSL)?" OFF)
|
||||||
option(WEBCC_ENABLE_UNITTEST "Build unit test?" ON)
|
option(WEBCC_ENABLE_UNITTEST "Build unit test?" ON)
|
||||||
@@ -121,17 +122,18 @@ add_subdirectory(webcc)
|
|||||||
|
|
||||||
if(WEBCC_ENABLE_EXAMPLES)
|
if(WEBCC_ENABLE_EXAMPLES)
|
||||||
add_subdirectory(example/http_client)
|
add_subdirectory(example/http_client)
|
||||||
add_subdirectory(example/http_async_client)
|
# add_subdirectory(example/http_async_client)
|
||||||
|
|
||||||
# For including jsoncpp as "json/json.h".
|
if(WEBCC_ENABLE_REST)
|
||||||
include_directories(${THIRD_PARTY_DIR}/src/jsoncpp)
|
# For including jsoncpp as "json/json.h".
|
||||||
|
include_directories(${THIRD_PARTY_DIR}/src/jsoncpp)
|
||||||
|
# REST examples need jsoncpp to parse and create JSON.
|
||||||
|
add_subdirectory(${THIRD_PARTY_DIR}/src/jsoncpp)
|
||||||
|
|
||||||
# REST examples need jsoncpp to parse and create JSON.
|
add_subdirectory(example/rest_book_server)
|
||||||
add_subdirectory(${THIRD_PARTY_DIR}/src/jsoncpp)
|
# add_subdirectory(example/rest_book_client)
|
||||||
|
# add_subdirectory(example/rest_book_async_client)
|
||||||
add_subdirectory(example/rest_book_server)
|
endif()
|
||||||
add_subdirectory(example/rest_book_client)
|
|
||||||
add_subdirectory(example/rest_book_async_client)
|
|
||||||
|
|
||||||
if(WEBCC_ENABLE_SOAP)
|
if(WEBCC_ENABLE_SOAP)
|
||||||
add_subdirectory(example/soap_calc_server)
|
add_subdirectory(example/soap_calc_server)
|
||||||
@@ -143,8 +145,11 @@ if(WEBCC_ENABLE_EXAMPLES)
|
|||||||
|
|
||||||
if(WEBCC_ENABLE_SSL)
|
if(WEBCC_ENABLE_SSL)
|
||||||
add_subdirectory(example/http_ssl_client)
|
add_subdirectory(example/http_ssl_client)
|
||||||
add_subdirectory(example/http_ssl_async_client)
|
# add_subdirectory(example/http_ssl_async_client)
|
||||||
add_subdirectory(example/github_rest_client)
|
|
||||||
|
if(WEBCC_ENABLE_REST)
|
||||||
|
# add_subdirectory(example/github_rest_client)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
Language: Cpp
|
Language: Cpp
|
||||||
# BasedOnStyle: Google
|
# BasedOnStyle: Google
|
||||||
AccessModifierOffset: -1
|
AccessModifierOffset: -2
|
||||||
AlignAfterOpenBracket: Align
|
AlignAfterOpenBracket: Align
|
||||||
AlignConsecutiveAssignments: false
|
AlignConsecutiveAssignments: false
|
||||||
AlignConsecutiveDeclarations: false
|
AlignConsecutiveDeclarations: false
|
||||||
|
|||||||
+37
-17
@@ -1,29 +1,49 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "webcc/http_client.h"
|
#include "webcc/http_client_session.h" // TEST
|
||||||
#include "webcc/logger.h"
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
static void PrintError(const webcc::HttpClient& client) {
|
|
||||||
std::cout << webcc::DescribeError(client.error());
|
|
||||||
if (client.timed_out()) {
|
|
||||||
std::cout << " (timed out)";
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||||
|
|
||||||
auto request = webcc::HttpRequest::New(webcc::kHttpGet, "/get",
|
using namespace webcc;
|
||||||
"httpbin.org");
|
|
||||||
|
|
||||||
webcc::HttpClient client;
|
HttpResponsePtr r;
|
||||||
|
|
||||||
if (client.Request(*request)) {
|
HttpClientSession session;
|
||||||
std::cout << client.response_content() << std::endl;
|
|
||||||
} else {
|
#if 0
|
||||||
PrintError(client);
|
r = session.Request(HttpRequestArgs("GET")
|
||||||
}
|
.url("http://httpbin.org/get") // Moved
|
||||||
|
.parameters({ "key1", "value1", "key2", "value2" }) // Moved
|
||||||
|
.headers({ "Accept", "application/json" }) // Moved
|
||||||
|
.buffer_size(1000));
|
||||||
|
|
||||||
|
std::cout << r->content() << std::endl;
|
||||||
|
|
||||||
|
// If you want to create the args object firstly, there'll be an extra call
|
||||||
|
// to its move constructor.
|
||||||
|
// - constructor: HttpRequestArgs("GET")
|
||||||
|
// - move constructor: auto args = ...
|
||||||
|
auto args = HttpRequestArgs("GET")
|
||||||
|
.url("http://httpbin.org/get")
|
||||||
|
.parameters({ "key1", "value1", "key2", "value2" })
|
||||||
|
.headers({ "Accept", "application/json" })
|
||||||
|
.buffer_size(1000);
|
||||||
|
|
||||||
|
r = session.Request(std::move(args));
|
||||||
|
|
||||||
|
r = session.Get("http://httpbin.org/get",
|
||||||
|
{ "key1", "value1", "key2", "value2" },
|
||||||
|
{ "Accept", "application/json" },
|
||||||
|
HttpRequestArgs().buffer_size(1000));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
r = session.Post("http://httpbin.org/post", "{ 'key': 'value' }", true,
|
||||||
|
{ "Accept", "application/json" },
|
||||||
|
HttpRequestArgs().buffer_size(1000));
|
||||||
|
|
||||||
|
std::cout << r->content() << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,25 +4,21 @@
|
|||||||
#include "webcc/logger.h"
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
std::string host;
|
|
||||||
std::string url;
|
std::string url;
|
||||||
|
|
||||||
if (argc != 3) {
|
if (argc != 3) {
|
||||||
host = "www.boost.org";
|
url = "www.boost.org/LICENSE_1_0.txt";
|
||||||
url = "/LICENSE_1_0.txt";
|
|
||||||
} else {
|
} else {
|
||||||
host = argv[1];
|
url = argv[1];
|
||||||
url = argv[2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Host: " << host << std::endl;
|
|
||||||
std::cout << "URL: " << url << std::endl;
|
std::cout << "URL: " << url << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
|
||||||
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);
|
||||||
|
|
||||||
// Leave port to default value.
|
// Leave port to default value.
|
||||||
webcc::HttpRequest request(webcc::kHttpGet, url, host);
|
webcc::HttpRequest request(webcc::http::kGet, url);
|
||||||
|
|
||||||
request.Prepare();
|
request.Prepare();
|
||||||
|
|
||||||
@@ -30,7 +26,7 @@ int main(int argc, char* argv[]) {
|
|||||||
// See HttpSslClient::Request() for more details.
|
// See HttpSslClient::Request() for more details.
|
||||||
bool ssl_verify = false;
|
bool ssl_verify = false;
|
||||||
|
|
||||||
webcc::HttpSslClient client(2000, ssl_verify);
|
webcc::HttpSslClient client(ssl_verify, 2000);
|
||||||
|
|
||||||
if (client.Request(request)) {
|
if (client.Request(request)) {
|
||||||
//std::cout << client.response()->content() << std::endl;
|
//std::cout << client.response()->content() << std::endl;
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
# Unit test
|
# Unit test
|
||||||
set(UT_SRCS
|
set(UT_SRCS
|
||||||
rest_service_manager_test.cc
|
url_test.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(WEBCC_ENABLE_REST)
|
||||||
|
set(UT_SRCS ${UT_SRCS}
|
||||||
|
rest_service_manager_test.cc
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(UT_TARGET_NAME webcc_unittest)
|
set(UT_TARGET_NAME webcc_unittest)
|
||||||
|
|
||||||
add_executable(${UT_TARGET_NAME} ${UT_SRCS})
|
add_executable(${UT_TARGET_NAME} ${UT_SRCS})
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#include "webcc/rest_service_manager.h"
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "webcc/rest_service_manager.h"
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
class TestRestService : public webcc::RestService {
|
class TestRestService : public webcc::RestService {
|
||||||
public:
|
public:
|
||||||
void Handle(const webcc::RestRequest& request,
|
void Handle(const webcc::RestRequest& request,
|
||||||
@@ -9,6 +12,8 @@ class TestRestService : public webcc::RestService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
TEST(RestServiceManager, URL_RegexBasic) {
|
TEST(RestServiceManager, URL_RegexBasic) {
|
||||||
webcc::RestServiceManager service_manager;
|
webcc::RestServiceManager service_manager;
|
||||||
|
|
||||||
|
|||||||
+35
-31
@@ -15,27 +15,23 @@ include(GNUInstallDirs)
|
|||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
globals.h
|
globals.h
|
||||||
http_async_client_base.h
|
# http_async_client_base.h
|
||||||
http_async_client.h
|
# http_async_client.h
|
||||||
http_client_base.h
|
http_client_base.h
|
||||||
http_client.h
|
http_client.h
|
||||||
http_session.h
|
http_client_session.h
|
||||||
|
http_connection.h
|
||||||
http_message.h
|
http_message.h
|
||||||
http_parser.h
|
http_parser.h
|
||||||
http_request.h
|
http_request.h
|
||||||
|
http_request_args.h
|
||||||
http_request_handler.h
|
http_request_handler.h
|
||||||
http_request_parser.h
|
http_request_parser.h
|
||||||
http_response.h
|
http_response.h
|
||||||
http_response_parser.h
|
http_response_parser.h
|
||||||
http_server.h
|
http_server.h
|
||||||
http_ssl_async_client.h
|
# http_ssl_async_client.h
|
||||||
queue.h
|
queue.h
|
||||||
rest_async_client.h
|
|
||||||
rest_client.h
|
|
||||||
rest_request_handler.h
|
|
||||||
rest_server.h
|
|
||||||
rest_service.h
|
|
||||||
rest_service_manager.h
|
|
||||||
url.h
|
url.h
|
||||||
utility.h
|
utility.h
|
||||||
version.h
|
version.h
|
||||||
@@ -43,11 +39,12 @@ set(HEADERS
|
|||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
globals.cc
|
globals.cc
|
||||||
http_async_client_base.cc
|
# http_async_client_base.cc
|
||||||
http_async_client.cc
|
# http_async_client.cc
|
||||||
http_client_base.cc
|
http_client_base.cc
|
||||||
http_client.cc
|
http_client.cc
|
||||||
http_session.cc
|
http_client_session.cc
|
||||||
|
http_connection.cc
|
||||||
http_message.cc
|
http_message.cc
|
||||||
http_parser.cc
|
http_parser.cc
|
||||||
http_request.cc
|
http_request.cc
|
||||||
@@ -56,34 +53,41 @@ set(SOURCES
|
|||||||
http_response.cc
|
http_response.cc
|
||||||
http_response_parser.cc
|
http_response_parser.cc
|
||||||
http_server.cc
|
http_server.cc
|
||||||
http_ssl_async_client.cc
|
# http_ssl_async_client.cc
|
||||||
logger.cc
|
logger.cc
|
||||||
rest_async_client.cc
|
|
||||||
rest_client.cc
|
|
||||||
rest_request_handler.cc
|
|
||||||
rest_service_manager.cc
|
|
||||||
rest_service.cc
|
|
||||||
url.cc
|
url.cc
|
||||||
utility.cc
|
utility.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WEBCC_ENABLE_SSL)
|
if(WEBCC_ENABLE_REST)
|
||||||
set(HEADERS ${HEADERS}
|
set(REST_HEADERS
|
||||||
http_ssl_client.h
|
# rest_async_client.h
|
||||||
# rest_ssl_async_client.h
|
# rest_client.h
|
||||||
rest_ssl_client.h
|
rest_request_handler.h
|
||||||
|
rest_server.h
|
||||||
|
rest_service.h
|
||||||
|
rest_service_manager.h
|
||||||
|
)
|
||||||
|
set(REST_SOURCES
|
||||||
|
# rest_async_client.cc
|
||||||
|
# rest_client.cc
|
||||||
|
rest_request_handler.cc
|
||||||
|
rest_service_manager.cc
|
||||||
|
rest_service.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES ${SOURCES}
|
set(HEADERS ${HEADERS} ${REST_HEADERS})
|
||||||
http_ssl_client.cc
|
set(SOURCES ${SOURCES} ${REST_SOURCES})
|
||||||
# rest_ssl_async_client.cc
|
endif()
|
||||||
rest_ssl_client.cc
|
|
||||||
)
|
if(WEBCC_ENABLE_SSL)
|
||||||
|
set(HEADERS ${HEADERS} http_ssl_client.h)
|
||||||
|
set(SOURCES ${SOURCES} http_ssl_client.cc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WEBCC_ENABLE_SOAP)
|
if(WEBCC_ENABLE_SOAP)
|
||||||
set(SOAP_HEADERS
|
set(SOAP_HEADERS
|
||||||
soap_async_client.h
|
# soap_async_client.h
|
||||||
soap_client.h
|
soap_client.h
|
||||||
soap_globals.h
|
soap_globals.h
|
||||||
soap_message.h
|
soap_message.h
|
||||||
@@ -97,7 +101,7 @@ if(WEBCC_ENABLE_SOAP)
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(SOAP_SOURCES
|
set(SOAP_SOURCES
|
||||||
soap_async_client.cc
|
# soap_async_client.cc
|
||||||
soap_client.cc
|
soap_client.cc
|
||||||
soap_globals.cc
|
soap_globals.cc
|
||||||
soap_message.cc
|
soap_message.cc
|
||||||
|
|||||||
+2
-9
@@ -1,18 +1,11 @@
|
|||||||
#include "webcc/globals.h"
|
#include "webcc/globals.h"
|
||||||
|
|
||||||
|
#include "boost/algorithm/string.hpp"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
const std::string kHttpHead = "HEAD";
|
|
||||||
const std::string kHttpGet = "GET";
|
|
||||||
const std::string kHttpPost = "POST";
|
|
||||||
const std::string kHttpPatch = "PATCH";
|
|
||||||
const std::string kHttpPut = "PUT";
|
|
||||||
const std::string kHttpDelete = "DELETE";
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const char* DescribeError(Error error) {
|
const char* DescribeError(Error error) {
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case kHostResolveError:
|
case kHostResolveError:
|
||||||
|
|||||||
+44
-39
@@ -1,7 +1,9 @@
|
|||||||
#ifndef WEBCC_GLOBALS_H_
|
#ifndef WEBCC_GLOBALS_H_
|
||||||
#define WEBCC_GLOBALS_H_
|
#define WEBCC_GLOBALS_H_
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "webcc/version.h"
|
#include "webcc/version.h"
|
||||||
|
|
||||||
@@ -25,6 +27,9 @@
|
|||||||
TypeName(const TypeName&) = delete; \
|
TypeName(const TypeName&) = delete; \
|
||||||
TypeName& operator=(const TypeName&) = delete;
|
TypeName& operator=(const TypeName&) = delete;
|
||||||
|
|
||||||
|
// Default user agent.
|
||||||
|
#define USER_AGENT "Webcc/" WEBCC_VERSION
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -45,9 +50,47 @@ const int kMaxReadSeconds = 30;
|
|||||||
// when dumped/logged.
|
// when dumped/logged.
|
||||||
const std::size_t kMaxDumpSize = 2048;
|
const std::size_t kMaxDumpSize = 2048;
|
||||||
|
|
||||||
|
// Default ports.
|
||||||
|
const char* const kPort80 = "80";
|
||||||
|
const char* const kPort443 = "443";
|
||||||
|
|
||||||
|
// Client side error codes.
|
||||||
|
enum Error {
|
||||||
|
kNoError = 0, // i.e., OK
|
||||||
|
|
||||||
|
kHostResolveError,
|
||||||
|
kEndpointConnectError,
|
||||||
|
kHandshakeError, // HTTPS handshake
|
||||||
|
kSocketReadError,
|
||||||
|
kSocketWriteError,
|
||||||
|
|
||||||
|
// HTTP error.
|
||||||
|
// E.g., failed to parse HTTP response (invalid content length, etc.).
|
||||||
|
kHttpError,
|
||||||
|
|
||||||
|
// Server error.
|
||||||
|
// E.g., HTTP status 500 + SOAP Fault element.
|
||||||
|
kServerError,
|
||||||
|
|
||||||
|
// XML parsing error.
|
||||||
|
kXmlError,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return a descriptive message for the given error code.
|
||||||
|
const char* DescribeError(Error error);
|
||||||
|
|
||||||
// HTTP headers.
|
// HTTP headers.
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
|
// HTTP methods (verbs) in string.
|
||||||
|
// Don't use enum to avoid converting back and forth.
|
||||||
|
const char* const kHead = "HEAD";
|
||||||
|
const char* const kGet = "GET";
|
||||||
|
const char* const kPost = "POST";
|
||||||
|
const char* const kPatch = "PATCH";
|
||||||
|
const char* const kPut = "PUT";
|
||||||
|
const char* const kDelete = "DELETE";
|
||||||
|
|
||||||
// HTTP response status.
|
// HTTP response status.
|
||||||
// This is not a full list.
|
// This is not a full list.
|
||||||
// Full list: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
// Full list: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
||||||
@@ -94,6 +137,7 @@ const char* const kTextXml = "text/xml";
|
|||||||
|
|
||||||
} // namespace media_types
|
} // namespace media_types
|
||||||
|
|
||||||
|
// TODO: Rename to encodings?
|
||||||
namespace charsets {
|
namespace charsets {
|
||||||
|
|
||||||
const char* const kUtf8 = "utf-8";
|
const char* const kUtf8 = "utf-8";
|
||||||
@@ -102,45 +146,6 @@ const char* const kUtf8 = "utf-8";
|
|||||||
|
|
||||||
} // namespace http
|
} // namespace http
|
||||||
|
|
||||||
// Default ports.
|
|
||||||
const char* const kHttpPort = "80";
|
|
||||||
const char* const kHttpSslPort = "443";
|
|
||||||
|
|
||||||
// HTTP methods (verbs) in string ("HEAD", "GET", etc.).
|
|
||||||
// NOTE: Don't use enum to avoid converting back and forth.
|
|
||||||
// TODO: Add enum.
|
|
||||||
extern const std::string kHttpHead;
|
|
||||||
extern const std::string kHttpGet;
|
|
||||||
extern const std::string kHttpPost;
|
|
||||||
extern const std::string kHttpPatch;
|
|
||||||
extern const std::string kHttpPut;
|
|
||||||
extern const std::string kHttpDelete;
|
|
||||||
|
|
||||||
// Client side error codes.
|
|
||||||
enum Error {
|
|
||||||
kNoError = 0, // i.e., OK
|
|
||||||
|
|
||||||
kHostResolveError,
|
|
||||||
kEndpointConnectError,
|
|
||||||
kHandshakeError, // HTTPS handshake
|
|
||||||
kSocketReadError,
|
|
||||||
kSocketWriteError,
|
|
||||||
|
|
||||||
// HTTP error.
|
|
||||||
// E.g., failed to parse HTTP response (invalid content length, etc.).
|
|
||||||
kHttpError,
|
|
||||||
|
|
||||||
// Server error.
|
|
||||||
// E.g., HTTP status 500 + SOAP Fault element.
|
|
||||||
kServerError,
|
|
||||||
|
|
||||||
// XML parsing error.
|
|
||||||
kXmlError,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return a descriptive message for the given error code.
|
|
||||||
const char* DescribeError(Error error);
|
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|
||||||
#endif // WEBCC_GLOBALS_H_
|
#endif // WEBCC_GLOBALS_H_
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ class HttpClient : public HttpClientBase {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Error Connect(const HttpRequest& request) final {
|
Error Connect(const HttpRequest& request) final {
|
||||||
return DoConnect(request, kHttpPort);
|
return DoConnect(request, kPort80);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketConnect(const Endpoints& endpoints,
|
void SocketConnect(const Endpoints& endpoints,
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
#include "webcc/http_client_session.h"
|
||||||
|
|
||||||
|
#include "webcc/http_client.h"
|
||||||
|
#include "webcc/http_request.h"
|
||||||
|
#include "webcc/url.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
HttpClientSession::HttpClientSession() {
|
||||||
|
InitHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponsePtr HttpClientSession::Request(HttpRequestArgs&& args) {
|
||||||
|
assert(args.parameters_.size() % 2 == 0);
|
||||||
|
assert(args.headers_.size() % 2 == 0);
|
||||||
|
|
||||||
|
HttpRequest request(args.method_, args.url_, args.parameters_);
|
||||||
|
|
||||||
|
if (!args.data_.empty()) {
|
||||||
|
request.SetContent(std::move(args.data_), true);
|
||||||
|
|
||||||
|
// TODO: charset/encoding
|
||||||
|
if (args.json_) {
|
||||||
|
request.SetContentType(http::media_types::kApplicationJson, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the session-level headers.
|
||||||
|
for (const HttpHeader& h : headers_.data()) {
|
||||||
|
request.SetHeader(h.first, h.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the request-level headers.
|
||||||
|
// This will overwrite the session-level headers.
|
||||||
|
for (std::size_t i = 1; i < args.headers_.size(); i += 2) {
|
||||||
|
request.SetHeader(std::move(args.headers_[i - 1]),
|
||||||
|
std::move(args.headers_[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Prepare();
|
||||||
|
|
||||||
|
HttpClient client;
|
||||||
|
if (!client.Request(request, args.buffer_size_)) {
|
||||||
|
return HttpResponsePtr{};
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.response();
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponsePtr HttpClientSession::Get(const std::string& url,
|
||||||
|
std::vector<std::string>&& parameters,
|
||||||
|
std::vector<std::string>&& headers,
|
||||||
|
HttpRequestArgs&& args) {
|
||||||
|
return Request(args.method(http::kGet).url(url)
|
||||||
|
.parameters(std::move(parameters))
|
||||||
|
.headers(std::move(headers)));
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponsePtr HttpClientSession::Post(const std::string& url,
|
||||||
|
std::string&& data, bool json,
|
||||||
|
std::vector<std::string>&& headers,
|
||||||
|
HttpRequestArgs&& args) {
|
||||||
|
return Request(args.method(http::kPost).url(url).data(std::move(data))
|
||||||
|
.json(json).headers(std::move(headers)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpClientSession::InitHeaders() {
|
||||||
|
// NOTE: C++11 requires a space between literal and string macro.
|
||||||
|
headers_.Add(http::headers::kUserAgent, USER_AGENT);
|
||||||
|
|
||||||
|
// TODO: Support gzip, deflate
|
||||||
|
headers_.Add(http::headers::kAcceptEncoding, "identity");
|
||||||
|
|
||||||
|
headers_.Add(http::headers::kAccept, "*/*");
|
||||||
|
|
||||||
|
// TODO: Support Keep-Alive connection.
|
||||||
|
//headers_.Add(http::headers::kConnection, "close");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef WEBCC_HTTP_CLIENT_SESSION_H_
|
||||||
|
#define WEBCC_HTTP_CLIENT_SESSION_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webcc/http_request_args.h"
|
||||||
|
#include "webcc/http_response.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class HttpClientSession {
|
||||||
|
public:
|
||||||
|
HttpClientSession();
|
||||||
|
|
||||||
|
~HttpClientSession() = default;
|
||||||
|
|
||||||
|
void AddHeader(const std::string& key, const std::string& value) {
|
||||||
|
headers_.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponsePtr Request(HttpRequestArgs&& args);
|
||||||
|
|
||||||
|
HttpResponsePtr Get(const std::string& url,
|
||||||
|
std::vector<std::string>&& parameters = {},
|
||||||
|
std::vector<std::string>&& headers = {},
|
||||||
|
HttpRequestArgs&& args = HttpRequestArgs());
|
||||||
|
|
||||||
|
HttpResponsePtr Post(const std::string& url,
|
||||||
|
std::string&& data, bool json,
|
||||||
|
std::vector<std::string>&& headers = {},
|
||||||
|
HttpRequestArgs&& args = HttpRequestArgs());
|
||||||
|
|
||||||
|
private:
|
||||||
|
void InitHeaders();
|
||||||
|
|
||||||
|
// Headers to be sent on each request sent from this session.
|
||||||
|
HttpHeaderDict headers_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_HTTP_CLIENT_SESSION_H_
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "webcc/http_session.h"
|
#include "webcc/http_connection.h"
|
||||||
|
|
||||||
#include <utility> // for move()
|
#include <utility> // for move()
|
||||||
|
|
||||||
@@ -11,18 +11,18 @@ using boost::asio::ip::tcp;
|
|||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
HttpSession::HttpSession(tcp::socket socket, HttpRequestHandler* handler)
|
HttpConnection::HttpConnection(tcp::socket socket, HttpRequestHandler* handler)
|
||||||
: socket_(std::move(socket)),
|
: socket_(std::move(socket)),
|
||||||
buffer_(kBufferSize),
|
buffer_(kBufferSize),
|
||||||
request_handler_(handler),
|
request_handler_(handler),
|
||||||
request_parser_(&request_) {
|
request_parser_(&request_) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::Start() {
|
void HttpConnection::Start() {
|
||||||
DoRead();
|
DoRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::Close() {
|
void HttpConnection::Close() {
|
||||||
LOG_INFO("Close socket...");
|
LOG_INFO("Close socket...");
|
||||||
|
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
@@ -32,27 +32,27 @@ void HttpSession::Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::SetResponseContent(std::string&& content,
|
void HttpConnection::SetResponseContent(std::string&& content,
|
||||||
const std::string& media_type,
|
const std::string& media_type,
|
||||||
const std::string& charset) {
|
const std::string& charset) {
|
||||||
response_.SetContent(std::move(content), true);
|
response_.SetContent(std::move(content), true);
|
||||||
response_.SetContentType(media_type, charset);
|
response_.SetContentType(media_type, charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::SendResponse(http::Status status) {
|
void HttpConnection::SendResponse(http::Status status) {
|
||||||
response_.set_status(status);
|
response_.set_status(status);
|
||||||
response_.Prepare();
|
response_.Prepare();
|
||||||
DoWrite();
|
DoWrite();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::DoRead() {
|
void HttpConnection::DoRead() {
|
||||||
socket_.async_read_some(boost::asio::buffer(buffer_),
|
socket_.async_read_some(boost::asio::buffer(buffer_),
|
||||||
std::bind(&HttpSession::OnRead, shared_from_this(),
|
std::bind(&HttpConnection::OnRead, shared_from_this(),
|
||||||
std::placeholders::_1,
|
std::placeholders::_1,
|
||||||
std::placeholders::_2));
|
std::placeholders::_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::OnRead(boost::system::error_code ec, std::size_t length) {
|
void HttpConnection::OnRead(boost::system::error_code ec, std::size_t length) {
|
||||||
if (ec) {
|
if (ec) {
|
||||||
LOG_ERRO("Socket read error (%s).", ec.message().c_str());
|
LOG_ERRO("Socket read error (%s).", ec.message().c_str());
|
||||||
if (ec != boost::asio::error::operation_aborted) {
|
if (ec != boost::asio::error::operation_aborted) {
|
||||||
@@ -77,16 +77,16 @@ void HttpSession::OnRead(boost::system::error_code ec, std::size_t length) {
|
|||||||
|
|
||||||
LOG_VERB("HTTP request:\n%s", request_.Dump(4, "> ").c_str());
|
LOG_VERB("HTTP request:\n%s", request_.Dump(4, "> ").c_str());
|
||||||
|
|
||||||
// Enqueue this session.
|
// Enqueue this connection.
|
||||||
// Some worker thread will handle it later.
|
// Some worker thread will handle it later.
|
||||||
request_handler_->Enqueue(shared_from_this());
|
request_handler_->Enqueue(shared_from_this());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpSession::DoWrite() {
|
void HttpConnection::DoWrite() {
|
||||||
LOG_VERB("HTTP response:\n%s", response_.Dump(4, "> ").c_str());
|
LOG_VERB("HTTP response:\n%s", response_.Dump(4, "> ").c_str());
|
||||||
|
|
||||||
boost::asio::async_write(socket_, response_.ToBuffers(),
|
boost::asio::async_write(socket_, response_.ToBuffers(),
|
||||||
std::bind(&HttpSession::OnWrite, shared_from_this(),
|
std::bind(&HttpConnection::OnWrite, shared_from_this(),
|
||||||
std::placeholders::_1,
|
std::placeholders::_1,
|
||||||
std::placeholders::_2));
|
std::placeholders::_2));
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ void HttpSession::DoWrite() {
|
|||||||
// This write handler will be called from main thread (the thread calling
|
// This write handler will be called from main thread (the thread calling
|
||||||
// io_context.run), even though AsyncWrite() is invoked by worker threads.
|
// io_context.run), even though AsyncWrite() is invoked by worker threads.
|
||||||
// This is ensured by Asio.
|
// This is ensured by Asio.
|
||||||
void HttpSession::OnWrite(boost::system::error_code ec, std::size_t length) {
|
void HttpConnection::OnWrite(boost::system::error_code ec, std::size_t length) {
|
||||||
if (ec) {
|
if (ec) {
|
||||||
LOG_ERRO("Socket write error (%s).", ec.message().c_str());
|
LOG_ERRO("Socket write error (%s).", ec.message().c_str());
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ void HttpSession::OnWrite(boost::system::error_code ec, std::size_t length) {
|
|||||||
|
|
||||||
// Socket close VS. shutdown:
|
// Socket close VS. shutdown:
|
||||||
// https://stackoverflow.com/questions/4160347/close-vs-shutdown-socket
|
// https://stackoverflow.com/questions/4160347/close-vs-shutdown-socket
|
||||||
void HttpSession::Shutdown() {
|
void HttpConnection::Shutdown() {
|
||||||
LOG_INFO("Shutdown socket...");
|
LOG_INFO("Shutdown socket...");
|
||||||
|
|
||||||
// Initiate graceful connection closure.
|
// Initiate graceful connection closure.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef WEBCC_HTTP_SESSION_H_
|
#ifndef WEBCC_HTTP_CONNECTION_H_
|
||||||
#define WEBCC_HTTP_SESSION_H_
|
#define WEBCC_HTTP_CONNECTION_H_
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -14,18 +14,23 @@
|
|||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
class HttpConnection;
|
||||||
class HttpRequestHandler;
|
class HttpRequestHandler;
|
||||||
|
|
||||||
class HttpSession : public std::enable_shared_from_this<HttpSession> {
|
typedef std::shared_ptr<HttpConnection> HttpConnectionPtr;
|
||||||
public:
|
|
||||||
HttpSession(boost::asio::ip::tcp::socket socket,
|
|
||||||
HttpRequestHandler* handler);
|
|
||||||
|
|
||||||
~HttpSession() = default;
|
class HttpConnection : public std::enable_shared_from_this<HttpConnection> {
|
||||||
|
public:
|
||||||
|
HttpConnection(boost::asio::ip::tcp::socket socket,
|
||||||
|
HttpRequestHandler* handler);
|
||||||
|
|
||||||
WEBCC_DELETE_COPY_ASSIGN(HttpSession);
|
~HttpConnection() = default;
|
||||||
|
|
||||||
const HttpRequest& request() const { return request_; }
|
WEBCC_DELETE_COPY_ASSIGN(HttpConnection);
|
||||||
|
|
||||||
|
const HttpRequest& request() const {
|
||||||
|
return request_;
|
||||||
|
}
|
||||||
|
|
||||||
// Start to read and process the client request.
|
// Start to read and process the client request.
|
||||||
void Start();
|
void Start();
|
||||||
@@ -40,7 +45,7 @@ class HttpSession : public std::enable_shared_from_this<HttpSession> {
|
|||||||
// Send response to client with the given status.
|
// Send response to client with the given status.
|
||||||
void SendResponse(http::Status status);
|
void SendResponse(http::Status status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DoRead();
|
void DoRead();
|
||||||
void OnRead(boost::system::error_code ec, std::size_t length);
|
void OnRead(boost::system::error_code ec, std::size_t length);
|
||||||
|
|
||||||
@@ -69,8 +74,6 @@ class HttpSession : public std::enable_shared_from_this<HttpSession> {
|
|||||||
HttpResponse response_;
|
HttpResponse response_;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<HttpSession> HttpSessionPtr;
|
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|
||||||
#endif // WEBCC_HTTP_SESSION_H_
|
#endif // WEBCC_HTTP_CONNECTION_H_
|
||||||
+37
-27
@@ -17,38 +17,53 @@ const char CRLF[] = { '\r', '\n' };
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
void HttpMessage::SetHeader(const std::string& name, const std::string& value) {
|
std::ostream& operator<<(std::ostream& os, const HttpMessage& message) {
|
||||||
|
message.Dump(os);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void HttpHeaderDict::Add(const std::string& key, const std::string& value) {
|
||||||
for (HttpHeader& h : headers_) {
|
for (HttpHeader& h : headers_) {
|
||||||
if (boost::iequals(h.name, name)) {
|
if (boost::iequals(h.first, key)) {
|
||||||
h.value = value;
|
h.second = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
headers_.push_back({ name, value });
|
headers_.push_back({ key, value });
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpMessage::SetHeader(std::string&& name, std::string&& value) {
|
void HttpHeaderDict::Add(std::string&& key, std::string&& value) {
|
||||||
for (HttpHeader& h : headers_) {
|
for (HttpHeader& h : headers_) {
|
||||||
if (boost::iequals(h.name, name)) {
|
if (boost::iequals(h.first, key)) {
|
||||||
h.value = std::move(value);
|
h.second = std::move(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
headers_.push_back({ std::move(name), std::move(value) });
|
headers_.push_back({ std::move(key), std::move(value) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE:
|
bool HttpHeaderDict::Has(const std::string& key) const {
|
||||||
// According to HTTP 1.1 RFC7231, the following examples are all equivalent,
|
for (const HttpHeader& h : headers_) {
|
||||||
// but the first is preferred for consistency:
|
if (boost::iequals(h.first, key)) {
|
||||||
// text/html;charset=utf-8
|
return true;
|
||||||
// text/html;charset=UTF-8
|
}
|
||||||
// Text/HTML;Charset="utf-8"
|
}
|
||||||
// text/html; charset="utf-8"
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// See: https://tools.ietf.org/html/rfc7231#section-3.1.1.1
|
// See: https://tools.ietf.org/html/rfc7231#section-3.1.1.1
|
||||||
void HttpMessage::SetContentType(const std::string& media_type,
|
void HttpMessage::SetContentType(const std::string& media_type,
|
||||||
const std::string& charset) {
|
const std::string& charset) {
|
||||||
SetHeader(http::headers::kContentType,
|
if (charset.empty()) {
|
||||||
media_type + ";charset=" + charset);
|
SetHeader(http::headers::kContentType, media_type);
|
||||||
|
} else {
|
||||||
|
SetHeader(http::headers::kContentType,
|
||||||
|
media_type + ";charset=" + charset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpMessage::SetContent(std::string&& content, bool set_length) {
|
void HttpMessage::SetContent(std::string&& content, bool set_length) {
|
||||||
@@ -72,10 +87,10 @@ std::vector<boost::asio::const_buffer> HttpMessage::ToBuffers() const {
|
|||||||
|
|
||||||
buffers.push_back(boost::asio::buffer(start_line_));
|
buffers.push_back(boost::asio::buffer(start_line_));
|
||||||
|
|
||||||
for (const HttpHeader& h : headers_) {
|
for (const HttpHeader& h : headers_.data()) {
|
||||||
buffers.push_back(boost::asio::buffer(h.name));
|
buffers.push_back(boost::asio::buffer(h.first));
|
||||||
buffers.push_back(boost::asio::buffer(misc_strings::NAME_VALUE_SEPARATOR));
|
buffers.push_back(boost::asio::buffer(misc_strings::NAME_VALUE_SEPARATOR));
|
||||||
buffers.push_back(boost::asio::buffer(h.value));
|
buffers.push_back(boost::asio::buffer(h.second));
|
||||||
buffers.push_back(boost::asio::buffer(misc_strings::CRLF));
|
buffers.push_back(boost::asio::buffer(misc_strings::CRLF));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,8 +113,8 @@ void HttpMessage::Dump(std::ostream& os, std::size_t indent,
|
|||||||
|
|
||||||
os << indent_str << start_line_;
|
os << indent_str << start_line_;
|
||||||
|
|
||||||
for (const HttpHeader& h : headers_) {
|
for (const HttpHeader& h : headers_.data()) {
|
||||||
os << indent_str << h.name << ": " << h.value << std::endl;
|
os << indent_str << h.first << ": " << h.second << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
os << indent_str << std::endl;
|
os << indent_str << std::endl;
|
||||||
@@ -144,9 +159,4 @@ std::string HttpMessage::Dump(std::size_t indent,
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const HttpMessage& message) {
|
|
||||||
message.Dump(os);
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
+57
-18
@@ -12,35 +12,76 @@
|
|||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
struct HttpHeader {
|
// -----------------------------------------------------------------------------
|
||||||
std::string name;
|
|
||||||
std::string value;
|
typedef std::pair<std::string, std::string> HttpHeader;
|
||||||
|
|
||||||
|
class HttpMessage;
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const HttpMessage& message);
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HttpHeaderDict {
|
||||||
|
public:
|
||||||
|
std::size_t size() const {
|
||||||
|
return headers_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Add(const std::string& key, const std::string& value);
|
||||||
|
|
||||||
|
void Add(std::string&& key, std::string&& value);
|
||||||
|
|
||||||
|
bool Has(const std::string& key) const;
|
||||||
|
|
||||||
|
const HttpHeader& Get(std::size_t i) const {
|
||||||
|
assert(i < size());
|
||||||
|
return headers_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<HttpHeader>& data() const {
|
||||||
|
return headers_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<HttpHeader> headers_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
// Base class for HTTP request and response messages.
|
// Base class for HTTP request and response messages.
|
||||||
class HttpMessage {
|
class HttpMessage {
|
||||||
public:
|
public:
|
||||||
HttpMessage() : content_length_(kInvalidLength) {}
|
HttpMessage() : content_length_(kInvalidLength) {
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~HttpMessage() = default;
|
virtual ~HttpMessage() = default;
|
||||||
|
|
||||||
const std::string& start_line() const { return start_line_; }
|
const std::string& start_line() const {
|
||||||
|
return start_line_;
|
||||||
|
}
|
||||||
|
|
||||||
void set_start_line(const std::string& start_line) {
|
void set_start_line(const std::string& start_line) {
|
||||||
start_line_ = start_line;
|
start_line_ = start_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t content_length() const { return content_length_; }
|
std::size_t content_length() const {
|
||||||
|
return content_length_;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& content() const { return content_; }
|
const std::string& content() const {
|
||||||
|
return content_;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Rename to AddHeader.
|
void SetHeader(const std::string& key, const std::string& value) {
|
||||||
void SetHeader(const std::string& name, const std::string& value);
|
headers_.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Remove
|
void SetHeader(std::string&& key, std::string&& value) {
|
||||||
void SetHeader(std::string&& name, std::string&& value);
|
headers_.Add(std::move(key), std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
// E.g., "application/json; charset=utf-8"
|
// E.g., "text/html", "application/json; charset=utf-8", etc.
|
||||||
void SetContentType(const std::string& media_type,
|
void SetContentType(const std::string& media_type,
|
||||||
const std::string& charset);
|
const std::string& charset);
|
||||||
|
|
||||||
@@ -51,7 +92,7 @@ class HttpMessage {
|
|||||||
|
|
||||||
// Make the message (e.g., update start line).
|
// Make the message (e.g., update start line).
|
||||||
// Must be called before ToBuffers()!
|
// Must be called before ToBuffers()!
|
||||||
virtual void Prepare() = 0;
|
virtual bool Prepare() = 0;
|
||||||
|
|
||||||
// Convert the message into a vector of buffers. The buffers do not own the
|
// Convert the message into a vector of buffers. The buffers do not own the
|
||||||
// underlying memory blocks, therefore the message object must remain valid
|
// underlying memory blocks, therefore the message object must remain valid
|
||||||
@@ -66,7 +107,7 @@ class HttpMessage {
|
|||||||
std::string Dump(std::size_t indent = 0,
|
std::string Dump(std::size_t indent = 0,
|
||||||
const std::string& prefix = "") const;
|
const std::string& prefix = "") const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SetContentLength(std::size_t content_length) {
|
void SetContentLength(std::size_t content_length) {
|
||||||
content_length_ = content_length;
|
content_length_ = content_length;
|
||||||
SetHeader(http::headers::kContentLength, std::to_string(content_length));
|
SetHeader(http::headers::kContentLength, std::to_string(content_length));
|
||||||
@@ -77,13 +118,11 @@ class HttpMessage {
|
|||||||
|
|
||||||
std::size_t content_length_;
|
std::size_t content_length_;
|
||||||
|
|
||||||
std::vector<HttpHeader> headers_;
|
HttpHeaderDict headers_;
|
||||||
|
|
||||||
std::string content_;
|
std::string content_;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const HttpMessage& message);
|
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|
||||||
#endif // WEBCC_HTTP_MESSAGE_H_
|
#endif // WEBCC_HTTP_MESSAGE_H_
|
||||||
|
|||||||
+27
-22
@@ -1,46 +1,51 @@
|
|||||||
#include "webcc/http_request.h"
|
#include "webcc/http_request.h"
|
||||||
|
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
HttpRequest::HttpRequest(const std::string& method,
|
HttpRequest::HttpRequest(const std::string& method,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
const std::string& host,
|
const std::vector<std::string>& parameters)
|
||||||
const std::string& port)
|
: method_(method), url_(url) {
|
||||||
: method_(method), url_(url), host_(host), port_(port) {
|
assert(parameters.size() % 2 == 0);
|
||||||
|
for (std::size_t i = 1; i < parameters.size(); i += 2) {
|
||||||
|
url_.AddParameter(parameters[i - 1], parameters[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpRequest::Prepare() {
|
bool HttpRequest::Prepare() {
|
||||||
|
if (url_.host().empty()) {
|
||||||
|
LOG_ERRO("Invalid request: host is missing.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string target = "/" + url_.path();
|
||||||
|
if (!url_.query().empty()) {
|
||||||
|
target += "?";
|
||||||
|
target += url_.query();
|
||||||
|
}
|
||||||
|
|
||||||
start_line_ = method_;
|
start_line_ = method_;
|
||||||
start_line_ += " ";
|
start_line_ += " ";
|
||||||
start_line_ += url_;
|
start_line_ += target;
|
||||||
start_line_ += " HTTP/1.1";
|
start_line_ += " HTTP/1.1";
|
||||||
start_line_ += CRLF;
|
start_line_ += CRLF;
|
||||||
|
|
||||||
if (port_.empty()) {
|
if (url_.port().empty()) {
|
||||||
SetHeader(http::headers::kHost, host_);
|
SetHeader(http::headers::kHost, url_.host());
|
||||||
} else {
|
} else {
|
||||||
SetHeader(http::headers::kHost, host_ + ":" + port_);
|
SetHeader(http::headers::kHost, url_.host() + ":" + url_.port());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support Keep-Alive connection.
|
return true;
|
||||||
//SetHeader(http::headers::kConnection, "close");
|
|
||||||
|
|
||||||
// TODO: Support gzip, deflate
|
|
||||||
SetHeader(http::headers::kAcceptEncoding, "identity");
|
|
||||||
|
|
||||||
// NOTE: C++11 requires a space between literal and string macro.
|
|
||||||
SetHeader(http::headers::kUserAgent, "Webcc/" WEBCC_VERSION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
HttpRequestPtr HttpRequest::New(const std::string& method,
|
HttpRequestPtr HttpRequest::New(const std::string& method,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
const std::string& host,
|
const std::vector<std::string>& parameters,
|
||||||
const std::string& port,
|
|
||||||
bool prepare) {
|
bool prepare) {
|
||||||
HttpRequestPtr request{
|
HttpRequestPtr request{ new HttpRequest{ method, url, parameters } };
|
||||||
new HttpRequest{ method, url, host, port }
|
|
||||||
};
|
|
||||||
|
|
||||||
if (prepare) {
|
if (prepare) {
|
||||||
request->Prepare();
|
request->Prepare();
|
||||||
|
|||||||
+30
-27
@@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "webcc/http_message.h"
|
#include "webcc/http_message.h"
|
||||||
|
#include "webcc/url.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
@@ -14,29 +16,34 @@ class HttpRequestParser;
|
|||||||
typedef std::shared_ptr<HttpRequest> HttpRequestPtr;
|
typedef std::shared_ptr<HttpRequest> HttpRequestPtr;
|
||||||
|
|
||||||
class HttpRequest : public HttpMessage {
|
class HttpRequest : public HttpMessage {
|
||||||
public:
|
public:
|
||||||
HttpRequest() = default;
|
HttpRequest() = default;
|
||||||
|
|
||||||
// The |host| is a descriptive name (e.g., www.google.com) or a numeric IP
|
// TODO: Move parameters
|
||||||
// address (127.0.0.1).
|
|
||||||
// The |port| is a numeric number (e.g., 9000). The default value (80 for HTTP
|
|
||||||
// or 443 for HTTPS) will be used to connect to server if it's empty.
|
|
||||||
HttpRequest(const std::string& method,
|
HttpRequest(const std::string& method,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
const std::string& host,
|
const std::vector<std::string>& parameters = {});
|
||||||
const std::string& port = "");
|
|
||||||
|
|
||||||
~HttpRequest() override = default;
|
~HttpRequest() override = default;
|
||||||
|
|
||||||
const std::string& method() const { return method_; }
|
const std::string& method() const {
|
||||||
|
return method_;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& url() const { return url_; }
|
const Url& url() const {
|
||||||
|
return url_;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& host() const { return host_; }
|
const std::string& host() const {
|
||||||
const std::string& port() const { return port_; }
|
return url_.host();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& port() const {
|
||||||
|
return url_.port();
|
||||||
|
}
|
||||||
|
|
||||||
std::string port(const std::string& default_port) const {
|
std::string port(const std::string& default_port) const {
|
||||||
return port_.empty() ? default_port : port_;
|
return port().empty() ? default_port : port();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shortcut to set `Accept` header.
|
// Shortcut to set `Accept` header.
|
||||||
@@ -51,30 +58,26 @@ class HttpRequest : public HttpMessage {
|
|||||||
|
|
||||||
// Prepare payload.
|
// Prepare payload.
|
||||||
// Compose start line, set Host header, etc.
|
// Compose start line, set Host header, etc.
|
||||||
void Prepare() override;
|
bool Prepare() override;
|
||||||
|
|
||||||
|
// TODO: Re-place
|
||||||
static HttpRequestPtr New(const std::string& method,
|
static HttpRequestPtr New(const std::string& method,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
const std::string& host,
|
const std::vector<std::string>& parameters = {},
|
||||||
const std::string& port = "",
|
|
||||||
bool prepare = true);
|
bool prepare = true);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class HttpRequestParser;
|
friend class HttpRequestParser;
|
||||||
|
|
||||||
void set_method(const std::string& method) { method_ = method; }
|
void set_method(const std::string& method) {
|
||||||
void set_url(const std::string& url) { url_ = url; }
|
method_ = method;
|
||||||
|
}
|
||||||
|
void set_url(const std::string& url) {
|
||||||
|
url_.Init(url);
|
||||||
|
}
|
||||||
|
|
||||||
// HTTP method.
|
|
||||||
std::string method_;
|
std::string method_;
|
||||||
|
Url url_;
|
||||||
// Request URL.
|
|
||||||
// A complete URL naming the requested resource, or the path component of
|
|
||||||
// the URL.
|
|
||||||
std::string url_;
|
|
||||||
|
|
||||||
std::string host_;
|
|
||||||
std::string port_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
#ifndef WEBCC_HTTP_REQUEST_ARGS_H_
|
||||||
|
#define WEBCC_HTTP_REQUEST_ARGS_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webcc/globals.h"
|
||||||
|
#include "webcc/logger.h"
|
||||||
|
|
||||||
|
namespace webcc {
|
||||||
|
|
||||||
|
class HttpClientSession;
|
||||||
|
|
||||||
|
// Args maker for HttpClientSession.
|
||||||
|
// Use method chaining to simulate Named Parameters.
|
||||||
|
class HttpRequestArgs {
|
||||||
|
public:
|
||||||
|
explicit HttpRequestArgs(const std::string& method = "")
|
||||||
|
: method_(method), json_(false), buffer_size_(0) {
|
||||||
|
LOG_VERB("HttpRequestArgs()");
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs(const HttpRequestArgs&) = default;
|
||||||
|
HttpRequestArgs& operator=(const HttpRequestArgs&) = default;
|
||||||
|
|
||||||
|
#if WEBCC_DEFAULT_MOVE_COPY_ASSIGN
|
||||||
|
|
||||||
|
HttpRequestArgs(HttpRequestArgs&&) = default;
|
||||||
|
HttpRequestArgs& operator=(HttpRequestArgs&&) = default;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
HttpRequestArgs(HttpRequestArgs&& rhs)
|
||||||
|
: method_(std::move(rhs.method_)),
|
||||||
|
url_(std::move(rhs.url_)),
|
||||||
|
parameters_(std::move(rhs.parameters_)),
|
||||||
|
data_(std::move(rhs.data_)),
|
||||||
|
json_(rhs.json_),
|
||||||
|
headers_(std::move(rhs.headers_)),
|
||||||
|
buffer_size_(rhs.buffer_size_) {
|
||||||
|
LOG_VERB("HttpRequestArgs(&&)");
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs& operator=(HttpRequestArgs&& rhs) {
|
||||||
|
if (&rhs != this) {
|
||||||
|
method_ = std::move(rhs.method_);
|
||||||
|
url_ = std::move(rhs.url_);
|
||||||
|
parameters_ = std::move(rhs.parameters_);
|
||||||
|
data_ = std::move(rhs.data_);
|
||||||
|
json_ = rhs.json_;
|
||||||
|
headers_ = std::move(rhs.headers_);
|
||||||
|
buffer_size_ = buffer_size_;
|
||||||
|
}
|
||||||
|
LOG_VERB("HttpRequestArgs& operator=(&&)");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WEBCC_DEFAULT_MOVE_COPY_ASSIGN
|
||||||
|
|
||||||
|
HttpRequestArgs&& method(const std::string& method) {
|
||||||
|
method_ = method;
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs&& url(const std::string& url) {
|
||||||
|
url_ = url;
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs&& url(std::string&& url) {
|
||||||
|
url_ = std::move(url);
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs&& parameters(const std::vector<std::string>& parameters) {
|
||||||
|
parameters_ = parameters;
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs&& parameters(std::vector<std::string>&& parameters) {
|
||||||
|
parameters_ = std::move(parameters);
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs&& data(const std::string& data) {
|
||||||
|
data_ = data;
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs&& data(std::string&& data) {
|
||||||
|
data_ = std::move(data);
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs&& json(bool json = true) {
|
||||||
|
json_ = json;
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs&& headers(const std::vector<std::string>& headers) {
|
||||||
|
headers_ = headers;
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs&& headers(std::vector<std::string>&& headers) {
|
||||||
|
headers_ = std::move(headers);
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestArgs&& buffer_size(std::size_t buffer_size) {
|
||||||
|
buffer_size_ = buffer_size;
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class HttpClientSession;
|
||||||
|
|
||||||
|
std::string method_;
|
||||||
|
|
||||||
|
std::string url_;
|
||||||
|
|
||||||
|
std::vector<std::string> parameters_;
|
||||||
|
|
||||||
|
// Data to send in the body of the request.
|
||||||
|
std::string data_;
|
||||||
|
|
||||||
|
// Is the data to send a JSON string?
|
||||||
|
bool json_;
|
||||||
|
|
||||||
|
std::vector<std::string> headers_;
|
||||||
|
|
||||||
|
// Size of the buffer to read response.
|
||||||
|
// Leave it to 0 for using default value.
|
||||||
|
std::size_t buffer_size_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webcc
|
||||||
|
|
||||||
|
#endif // WEBCC_HTTP_REQUEST_ARGS_H_
|
||||||
@@ -9,8 +9,8 @@
|
|||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
void HttpRequestHandler::Enqueue(HttpSessionPtr session) {
|
void HttpRequestHandler::Enqueue(HttpConnectionPtr connection) {
|
||||||
queue_.Push(session);
|
queue_.Push(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpRequestHandler::Start(std::size_t count) {
|
void HttpRequestHandler::Start(std::size_t count) {
|
||||||
@@ -24,14 +24,14 @@ void HttpRequestHandler::Start(std::size_t count) {
|
|||||||
void HttpRequestHandler::Stop() {
|
void HttpRequestHandler::Stop() {
|
||||||
LOG_INFO("Stopping workers...");
|
LOG_INFO("Stopping workers...");
|
||||||
|
|
||||||
// Close pending sessions.
|
// Close pending connections.
|
||||||
for (HttpSessionPtr s = queue_.Pop(); s; s = queue_.Pop()) {
|
for (HttpConnectionPtr s = queue_.Pop(); s; s = queue_.Pop()) {
|
||||||
LOG_INFO("Closing pending session...");
|
LOG_INFO("Closing pending connection...");
|
||||||
s->Close();
|
s->Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enqueue a null session to trigger the first worker to stop.
|
// Enqueue a null connection to trigger the first worker to stop.
|
||||||
queue_.Push(HttpSessionPtr());
|
queue_.Push(HttpConnectionPtr());
|
||||||
|
|
||||||
for (auto& worker : workers_) {
|
for (auto& worker : workers_) {
|
||||||
if (worker.joinable()) {
|
if (worker.joinable()) {
|
||||||
@@ -46,19 +46,19 @@ void HttpRequestHandler::WorkerRoutine() {
|
|||||||
LOG_INFO("Worker is running.");
|
LOG_INFO("Worker is running.");
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
HttpSessionPtr session = queue_.PopOrWait();
|
HttpConnectionPtr connection = queue_.PopOrWait();
|
||||||
|
|
||||||
if (!session) {
|
if (!connection) {
|
||||||
LOG_INFO("Worker is going to stop.");
|
LOG_INFO("Worker is going to stop.");
|
||||||
|
|
||||||
// For stopping next worker.
|
// For stopping next worker.
|
||||||
queue_.Push(HttpSessionPtr());
|
queue_.Push(HttpConnectionPtr());
|
||||||
|
|
||||||
// Stop the worker.
|
// Stop the worker.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleSession(session);
|
HandleConnection(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "webcc/http_session.h"
|
#include "webcc/http_connection.h"
|
||||||
#include "webcc/queue.h"
|
#include "webcc/queue.h"
|
||||||
#include "webcc/soap_service.h"
|
#include "webcc/soap_service.h"
|
||||||
|
|
||||||
@@ -16,28 +16,30 @@ class HttpResponse;
|
|||||||
|
|
||||||
// The common handler for all incoming requests.
|
// The common handler for all incoming requests.
|
||||||
class HttpRequestHandler {
|
class HttpRequestHandler {
|
||||||
public:
|
public:
|
||||||
HttpRequestHandler() = default;
|
HttpRequestHandler() = default;
|
||||||
virtual ~HttpRequestHandler() = default;
|
virtual ~HttpRequestHandler() = default;
|
||||||
|
|
||||||
WEBCC_DELETE_COPY_ASSIGN(HttpRequestHandler);
|
WEBCC_DELETE_COPY_ASSIGN(HttpRequestHandler);
|
||||||
|
|
||||||
// Put the session into the queue.
|
// Put the connection into the queue.
|
||||||
void Enqueue(HttpSessionPtr session);
|
void Enqueue(HttpConnectionPtr connection);
|
||||||
|
|
||||||
// Start worker threads.
|
// Start worker threads.
|
||||||
void Start(std::size_t count);
|
void Start(std::size_t count);
|
||||||
|
|
||||||
// Close pending sessions and stop worker threads.
|
// Close pending connections and stop worker threads.
|
||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void WorkerRoutine();
|
void WorkerRoutine();
|
||||||
|
|
||||||
// Called by the worker routine.
|
// Called by the worker routine.
|
||||||
virtual void HandleSession(HttpSessionPtr session) = 0;
|
virtual void HandleConnection(HttpConnectionPtr connection) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Queue<HttpConnectionPtr> queue_;
|
||||||
|
|
||||||
Queue<HttpSessionPtr> queue_;
|
|
||||||
std::vector<std::thread> workers_;
|
std::vector<std::thread> workers_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -57,13 +57,13 @@ const std::string& ToString(int status) {
|
|||||||
|
|
||||||
} // namespace status_strings
|
} // namespace status_strings
|
||||||
|
|
||||||
void HttpResponse::Prepare() {
|
bool HttpResponse::Prepare() {
|
||||||
start_line_ = status_strings::ToString(status_);
|
start_line_ = status_strings::ToString(status_);
|
||||||
|
|
||||||
// NOTE: C++11 requires a space between literal and string macro.
|
SetHeader("Server", USER_AGENT);
|
||||||
SetHeader("Server", "Webcc/" WEBCC_VERSION);
|
|
||||||
|
|
||||||
SetHeader("Date", GetHttpDateTimestamp());
|
SetHeader("Date", GetHttpDateTimestamp());
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse HttpResponse::Fault(http::Status status) {
|
HttpResponse HttpResponse::Fault(http::Status status) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class HttpResponse : public HttpMessage {
|
|||||||
void set_status(int status) { status_ = status; }
|
void set_status(int status) { status_ = status; }
|
||||||
|
|
||||||
// Set start line according to status code.
|
// Set start line according to status code.
|
||||||
void Prepare() override;
|
bool Prepare() override;
|
||||||
|
|
||||||
// Get a fault response when HTTP status is not OK.
|
// Get a fault response when HTTP status is not OK.
|
||||||
// TODO: Avoid copy.
|
// TODO: Avoid copy.
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ HttpResponseParser::HttpResponseParser(HttpResponse* response)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool HttpResponseParser::ParseStartLine(const std::string& line) {
|
bool HttpResponseParser::ParseStartLine(const std::string& line) {
|
||||||
std::vector<std::string> splitted;
|
std::vector<std::string> parts;
|
||||||
boost::split(splitted, line, boost::is_any_of(" "), boost::token_compress_on);
|
boost::split(parts, line, boost::is_any_of(" "), boost::token_compress_on);
|
||||||
|
|
||||||
if (splitted.size() < 3) {
|
if (parts.size() < 3) {
|
||||||
LOG_ERRO("Invalid HTTP response status line: %s", line.c_str());
|
LOG_ERRO("Invalid HTTP response status line: %s", line.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string& status_str = splitted[1];
|
std::string& status_str = parts[1];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
response_->set_status(std::stoi(status_str));
|
response_->set_status(std::stoi(status_str));
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ void HttpServer::DoAccept() {
|
|||||||
if (!ec) {
|
if (!ec) {
|
||||||
LOG_INFO("Accepted a connection.");
|
LOG_INFO("Accepted a connection.");
|
||||||
|
|
||||||
std::make_shared<HttpSession>(std::move(socket),
|
std::make_shared<HttpConnection>(std::move(socket),
|
||||||
GetRequestHandler())->Start();
|
GetRequestHandler())->Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
DoAccept();
|
DoAccept();
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
#include "boost/asio/signal_set.hpp"
|
#include "boost/asio/signal_set.hpp"
|
||||||
|
|
||||||
#include "webcc/globals.h"
|
#include "webcc/globals.h"
|
||||||
#include "webcc/http_session.h"
|
#include "webcc/http_connection.h"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ HttpSslClient::HttpSslClient(bool ssl_verify, std::size_t buffer_size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Error HttpSslClient::Connect(const HttpRequest& request) {
|
Error HttpSslClient::Connect(const HttpRequest& request) {
|
||||||
Error error = DoConnect(request, kHttpSslPort);
|
Error error = DoConnect(request, kPort443);
|
||||||
|
|
||||||
if (error != kNoError) {
|
if (error != kNoError) {
|
||||||
return error;
|
return error;
|
||||||
|
|||||||
+4
-4
@@ -35,22 +35,22 @@ class RestClient {
|
|||||||
// instead for the HTTP status code.
|
// instead for the HTTP status code.
|
||||||
|
|
||||||
inline bool Get(const std::string& url, std::size_t buffer_size = 0) {
|
inline bool Get(const std::string& url, std::size_t buffer_size = 0) {
|
||||||
return Request(kHttpGet, url, "", buffer_size);
|
return Request("GET", url, "", buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Post(const std::string& url, std::string&& content,
|
inline bool Post(const std::string& url, std::string&& content,
|
||||||
std::size_t buffer_size = 0) {
|
std::size_t buffer_size = 0) {
|
||||||
return Request(kHttpPost, url, std::move(content), buffer_size);
|
return Request("POST", url, std::move(content), buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Put(const std::string& url, std::string&& content,
|
inline bool Put(const std::string& url, std::string&& content,
|
||||||
std::size_t buffer_size = 0) {
|
std::size_t buffer_size = 0) {
|
||||||
return Request(kHttpPut, url, std::move(content), buffer_size);
|
return Request("PUT", url, std::move(content), buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Patch(const std::string& url, std::string&& content,
|
inline bool Patch(const std::string& url, std::string&& content,
|
||||||
std::size_t buffer_size = 0) {
|
std::size_t buffer_size = 0) {
|
||||||
return Request(kHttpPatch, url, std::move(content), buffer_size);
|
return Request("PATCH", url, std::move(content), buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Delete(const std::string& url, std::size_t buffer_size = 0) {
|
inline bool Delete(const std::string& url, std::size_t buffer_size = 0) {
|
||||||
|
|||||||
@@ -13,13 +13,14 @@ bool RestRequestHandler::Bind(RestServicePtr service, const std::string& url,
|
|||||||
return service_manager_.AddService(service, url, is_regex);
|
return service_manager_.AddService(service, url, is_regex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestRequestHandler::HandleSession(HttpSessionPtr session) {
|
void RestRequestHandler::HandleConnection(HttpConnectionPtr connection) {
|
||||||
const HttpRequest& http_request = session->request();
|
const HttpRequest& http_request = connection->request();
|
||||||
|
|
||||||
Url url(http_request.url(), /*decode*/true);
|
// TODO
|
||||||
|
const Url& url = http_request.url();
|
||||||
|
|
||||||
if (!url.IsPathValid()) {
|
if (url.path().empty()) {
|
||||||
session->SendResponse(http::Status::kBadRequest);
|
connection->SendResponse(http::Status::kBadRequest);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ void RestRequestHandler::HandleSession(HttpSessionPtr session) {
|
|||||||
|
|
||||||
if (!service) {
|
if (!service) {
|
||||||
LOG_WARN("No service matches the URL path: %s", url.path().c_str());
|
LOG_WARN("No service matches the URL path: %s", url.path().c_str());
|
||||||
session->SendResponse(http::Status::kNotFound);
|
connection->SendResponse(http::Status::kNotFound);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,13 +43,13 @@ void RestRequestHandler::HandleSession(HttpSessionPtr session) {
|
|||||||
service->Handle(rest_request, &rest_response);
|
service->Handle(rest_request, &rest_response);
|
||||||
|
|
||||||
if (!rest_response.content.empty()) {
|
if (!rest_response.content.empty()) {
|
||||||
session->SetResponseContent(std::move(rest_response.content),
|
connection->SetResponseContent(std::move(rest_response.content),
|
||||||
http::media_types::kApplicationJson,
|
http::media_types::kApplicationJson,
|
||||||
http::charsets::kUtf8);
|
http::charsets::kUtf8);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send response back to client.
|
// Send response back to client.
|
||||||
session->SendResponse(rest_response.status);
|
connection->SendResponse(rest_response.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
@@ -11,14 +11,15 @@
|
|||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
class RestRequestHandler : public HttpRequestHandler {
|
class RestRequestHandler : public HttpRequestHandler {
|
||||||
public:
|
public:
|
||||||
RestRequestHandler() = default;
|
RestRequestHandler() = default;
|
||||||
|
|
||||||
~RestRequestHandler() override = default;
|
~RestRequestHandler() override = default;
|
||||||
|
|
||||||
bool Bind(RestServicePtr service, const std::string& url, bool is_regex);
|
bool Bind(RestServicePtr service, const std::string& url, bool is_regex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void HandleSession(HttpSessionPtr session) override;
|
void HandleConnection(HttpConnectionPtr connection) override;
|
||||||
|
|
||||||
RestServiceManager service_manager_;
|
RestServiceManager service_manager_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ namespace webcc {
|
|||||||
|
|
||||||
void RestListService::Handle(const RestRequest& request,
|
void RestListService::Handle(const RestRequest& request,
|
||||||
RestResponse* response) {
|
RestResponse* response) {
|
||||||
if (request.method == kHttpGet) {
|
if (request.method == http::kGet) {
|
||||||
Get(UrlQuery(request.url_query_str), response);
|
Get(UrlQuery(request.url_query_str), response);
|
||||||
} else if (request.method == kHttpPost) {
|
} else if (request.method == http::kPost) {
|
||||||
Post(request.content, response);
|
Post(request.content, response);
|
||||||
} else {
|
} else {
|
||||||
LOG_ERRO("RestListService doesn't support '%s' method.",
|
LOG_ERRO("RestListService doesn't support '%s' method.",
|
||||||
@@ -22,13 +22,13 @@ void RestListService::Handle(const RestRequest& request,
|
|||||||
|
|
||||||
void RestDetailService::Handle(const RestRequest& request,
|
void RestDetailService::Handle(const RestRequest& request,
|
||||||
RestResponse* response) {
|
RestResponse* response) {
|
||||||
if (request.method == kHttpGet) {
|
if (request.method == http::kGet) {
|
||||||
Get(request.url_sub_matches, UrlQuery(request.url_query_str), response);
|
Get(request.url_sub_matches, UrlQuery(request.url_query_str), response);
|
||||||
} else if (request.method == kHttpPut) {
|
} else if (request.method == http::kPut) {
|
||||||
Put(request.url_sub_matches, request.content, response);
|
Put(request.url_sub_matches, request.content, response);
|
||||||
} else if (request.method == kHttpPatch) {
|
} else if (request.method == http::kPatch) {
|
||||||
Patch(request.url_sub_matches, request.content, response);
|
Patch(request.url_sub_matches, request.content, response);
|
||||||
} else if (request.method == kHttpDelete) {
|
} else if (request.method == http::kDelete) {
|
||||||
Delete(request.url_sub_matches, response);
|
Delete(request.url_sub_matches, response);
|
||||||
} else {
|
} else {
|
||||||
LOG_ERRO("RestDetailService doesn't support '%s' method.",
|
LOG_ERRO("RestDetailService doesn't support '%s' method.",
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ protected:
|
|||||||
// Default: "utf-8".
|
// Default: "utf-8".
|
||||||
std::string content_charset_;
|
std::string content_charset_;
|
||||||
|
|
||||||
// Default headers for all session requests.
|
// Default headers for each request sent from this session.
|
||||||
std::map<std::string, std::string> headers_;
|
std::map<std::string, std::string> headers_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -96,7 +96,7 @@ public:
|
|||||||
|
|
||||||
inline bool Post(const std::string& url, std::string&& content,
|
inline bool Post(const std::string& url, std::string&& content,
|
||||||
const SSMap& headers = {}, std::size_t buffer_size = 0) {
|
const SSMap& headers = {}, std::size_t buffer_size = 0) {
|
||||||
return Request(kHttpPost, url, std::move(content), headers, buffer_size);
|
return Request(Post, url, std::move(content), headers, buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool Put(const std::string& url, std::string&& content,
|
inline bool Put(const std::string& url, std::string&& content,
|
||||||
|
|||||||
@@ -48,7 +48,14 @@ bool SoapClient::Request(const std::string& operation,
|
|||||||
std::string http_content;
|
std::string http_content;
|
||||||
soap_request.ToXml(format_raw_, indent_str_, &http_content);
|
soap_request.ToXml(format_raw_, indent_str_, &http_content);
|
||||||
|
|
||||||
HttpRequest http_request(kHttpPost, url_, host_, port_);
|
// TODO
|
||||||
|
std::string url = host_;
|
||||||
|
url += url_;
|
||||||
|
if (!port_.empty()) {
|
||||||
|
url += ":" + port_;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequest http_request(http::kPost, url);
|
||||||
|
|
||||||
http_request.SetContent(std::move(http_content), true);
|
http_request.SetContent(std::move(http_content), true);
|
||||||
|
|
||||||
|
|||||||
@@ -53,13 +53,11 @@ class SoapParameter {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
// Use "= default" if drop the support of VS 2013.
|
|
||||||
SoapParameter(SoapParameter&& rhs)
|
SoapParameter(SoapParameter&& rhs)
|
||||||
: key_(std::move(rhs.key_)), value_(std::move(rhs.value_)),
|
: key_(std::move(rhs.key_)), value_(std::move(rhs.value_)),
|
||||||
as_cdata_(rhs.as_cdata_) {
|
as_cdata_(rhs.as_cdata_) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use "= default" if drop the support of VS 2013.
|
|
||||||
SoapParameter& operator=(SoapParameter&& rhs) {
|
SoapParameter& operator=(SoapParameter&& rhs) {
|
||||||
if (&rhs != this) {
|
if (&rhs != this) {
|
||||||
key_ = std::move(rhs.key_);
|
key_ = std::move(rhs.key_);
|
||||||
|
|||||||
@@ -15,17 +15,17 @@ bool SoapRequestHandler::Bind(SoapServicePtr service, const std::string& url) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoapRequestHandler::HandleSession(HttpSessionPtr session) {
|
void SoapRequestHandler::HandleConnection(HttpConnectionPtr connection) {
|
||||||
SoapServicePtr service = GetServiceByUrl(session->request().url());
|
SoapServicePtr service = GetServiceByUrl(connection->request().url().path());
|
||||||
if (!service) {
|
if (!service) {
|
||||||
session->SendResponse(http::Status::kBadRequest);
|
connection->SendResponse(http::Status::kBadRequest);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the SOAP request XML.
|
// Parse the SOAP request XML.
|
||||||
SoapRequest soap_request;
|
SoapRequest soap_request;
|
||||||
if (!soap_request.FromXml(session->request().content())) {
|
if (!soap_request.FromXml(connection->request().content())) {
|
||||||
session->SendResponse(http::Status::kBadRequest);
|
connection->SendResponse(http::Status::kBadRequest);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ void SoapRequestHandler::HandleSession(HttpSessionPtr session) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!service->Handle(soap_request, &soap_response)) {
|
if (!service->Handle(soap_request, &soap_response)) {
|
||||||
session->SendResponse(http::Status::kBadRequest);
|
connection->SendResponse(http::Status::kBadRequest);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,16 +48,16 @@ void SoapRequestHandler::HandleSession(HttpSessionPtr session) {
|
|||||||
soap_response.ToXml(format_raw_, indent_str_, &content);
|
soap_response.ToXml(format_raw_, indent_str_, &content);
|
||||||
|
|
||||||
if (soap_version_ == kSoapV11) {
|
if (soap_version_ == kSoapV11) {
|
||||||
session->SetResponseContent(std::move(content),
|
connection->SetResponseContent(std::move(content),
|
||||||
http::media_types::kTextXml,
|
http::media_types::kTextXml,
|
||||||
http::charsets::kUtf8);
|
http::charsets::kUtf8);
|
||||||
} else {
|
} else {
|
||||||
session->SetResponseContent(std::move(content),
|
connection->SetResponseContent(std::move(content),
|
||||||
http::media_types::kApplicationSoapXml,
|
http::media_types::kApplicationSoapXml,
|
||||||
http::charsets::kUtf8);
|
http::charsets::kUtf8);
|
||||||
}
|
}
|
||||||
|
|
||||||
session->SendResponse(http::Status::kOK);
|
connection->SendResponse(http::Status::kOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
SoapServicePtr SoapRequestHandler::GetServiceByUrl(const std::string& url) {
|
SoapServicePtr SoapRequestHandler::GetServiceByUrl(const std::string& url) {
|
||||||
|
|||||||
@@ -10,15 +10,16 @@
|
|||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
class SoapRequestHandler : public HttpRequestHandler {
|
class SoapRequestHandler : public HttpRequestHandler {
|
||||||
public:
|
public:
|
||||||
explicit SoapRequestHandler(SoapVersion soap_version)
|
explicit SoapRequestHandler(SoapVersion soap_version)
|
||||||
: soap_version_(soap_version),
|
: soap_version_(soap_version), format_raw_(true) {
|
||||||
format_raw_(true) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~SoapRequestHandler() override = default;
|
~SoapRequestHandler() override = default;
|
||||||
|
|
||||||
void set_format_raw(bool format_raw) { format_raw_ = format_raw; }
|
void set_format_raw(bool format_raw) {
|
||||||
|
format_raw_ = format_raw;
|
||||||
|
}
|
||||||
|
|
||||||
void set_indent_str(const std::string& indent_str) {
|
void set_indent_str(const std::string& indent_str) {
|
||||||
indent_str_ = indent_str;
|
indent_str_ = indent_str;
|
||||||
@@ -26,8 +27,8 @@ class SoapRequestHandler : public HttpRequestHandler {
|
|||||||
|
|
||||||
bool Bind(SoapServicePtr service, const std::string& url);
|
bool Bind(SoapServicePtr service, const std::string& url);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void HandleSession(HttpSessionPtr session) override;
|
void HandleConnection(HttpConnectionPtr connection) override;
|
||||||
|
|
||||||
SoapServicePtr GetServiceByUrl(const std::string& url);
|
SoapServicePtr GetServiceByUrl(const std::string& url);
|
||||||
|
|
||||||
|
|||||||
+65
-25
@@ -4,6 +4,8 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "boost/algorithm/string.hpp"
|
||||||
|
|
||||||
namespace webcc {
|
namespace webcc {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -244,48 +246,86 @@ UrlQuery::ConstIterator UrlQuery::Find(const std::string& key) const {
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
Url::Url(const std::string& str, bool decode) {
|
Url::Url(const std::string& str, bool decode) {
|
||||||
|
Init(str, decode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Url::Init(const std::string& str, bool decode, bool clear) {
|
||||||
|
if (clear) {
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
if (!decode || str.find('%') == std::string::npos) {
|
if (!decode || str.find('%') == std::string::npos) {
|
||||||
Init(str);
|
Parse(str);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string decoded;
|
std::string decoded;
|
||||||
if (Decode(str, &decoded)) {
|
if (Decode(str, &decoded)) {
|
||||||
Init(decoded);
|
Parse(decoded);
|
||||||
} else {
|
} else {
|
||||||
// TODO(Adam): Exception?
|
// TODO: Exception?
|
||||||
Init(str);
|
Parse(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Url::IsPathValid() const {
|
void Url::AddParameter(const std::string& key, const std::string& value) {
|
||||||
// URL path must be absolute.
|
if (!query_.empty()) {
|
||||||
if (path_.empty() || path_[0] != '/') {
|
query_ += "&";
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
query_ += key + "=" + value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Url::SplitPath(const std::string& path) {
|
void Url::Parse(const std::string& str) {
|
||||||
std::vector<std::string> results;
|
std::string tmp = boost::trim_left_copy(str);
|
||||||
std::stringstream iss(path);
|
|
||||||
std::string s;
|
std::size_t pos = std::string::npos;
|
||||||
while (std::getline(iss, s, '/')) {
|
|
||||||
if (!s.empty()) {
|
pos = tmp.find("://");
|
||||||
results.push_back(s);
|
if (pos != std::string::npos) {
|
||||||
|
scheme_ = tmp.substr(0, pos);
|
||||||
|
tmp = tmp.substr(pos + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = tmp.find('/');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
host_ = tmp.substr(0, pos);
|
||||||
|
|
||||||
|
tmp = tmp.substr(pos + 1);
|
||||||
|
|
||||||
|
pos = tmp.find('?');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
path_ = tmp.substr(0, pos);
|
||||||
|
query_ = tmp.substr(pos + 1);
|
||||||
|
} else {
|
||||||
|
path_ = tmp;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
path_ = "";
|
||||||
|
|
||||||
|
pos = tmp.find('?');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
host_ = tmp.substr(0, pos);
|
||||||
|
query_ = tmp.substr(pos + 1);
|
||||||
|
} else {
|
||||||
|
host_ = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!host_.empty()) {
|
||||||
|
pos = host_.find(':');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
port_ = host_.substr(pos + 1);
|
||||||
|
host_ = host_.substr(0, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Url::Init(const std::string& str) {
|
void Url::Clear() {
|
||||||
std::size_t pos = str.find('?');
|
scheme_.clear();
|
||||||
if (pos == std::string::npos) {
|
host_.clear();
|
||||||
path_ = str;
|
port_.clear();
|
||||||
} else {
|
path_.clear();
|
||||||
path_ = str.substr(0, pos);
|
query_.clear();
|
||||||
query_ = str.substr(pos + 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webcc
|
} // namespace webcc
|
||||||
|
|||||||
+28
-20
@@ -2,10 +2,6 @@
|
|||||||
#define WEBCC_URL_H_
|
#define WEBCC_URL_H_
|
||||||
|
|
||||||
// A simplified implementation of URL (or URI).
|
// A simplified implementation of URL (or URI).
|
||||||
// The URL should start with "/".
|
|
||||||
// The parameters (separated by ";") are not supported.
|
|
||||||
// Example:
|
|
||||||
// /inventory-check.cgi?item=12731&color=blue&size=large
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -17,7 +13,7 @@ namespace webcc {
|
|||||||
|
|
||||||
// URL query parameters.
|
// URL query parameters.
|
||||||
class UrlQuery {
|
class UrlQuery {
|
||||||
public:
|
public:
|
||||||
typedef std::pair<std::string, std::string> Parameter;
|
typedef std::pair<std::string, std::string> Parameter;
|
||||||
typedef std::vector<Parameter> Parameters;
|
typedef std::vector<Parameter> Parameters;
|
||||||
|
|
||||||
@@ -48,7 +44,7 @@ class UrlQuery {
|
|||||||
// E.g., "item=12731&color=blue&size=large".
|
// E.g., "item=12731&color=blue&size=large".
|
||||||
std::string ToString() const;
|
std::string ToString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef Parameters::const_iterator ConstIterator;
|
typedef Parameters::const_iterator ConstIterator;
|
||||||
ConstIterator Find(const std::string& key) const;
|
ConstIterator Find(const std::string& key) const;
|
||||||
|
|
||||||
@@ -58,34 +54,46 @@ class UrlQuery {
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
class Url {
|
class Url {
|
||||||
public:
|
public:
|
||||||
Url() = default;
|
Url() = default;
|
||||||
Url(const std::string& str, bool decode);
|
|
||||||
|
|
||||||
bool IsPathValid() const;
|
// TODO: decode/encode/encoded?
|
||||||
|
explicit Url(const std::string& str, bool decode = true);
|
||||||
|
|
||||||
|
void Init(const std::string& str, bool decode = true, bool clear = true);
|
||||||
|
|
||||||
|
const std::string& scheme() const {
|
||||||
|
return scheme_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& host() const {
|
||||||
|
return host_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& port() const {
|
||||||
|
return port_;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& path() const {
|
const std::string& path() const {
|
||||||
return path_;
|
return path_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_path(const std::string& path) {
|
|
||||||
path_ = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& query() const {
|
const std::string& query() const {
|
||||||
return query_;
|
return query_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_query(const std::string& query) {
|
// Add a parameter to the query string.
|
||||||
query_ = query;
|
void AddParameter(const std::string& key, const std::string& value);
|
||||||
}
|
|
||||||
|
|
||||||
// Split a path into its hierarchical components.
|
private:
|
||||||
static std::vector<std::string> SplitPath(const std::string& path);
|
void Parse(const std::string& str);
|
||||||
|
|
||||||
private:
|
void Clear();
|
||||||
void Init(const std::string& str);
|
|
||||||
|
|
||||||
|
// TODO: Support auth & fragment.
|
||||||
|
std::string scheme_;
|
||||||
|
std::string host_;
|
||||||
|
std::string port_;
|
||||||
std::string path_;
|
std::string path_;
|
||||||
std::string query_;
|
std::string query_;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user