From f43b1d0c3684bde68207921c6d296a8df2bff4f7 Mon Sep 17 00:00:00 2001 From: Adam Gu Date: Mon, 15 Jan 2018 16:42:24 +0800 Subject: [PATCH] Remove http version; refine sending request and error handling. --- CMakeLists.txt | 1 + src/csoap/CMakeLists.txt | 3 ++ src/csoap/common.cc | 6 ++- src/csoap/common.h | 34 ++----------- src/csoap/connection.cc | 4 +- src/csoap/connection.h | 2 +- src/csoap/http_client.cc | 32 +++++------- src/csoap/http_client.h | 5 -- src/csoap/http_message.cc | 16 ++++++ src/csoap/http_message.h | 41 +++++++++------ src/csoap/http_parser.cc | 27 ++++------ src/csoap/http_parser.h | 4 -- src/csoap/http_request.cc | 81 ++++++++++++++++-------------- src/csoap/http_request.h | 44 +++++----------- src/csoap/http_request_handler.cc | 20 ++++---- src/csoap/http_request_handler.h | 7 ++- src/csoap/http_request_parser.cc | 2 +- src/csoap/http_response.cc | 58 ++++++--------------- src/csoap/http_response_parser.cc | 7 +-- src/csoap/soap_client.cc | 19 +++---- src/csoap/soap_message.h | 13 +++-- src/csoap/soap_request.h | 7 ++- src/csoap/soap_response.h | 10 ++-- src/csoap/soap_service.cc | 1 - src/csoap/soap_service.h | 2 +- src/csoap/xml.h | 2 +- src/demo/calculator_server/main.cc | 16 +++--- 27 files changed, 201 insertions(+), 263 deletions(-) create mode 100644 src/csoap/http_message.cc delete mode 100644 src/csoap/soap_service.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c192e8..f90bd72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ if(MSVC) add_definitions(-D_WIN32_WINNT=0x0601) endif() +# Boost version: 1.66+ set(Boost_USE_STATIC_LIBS ON) find_package(Boost) if(Boost_FOUND) diff --git a/src/csoap/CMakeLists.txt b/src/csoap/CMakeLists.txt index 03e2a39..402ab2f 100644 --- a/src/csoap/CMakeLists.txt +++ b/src/csoap/CMakeLists.txt @@ -2,6 +2,9 @@ if(UNIX) add_definitions(-D_GLIBCXX_USE_WCHAR_T -std=c++11) endif() +# Don't use any deprecated definitions (e.g., io_service). +add_definitions(-DBOOST_ASIO_NO_DEPRECATED) + file(GLOB SRCS *.cc *.h) add_library(csoap ${SRCS}) diff --git a/src/csoap/common.cc b/src/csoap/common.cc index f616075..83168ca 100644 --- a/src/csoap/common.cc +++ b/src/csoap/common.cc @@ -7,8 +7,10 @@ namespace csoap { // NOTE: // Field names are case-insensitive. // See: https://stackoverflow.com/a/5259004 -const std::string kContentTypeName = "Content-Type"; -const std::string kContentLengthName = "Content-Length"; +const std::string kContentType = "Content-Type"; +const std::string kContentLength = "Content-Length"; +const std::string kSOAPAction = "SOAPAction"; +const std::string kHost = "Host"; // According to www.w3.org when placing SOAP messages in HTTP bodies, the HTTP // Content-type header must be chosen as "application/soap+xml" [RFC 3902]. diff --git a/src/csoap/common.h b/src/csoap/common.h index d9e4bbc..27e7979 100644 --- a/src/csoap/common.h +++ b/src/csoap/common.h @@ -10,29 +10,17 @@ namespace csoap { //////////////////////////////////////////////////////////////////////////////// -// API decorators. -// For a given class, e.g., SoapRequest, some APIs are for client while others -// are for server. In order to make it clear to the user, use the following -// macros to decorate. -#define SERVER_API -#define CLIENT_API - -//////////////////////////////////////////////////////////////////////////////// - -// TODO - // Buffer size for sending HTTP request and receiving HTTP response. const std::size_t BUF_SIZE = 1024; +const std::size_t kInvalidLength = std::string::npos; -static const std::string kCRLF = "\r\n"; - -extern const std::string kContentTypeName; -extern const std::string kContentLengthName; +extern const std::string kContentType; +extern const std::string kContentLength; +extern const std::string kSOAPAction; +extern const std::string kHost; extern const std::string kTextXmlUtf8; -const std::size_t kInvalidLength = std::string::npos; - //////////////////////////////////////////////////////////////////////////////// // Error codes. @@ -64,12 +52,6 @@ const char* GetErrorMessage(Error error); //////////////////////////////////////////////////////////////////////////////// -// TODO: No 1.1 feature has been used or supported yet. -enum HttpVersion { - kHttpV10, - kHttpV11, -}; - // HTTP response status. // NOTE: Only support the listed status codes. enum HttpStatus { @@ -80,12 +62,6 @@ enum HttpStatus { SERVICE_UNAVAILABLE = 503, }; -enum HeaderField { - kHeaderContentType, - kHeaderContentLength, - kHeaderHost, -}; - //////////////////////////////////////////////////////////////////////////////// // XML namespace name/url pair. diff --git a/src/csoap/connection.cc b/src/csoap/connection.cc index e8a6b28..dabdfe7 100644 --- a/src/csoap/connection.cc +++ b/src/csoap/connection.cc @@ -56,7 +56,6 @@ void Connection::HandleRead(boost::system::error_code ec, // Bad request. response_ = HttpResponse::Fault(HttpStatus::BAD_REQUEST); DoWrite(); - return; } @@ -67,7 +66,8 @@ void Connection::HandleRead(boost::system::error_code ec, } // Handle request. - request_handler_.HandleRequest(request_, response_); + // TODO: Time consuming + request_handler_.HandleRequest(request_, &response_); // Send back the response. DoWrite(); diff --git a/src/csoap/connection.h b/src/csoap/connection.h index d036135..0943123 100644 --- a/src/csoap/connection.h +++ b/src/csoap/connection.h @@ -57,7 +57,7 @@ private: HttpRequestHandler& request_handler_; // Buffer for incoming data. - std::array buffer_; + std::array buffer_; // The incoming request. HttpRequest request_; diff --git a/src/csoap/http_client.cc b/src/csoap/http_client.cc index 5ff893a..aa22b49 100644 --- a/src/csoap/http_client.cc +++ b/src/csoap/http_client.cc @@ -4,10 +4,6 @@ #include #endif -#include "boost/algorithm/string.hpp" -#include "boost/bind.hpp" -#include "boost/lexical_cast.hpp" - #if 0 #include "boost/asio.hpp" #else @@ -20,7 +16,10 @@ #include "csoap/http_response_parser.h" #include "csoap/http_request.h" #include "csoap/http_response.h" -#include "csoap/xml.h" + +#if CSOAP_ENABLE_OUTPUT +#include "csoap/xml.h" // For pretty print response XML. +#endif namespace csoap { @@ -71,10 +70,9 @@ Error HttpClient::SendRequest(const HttpRequest& request, port = "80"; } - // TODO: IPv4 or both IPv4 and IPv6 boost::system::error_code ec; tcp::resolver::results_type endpoints = - resolver.resolve(/*tcp::v4(), */request.host(), port, ec); + resolver.resolve(tcp::v4(), request.host(), port, ec); if (ec) { return kHostResolveError; } @@ -88,23 +86,20 @@ Error HttpClient::SendRequest(const HttpRequest& request, // Send HTTP request. - std::string headers = request.GetHeaders(); - - std::vector buffers{ - boost::asio::buffer(headers), - boost::asio::buffer(request.content()), - }; - #if CSOAP_ENABLE_OUTPUT - std::cout << request << std::endl; + std::cout << "# REQUEST" << std::endl << request << std::endl; #endif try { - boost::asio::write(socket, buffers); + boost::asio::write(socket, request.ToBuffers()); } catch (boost::system::system_error&) { return kSocketWriteError; } +#if CSOAP_ENABLE_OUTPUT + std::cout << "# RESPONSE" << std::endl; +#endif + // Read and parse HTTP response. HttpResponseParser parser(response); @@ -139,10 +134,9 @@ Error HttpClient::SendRequest(const HttpRequest& request, } #if CSOAP_ENABLE_OUTPUT - std::cout << std::endl << std::endl; - std::cout << "[ PRETTY PRINT ]" << std::endl; - xml::PrettyPrintXml(std::cout, response->content()); std::cout << std::endl; + std::cout << "# RESPONSE (PARSED)" << std::endl; + std::cout << *response << std::endl; #endif return kNoError; diff --git a/src/csoap/http_client.h b/src/csoap/http_client.h index a1cb911..8e3c709 100644 --- a/src/csoap/http_client.h +++ b/src/csoap/http_client.h @@ -2,12 +2,7 @@ #define CSOAP_HTTP_CLIENT_H_ #include -#include - -// Don't use any deprecated definitions (e.g., io_service). -#define BOOST_ASIO_NO_DEPRECATED #include "boost/asio/io_context.hpp" - #include "csoap/common.h" namespace csoap { diff --git a/src/csoap/http_message.cc b/src/csoap/http_message.cc new file mode 100644 index 0000000..78c73b8 --- /dev/null +++ b/src/csoap/http_message.cc @@ -0,0 +1,16 @@ +#include "csoap/http_message.h" + +namespace csoap { + +void HttpMessage::SetHeader(const std::string& name, const std::string& value) { + for (HttpHeader& h : headers_) { + if (h.name == name) { // TODO: Ignore case? + h.value = value; + return; + } + } + + headers_.push_back({ name, value }); +} + +} // namespace csoap diff --git a/src/csoap/http_message.h b/src/csoap/http_message.h index f2c6c78..831143e 100644 --- a/src/csoap/http_message.h +++ b/src/csoap/http_message.h @@ -16,31 +16,36 @@ public: // Base class for HTTP request and response messages. class HttpMessage { public: - void set_version(HttpVersion version) { - version_ = version; + const std::string& start_line() const { + return start_line_; + } + void set_start_line(const std::string& start_line) { + start_line_ = start_line; } size_t content_length() const { return content_length_; } - void set_content_length(size_t length) { - content_length_ = length; + + const std::string& content() const { + return content_; } + void SetHeader(const std::string& name, const std::string& value); + // E.g., "text/xml; charset=utf-8" - void set_content_type(const std::string& content_type) { - content_type_ = content_type; + void SetContentType(const std::string& content_type) { + SetHeader(kContentType, content_type); } - void AddHeader(const std::string& name, const std::string& value) { - headers_.push_back({ name, value }); + void SetContentLength(size_t content_length) { + content_length_ = content_length; + SetHeader(kContentLength, std::to_string(content_length)); } - const std::string& content() const { - return content_; - } - void set_content(const std::string& content) { - content_ = content; + // Use move semantics to avoid copy. + void set_content(std::string&& content) { + content_ = std::move(content); } void AppendContent(const char* data, size_t count) { @@ -52,19 +57,23 @@ public: } bool IsContentFull() const { - assert(content_length_ != kInvalidLength); + assert(IsContentLengthValid()); return content_.length() >= content_length_; } + bool IsContentLengthValid() const { + return content_length_ != kInvalidLength; + } + protected: HttpMessage() { } protected: - HttpVersion version_ = kHttpV11; + // Start line with trailing "\r\n". + std::string start_line_; size_t content_length_ = kInvalidLength; - std::string content_type_; std::vector headers_; diff --git a/src/csoap/http_parser.cc b/src/csoap/http_parser.cc index 0dd883e..752f9de 100644 --- a/src/csoap/http_parser.cc +++ b/src/csoap/http_parser.cc @@ -8,14 +8,10 @@ namespace csoap { HttpParser::HttpParser(HttpMessage* message) - : message_(message) - , start_line_parsed_(false) - , header_parsed_(false) - , finished_(false) { -} - -void HttpParser::Reset() { - // TODO: Reset parsing state. + : message_(message) + , start_line_parsed_(false) + , header_parsed_(false) + , finished_(false) { } Error HttpParser::Parse(const char* data, size_t len) { @@ -35,13 +31,13 @@ Error HttpParser::Parse(const char* data, size_t len) { size_t off = 0; while (true) { - size_t pos = pending_data_.find(kCRLF, off); + size_t pos = pending_data_.find("\r\n", off); if (pos == std::string::npos) { break; } - if (pos == off) { // End of headers. - off = pos + 2; // Skip CRLF. + if (pos == off) { // End of headers. + off = pos + 2; // Skip CRLF. header_parsed_ = true; break; } @@ -57,8 +53,7 @@ Error HttpParser::Parse(const char* data, size_t len) { } else { // Currently, only Content-Length is important to us. // Other fields are ignored. - if (message_->content_length() == kInvalidLength) { - // Not parsed yet. + if (!message_->IsContentLengthValid()) { ParseContentLength(line); } } @@ -69,7 +64,7 @@ Error HttpParser::Parse(const char* data, size_t len) { if (header_parsed_) { // Headers just ended. - if (message_->content_length() == kInvalidLength) { + if (!message_->IsContentLengthValid()) { // No Content-Length? return kHttpContentLengthError; } @@ -96,7 +91,7 @@ void HttpParser::ParseContentLength(const std::string& line) { std::string name = line.substr(0, pos); - if (boost::iequals(name, kContentLengthName)) { + if (boost::iequals(name, kContentLength)) { ++pos; // Skip ':'. while (line[pos] == ' ') { // Skip spaces. ++pos; @@ -105,7 +100,7 @@ void HttpParser::ParseContentLength(const std::string& line) { std::string value = line.substr(pos); try { - message_->set_content_length(boost::lexical_cast(value)); + message_->SetContentLength(boost::lexical_cast(value)); } catch (boost::bad_lexical_cast&) { // TODO } diff --git a/src/csoap/http_parser.h b/src/csoap/http_parser.h index 8ae093e..ba6a5c0 100644 --- a/src/csoap/http_parser.h +++ b/src/csoap/http_parser.h @@ -2,7 +2,6 @@ #define CSOAP_HTTP_PARSER_H_ #include - #include "csoap/common.h" namespace csoap { @@ -18,9 +17,6 @@ public: return finished_; } - // Reset parsing state. - void Reset(); - Error Parse(const char* data, size_t len); protected: diff --git a/src/csoap/http_request.cc b/src/csoap/http_request.cc index b603864..a9befda 100644 --- a/src/csoap/http_request.cc +++ b/src/csoap/http_request.cc @@ -4,63 +4,66 @@ namespace csoap { -//////////////////////////////////////////////////////////////////////////////// - std::ostream& operator<<(std::ostream& os, const HttpRequest& request) { - return os << request.GetHeaders() << request.content(); -} + os << request.start_line(); -//////////////////////////////////////////////////////////////////////////////// + for (const HttpHeader& h : request.headers_) { + os << h.name << ": " << h.value << std::endl; + } -std::string HttpRequest::GetHeaders() const { - std::string headers; + os << std::endl; - // Start line + os << request.content() << std::endl; - headers += "POST "; - headers += url_; - headers += " "; + return os; +} - if (version_ == kHttpV10) { - headers += "HTTP/1.0"; - } else { - headers += "HTTP/1.1"; - } - headers += kCRLF; +void HttpRequest::SetURL(const std::string& url) { + url_ = url; - // Header fields + start_line_ = "POST "; + start_line_ += url_; + start_line_ += " HTTP/1.1\r\n"; +} - headers += kContentTypeName; - headers += ": "; +void HttpRequest::SetHost(const std::string& host, const std::string& port) { + host_ = host; + port_ = port; - if (!content_type_.empty()) { - headers += content_type_; + if (port.empty()) { + SetHeader(kHost, host); } else { - headers += kTextXmlUtf8; + SetHeader(kHost, host + ":" + port); } +} + +namespace misc_strings { - headers += kCRLF; +const char NAME_VALUE_SEPARATOR[] = { ':', ' ' }; +const char CRLF[] = { '\r', '\n' }; - headers += kContentLengthName; - headers += ": "; - headers += std::to_string(content_length_); - headers += kCRLF; +} // misc_strings - headers += "SOAPAction: "; - headers += soap_action_; - headers += kCRLF; +// ATTENTION: The buffers don't hold the memory! +std::vector HttpRequest::ToBuffers() const { + assert(IsContentLengthValid()); - headers += "Host: "; - headers += host_; - if (!port_.empty()) { - headers += ":"; - headers += port_; + std::vector buffers; + + buffers.push_back(boost::asio::buffer(start_line_)); + + for (const HttpHeader& h : headers_) { + buffers.push_back(boost::asio::buffer(h.name)); + 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(misc_strings::CRLF)); } - headers += kCRLF; - headers += kCRLF; // End of Headers. + buffers.push_back(boost::asio::buffer(misc_strings::CRLF)); + + buffers.push_back(boost::asio::buffer(content_)); - return headers; + return buffers; } } // namespace csoap diff --git a/src/csoap/http_request.h b/src/csoap/http_request.h index 69a3306..669eeff 100644 --- a/src/csoap/http_request.h +++ b/src/csoap/http_request.h @@ -2,21 +2,15 @@ #define CSOAP_HTTP_REQUEST_H_ #include -#include - -#include "csoap/common.h" +#include "boost/asio/buffer.hpp" // for const_buffer #include "csoap/http_message.h" namespace csoap { -//////////////////////////////////////////////////////////////////////////////// - class HttpRequest; std::ostream& operator<<(std::ostream& os, const HttpRequest& request); -//////////////////////////////////////////////////////////////////////////////// - // HTTP request. // NOTE: // - Only POST method is supported. @@ -27,18 +21,6 @@ class HttpRequest : public HttpMessage { const HttpRequest& request); public: - HttpRequest() { - } - - // Set the URL for the HTTP request start line. - // Either a complete URL or the path component it is acceptable. - // E.g., both of the following URLs are OK: - // - http://ws1.parasoft.com/glue/calculator - // - /glue/calculator - void set_url(const std::string& url) { - url_ = url; - } - const std::string& host() const { return host_; } @@ -47,19 +29,21 @@ public: return port_; } + // Set the URL for the HTTP request start line. + // Either a complete URL or the path component it is acceptable. + // E.g., both of the following URLs are OK: + // - http://ws1.parasoft.com/glue/calculator + // - /glue/calculator + void SetURL(const std::string& url); + // \param host Descriptive host name or numeric IP address. // \param port Numeric port number, "80" will be used if it's empty. - void set_host(const std::string& host, const std::string& port) { - host_ = host; - port_ = port; - } + void SetHost(const std::string& host, const std::string& port); - // SOAP specific. - void set_soap_action(const std::string& soap_action) { - soap_action_ = soap_action; - } - - std::string GetHeaders() const; + // Convert the response into a vector of buffers. The buffers do not own the + // underlying memory blocks, therefore the response object must remain valid + // and not be changed until the write operation has completed. + std::vector ToBuffers() const; private: // Request URL. @@ -69,8 +53,6 @@ private: std::string host_; std::string port_; - - std::string soap_action_; }; } // namespace csoap diff --git a/src/csoap/http_request_handler.cc b/src/csoap/http_request_handler.cc index 8467756..0fbc120 100644 --- a/src/csoap/http_request_handler.cc +++ b/src/csoap/http_request_handler.cc @@ -57,20 +57,18 @@ bool HttpRequestHandler::RegisterService(SoapServicePtr soap_service) { return true; } -void HttpRequestHandler::HandleRequest(const HttpRequest& request, - HttpResponse& response) { +void HttpRequestHandler::HandleRequest(const HttpRequest& http_request, + HttpResponse* http_response) { // Parse the SOAP request XML. SoapRequest soap_request; - if (!soap_request.FromXml(request.content())) { - // TODO: Bad request + if (!soap_request.FromXml(http_request.content())) { + http_response->set_status(HttpStatus::BAD_REQUEST); return; } - // TEST - SoapResponse soap_response; - // Get service by URL. + // TODO: Get service by URL. for (SoapServicePtr& service : soap_services_) { service->Handle(soap_request, &soap_response); @@ -79,10 +77,10 @@ void HttpRequestHandler::HandleRequest(const HttpRequest& request, std::string content; soap_response.ToXml(&content); - response.set_status(HttpStatus::OK); - response.AddHeader(kContentTypeName, kTextXmlUtf8); - response.AddHeader(kContentLengthName, std::to_string(content.length())); - response.set_content(content); + http_response->set_status(HttpStatus::OK); + http_response->SetContentType(kTextXmlUtf8); + http_response->SetContentLength(content.length()); + http_response->set_content(std::move(content)); #if 0 // Decode URL to path. diff --git a/src/csoap/http_request_handler.h b/src/csoap/http_request_handler.h index eee8296..01fa088 100644 --- a/src/csoap/http_request_handler.h +++ b/src/csoap/http_request_handler.h @@ -1,9 +1,7 @@ #ifndef CSOAP_HTTP_REQUEST_HANDLER_H_ #define CSOAP_HTTP_REQUEST_HANDLER_H_ -#include #include - #include "csoap/soap_service.h" namespace csoap { @@ -21,8 +19,9 @@ public: bool RegisterService(SoapServicePtr soap_service); - // Handle a request and produce a reply. - void HandleRequest(const HttpRequest& request, HttpResponse& response); + // Handle a request and produce a response. + void HandleRequest(const HttpRequest& http_request, + HttpResponse* http_response); private: std::vector soap_services_; diff --git a/src/csoap/http_request_parser.cc b/src/csoap/http_request_parser.cc index 83e65b3..c924e54 100644 --- a/src/csoap/http_request_parser.cc +++ b/src/csoap/http_request_parser.cc @@ -27,7 +27,7 @@ Error HttpRequestParser::ParseStartLine(const std::string& line) { return kHttpStartLineError; } - request_->set_url(strs[1]); + request_->SetURL(strs[1]); // TODO: strs[2]; diff --git a/src/csoap/http_response.cc b/src/csoap/http_response.cc index 9996bd4..d05f984 100644 --- a/src/csoap/http_response.cc +++ b/src/csoap/http_response.cc @@ -8,8 +8,13 @@ namespace csoap { //////////////////////////////////////////////////////////////////////////////// std::ostream& operator<<(std::ostream& os, const HttpResponse& response) { - // TODO - os << response.status() << std::endl; + os << response.start_line(); + + for (const HttpHeader& h : response.headers_) { + os << h.name << ": " << h.value << std::endl; + } + + os << std::endl; // Pretty print the SOAP response XML. if (!xml::PrettyPrintXml(os, response.content())) { @@ -66,9 +71,10 @@ const char CRLF[] = { '\r', '\n' }; std::vector HttpResponse::ToBuffers() const { std::vector buffers; + // Status line buffers.push_back(status_strings::ToBuffer(status_)); - // Header fields + // Header fields (optional) for (const HttpHeader& h : headers_) { buffers.push_back(boost::asio::buffer(h.name)); buffers.push_back(boost::asio::buffer(misc_strings::NAME_VALUE_SEPARATOR)); @@ -77,56 +83,22 @@ std::vector HttpResponse::ToBuffers() const { } buffers.push_back(boost::asio::buffer(misc_strings::CRLF)); - buffers.push_back(boost::asio::buffer(content_)); + // Content (optional) + if (IsContentLengthValid()) { + buffers.push_back(boost::asio::buffer(content_)); + } + return buffers; } -// TODO: Move to SoapResponse -static void CreateSoapFaultResponse(HttpStatus status, - std::string* xml_string) { - Namespace soapenv_ns{ - "soap", - "http://schemas.xmlsoap.org/soap/envelope/" - }; - - pugi::xml_document xdoc; - pugi::xml_node xroot = xml::AddChild(xdoc, soapenv_ns.name, "Envelope"); - - xml::AddNSAttr(xroot, soapenv_ns.name, soapenv_ns.url); - - // FIXME: Body - // See https://www.w3schools.com/XML/xml_soap.asp - - pugi::xml_node xfault = xml::AddChild(xroot, soapenv_ns.name, "Fault"); - - pugi::xml_node xfaultcode = xfault.append_child("faultcode"); - xfaultcode.text().set(std::to_string(HttpStatus::BAD_REQUEST).c_str()); // TODO - - pugi::xml_node xfaultstring = xfault.append_child("faultstring"); - xfaultstring.text().set("Bad Request"); // TODO - - // TODO: faultactor - - xml::XmlStrRefWriter writer(xml_string); - xdoc.save(writer, "\t", pugi::format_default, pugi::encoding_utf8); -} - HttpResponse HttpResponse::Fault(HttpStatus status) { assert(status != HttpStatus::OK); HttpResponse response; - - std::string content; - CreateSoapFaultResponse(status, &content); - - response.set_content(content); - response.set_content_length(content.length()); - response.set_content_type("text/xml"); - response.set_status(status); - return response; // TODO: Output parameter? + return response; } } // namespace csoap diff --git a/src/csoap/http_response_parser.cc b/src/csoap/http_response_parser.cc index 77f0fcf..bbd6bbe 100644 --- a/src/csoap/http_response_parser.cc +++ b/src/csoap/http_response_parser.cc @@ -5,12 +5,14 @@ namespace csoap { HttpResponseParser::HttpResponseParser(HttpResponse* response) - : HttpParser(response) - , response_(response) { + : HttpParser(response) + , response_(response) { } // TODO: Use split. Error HttpResponseParser::ParseStartLine(const std::string& line) { + response_->set_start_line(line + "\r\n"); + size_t off = 0; size_t pos = line.find(' '); @@ -37,7 +39,6 @@ Error HttpResponseParser::ParseStartLine(const std::string& line) { } off = pos + 1; // Skip space. - //response_->set_reason(line.substr(off)); if (response_->status() != HttpStatus::OK) { return kHttpStatusError; diff --git a/src/csoap/soap_client.cc b/src/csoap/soap_client.cc index 36e774d..5bc2484 100644 --- a/src/csoap/soap_client.cc +++ b/src/csoap/soap_client.cc @@ -1,9 +1,6 @@ #include "csoap/soap_client.h" #include -#include - -#include "boost/lexical_cast.hpp" #include "csoap/http_client.h" #include "csoap/http_request.h" @@ -33,17 +30,17 @@ Error SoapClient::Call(const std::string& operation, soap_request.AddParameter(parameters[i]); } - std::string http_request_body; - soap_request.ToXml(&http_request_body); + std::string http_content; + soap_request.ToXml(&http_content); HttpRequest http_request; - http_request.set_version(kHttpV11); // TODO - http_request.set_url(url_); - http_request.set_content_length(http_request_body.size()); - http_request.set_content(http_request_body); // TODO: move - http_request.set_host(host_, port_); - http_request.set_soap_action(operation); + http_request.SetURL(url_); + http_request.SetContentType(kTextXmlUtf8); + http_request.SetContentLength(http_content.size()); + http_request.SetHost(host_, port_); + http_request.SetHeader(kSOAPAction, operation); + http_request.set_content(std::move(http_content)); HttpResponse http_response; diff --git a/src/csoap/soap_message.h b/src/csoap/soap_message.h index 9544b02..0197c85 100644 --- a/src/csoap/soap_message.h +++ b/src/csoap/soap_message.h @@ -3,7 +3,6 @@ #include #include "pugixml/pugixml.hpp" - #include "csoap/common.h" namespace csoap { @@ -12,27 +11,27 @@ namespace csoap { class SoapMessage { public: // E.g., set as kSoapEnvNamespace. - CLIENT_API void set_soapenv_ns(const Namespace& soapenv_ns) { + void set_soapenv_ns(const Namespace& soapenv_ns) { soapenv_ns_ = soapenv_ns; } - CLIENT_API void set_service_ns(const Namespace& service_ns) { + void set_service_ns(const Namespace& service_ns) { service_ns_ = service_ns; } - SERVER_API const std::string& operation() const { + const std::string& operation() const { return operation_; } - CLIENT_API void set_operation(const std::string& operation) { + void set_operation(const std::string& operation) { operation_ = operation; } // Convert to SOAP request XML. - CLIENT_API void ToXml(std::string* xml_string); + void ToXml(std::string* xml_string); // Parse from SOAP request XML. - SERVER_API bool FromXml(const std::string& xml_string); + bool FromXml(const std::string& xml_string); protected: SoapMessage() { diff --git a/src/csoap/soap_request.h b/src/csoap/soap_request.h index 60b8e3a..db3a5e1 100644 --- a/src/csoap/soap_request.h +++ b/src/csoap/soap_request.h @@ -2,7 +2,6 @@ #define CSOAP_SOAP_REQUEST_H_ #include - #include "csoap/soap_message.h" namespace csoap { @@ -12,11 +11,11 @@ namespace csoap { // request body. class SoapRequest : public SoapMessage { public: - CLIENT_API void AddParameter(const std::string& key, const std::string& value); - CLIENT_API void AddParameter(const Parameter& parameter); + void AddParameter(const std::string& key, const std::string& value); + void AddParameter(const Parameter& parameter); // Get parameter value by key. - SERVER_API std::string GetParameter(const std::string& key) const; + std::string GetParameter(const std::string& key) const; protected: void ToXmlBody(pugi::xml_node xbody) override; diff --git a/src/csoap/soap_response.h b/src/csoap/soap_response.h index 083909f..94558e9 100644 --- a/src/csoap/soap_response.h +++ b/src/csoap/soap_response.h @@ -11,15 +11,15 @@ public: // Could be "Price" for an operation/method like "GetXyzPrice". // Really depend on the service. // Most services use a general name "Result". - CLIENT_API void set_result_name(const std::string& result_name) { + void set_result_name(const std::string& result_name) { result_name_ = result_name; } - CLIENT_API const std::string& result() const { + const std::string& result() const { return result_; } - SERVER_API void set_result(const std::string& result) { + void set_result(const std::string& result) { result_ = result; } @@ -29,7 +29,9 @@ protected: bool FromXmlBody(pugi::xml_node xbody) override; private: - // TODO: Support multiple results. + // NOTE: + // Multiple results might be necessary. But for most cases, single result + // should be enough, because an API normally returns only one value. // Result XML node name. // Used to parse the response XML from client side. diff --git a/src/csoap/soap_service.cc b/src/csoap/soap_service.cc deleted file mode 100644 index e08235a..0000000 --- a/src/csoap/soap_service.cc +++ /dev/null @@ -1 +0,0 @@ -#include "csoap/soap_service.h" \ No newline at end of file diff --git a/src/csoap/soap_service.h b/src/csoap/soap_service.h index 86f4ca2..bf36859 100644 --- a/src/csoap/soap_service.h +++ b/src/csoap/soap_service.h @@ -19,7 +19,7 @@ public: } // Handle SOAP request, output the response. - virtual bool Handle(const SoapRequest& request, + virtual bool Handle(const SoapRequest& soap_request, SoapResponse* soap_response) = 0; protected: diff --git a/src/csoap/xml.h b/src/csoap/xml.h index 139d1a2..7400c10 100644 --- a/src/csoap/xml.h +++ b/src/csoap/xml.h @@ -65,7 +65,7 @@ std::string GetNSAttr(pugi::xml_node& xnode, // ... // std::string xml_string; // XmlStrRefWriter writer(&xml_string); -// xdoc.print(writer, " ", pugi::format_default, pugi::encoding_utf8); +// xdoc.save(writer, "\t", pugi::format_default, pugi::encoding_utf8); class XmlStrRefWriter : public pugi::xml_writer { public: explicit XmlStrRefWriter(std::string* result) diff --git a/src/demo/calculator_server/main.cc b/src/demo/calculator_server/main.cc index 725788b..788c14d 100644 --- a/src/demo/calculator_server/main.cc +++ b/src/demo/calculator_server/main.cc @@ -2,28 +2,28 @@ #include "csoap/http_server.h" #include "calculator_service.h" -// TODO: Why need an address for a server? static void PrintHelp(const char* argv0) { - std::cout << "Usage: " << argv0 << "
" << std::endl; - std::cout << " For IPv4, try:" << std::endl; - std::cout << " " << argv0 << " 0.0.0.0 80" << std::endl; - std::cout << " For IPv6, try:" << std::endl; - std::cout << " " << argv0 << " 0::0 80" << std::endl; + std::cout << "Usage: " << argv0 << " " << std::endl; + std::cout << " E.g.," << std::endl; + std::cout << " " << argv0 << " 8080" << std::endl; } int main(int argc, char* argv[]) { - if (argc != 3) { + if (argc != 2) { PrintHelp(argv[0]); return 1; } + const char* host = "0.0.0.0"; // TODO + try { - csoap::HttpServer server(argv[1], argv[2]); + csoap::HttpServer server(host, argv[1]); csoap::SoapServicePtr service(new CalculatorService); server.RegisterService(service); server.Run(); + } catch (std::exception& e) { std::cerr << "exception: " << e.what() << "\n"; return 1;