Remove http version; refine sending request and error handling.

master
Adam Gu 8 years ago
parent ab4555d58d
commit f43b1d0c36

@ -15,6 +15,7 @@ if(MSVC)
add_definitions(-D_WIN32_WINNT=0x0601) add_definitions(-D_WIN32_WINNT=0x0601)
endif() endif()
# Boost version: 1.66+
set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_LIBS ON)
find_package(Boost) find_package(Boost)
if(Boost_FOUND) if(Boost_FOUND)

@ -2,6 +2,9 @@ if(UNIX)
add_definitions(-D_GLIBCXX_USE_WCHAR_T -std=c++11) add_definitions(-D_GLIBCXX_USE_WCHAR_T -std=c++11)
endif() endif()
# Don't use any deprecated definitions (e.g., io_service).
add_definitions(-DBOOST_ASIO_NO_DEPRECATED)
file(GLOB SRCS *.cc *.h) file(GLOB SRCS *.cc *.h)
add_library(csoap ${SRCS}) add_library(csoap ${SRCS})

@ -7,8 +7,10 @@ namespace csoap {
// NOTE: // NOTE:
// Field names are case-insensitive. // Field names are case-insensitive.
// See: https://stackoverflow.com/a/5259004 // See: https://stackoverflow.com/a/5259004
const std::string kContentTypeName = "Content-Type"; const std::string kContentType = "Content-Type";
const std::string kContentLengthName = "Content-Length"; 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 // 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]. // Content-type header must be chosen as "application/soap+xml" [RFC 3902].

@ -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. // Buffer size for sending HTTP request and receiving HTTP response.
const std::size_t BUF_SIZE = 1024; 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 kContentType;
extern const std::string kContentLength;
extern const std::string kContentTypeName; extern const std::string kSOAPAction;
extern const std::string kContentLengthName; extern const std::string kHost;
extern const std::string kTextXmlUtf8; extern const std::string kTextXmlUtf8;
const std::size_t kInvalidLength = std::string::npos;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Error codes. // 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. // HTTP response status.
// NOTE: Only support the listed status codes. // NOTE: Only support the listed status codes.
enum HttpStatus { enum HttpStatus {
@ -80,12 +62,6 @@ enum HttpStatus {
SERVICE_UNAVAILABLE = 503, SERVICE_UNAVAILABLE = 503,
}; };
enum HeaderField {
kHeaderContentType,
kHeaderContentLength,
kHeaderHost,
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// XML namespace name/url pair. // XML namespace name/url pair.

@ -56,7 +56,6 @@ void Connection::HandleRead(boost::system::error_code ec,
// Bad request. // Bad request.
response_ = HttpResponse::Fault(HttpStatus::BAD_REQUEST); response_ = HttpResponse::Fault(HttpStatus::BAD_REQUEST);
DoWrite(); DoWrite();
return; return;
} }
@ -67,7 +66,8 @@ void Connection::HandleRead(boost::system::error_code ec,
} }
// Handle request. // Handle request.
request_handler_.HandleRequest(request_, response_); // TODO: Time consuming
request_handler_.HandleRequest(request_, &response_);
// Send back the response. // Send back the response.
DoWrite(); DoWrite();

@ -57,7 +57,7 @@ private:
HttpRequestHandler& request_handler_; HttpRequestHandler& request_handler_;
// Buffer for incoming data. // Buffer for incoming data.
std::array<char, 8192> buffer_; std::array<char, BUF_SIZE> buffer_;
// The incoming request. // The incoming request.
HttpRequest request_; HttpRequest request_;

@ -4,10 +4,6 @@
#include <iostream> #include <iostream>
#endif #endif
#include "boost/algorithm/string.hpp"
#include "boost/bind.hpp"
#include "boost/lexical_cast.hpp"
#if 0 #if 0
#include "boost/asio.hpp" #include "boost/asio.hpp"
#else #else
@ -20,7 +16,10 @@
#include "csoap/http_response_parser.h" #include "csoap/http_response_parser.h"
#include "csoap/http_request.h" #include "csoap/http_request.h"
#include "csoap/http_response.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 { namespace csoap {
@ -71,10 +70,9 @@ Error HttpClient::SendRequest(const HttpRequest& request,
port = "80"; port = "80";
} }
// TODO: IPv4 or both IPv4 and IPv6
boost::system::error_code ec; boost::system::error_code ec;
tcp::resolver::results_type endpoints = tcp::resolver::results_type endpoints =
resolver.resolve(/*tcp::v4(), */request.host(), port, ec); resolver.resolve(tcp::v4(), request.host(), port, ec);
if (ec) { if (ec) {
return kHostResolveError; return kHostResolveError;
} }
@ -88,23 +86,20 @@ Error HttpClient::SendRequest(const HttpRequest& request,
// Send HTTP request. // Send HTTP request.
std::string headers = request.GetHeaders();
std::vector<boost::asio::const_buffer> buffers{
boost::asio::buffer(headers),
boost::asio::buffer(request.content()),
};
#if CSOAP_ENABLE_OUTPUT #if CSOAP_ENABLE_OUTPUT
std::cout << request << std::endl; std::cout << "# REQUEST" << std::endl << request << std::endl;
#endif #endif
try { try {
boost::asio::write(socket, buffers); boost::asio::write(socket, request.ToBuffers());
} catch (boost::system::system_error&) { } catch (boost::system::system_error&) {
return kSocketWriteError; return kSocketWriteError;
} }
#if CSOAP_ENABLE_OUTPUT
std::cout << "# RESPONSE" << std::endl;
#endif
// Read and parse HTTP response. // Read and parse HTTP response.
HttpResponseParser parser(response); HttpResponseParser parser(response);
@ -139,10 +134,9 @@ Error HttpClient::SendRequest(const HttpRequest& request,
} }
#if CSOAP_ENABLE_OUTPUT #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 << std::endl;
std::cout << "# RESPONSE (PARSED)" << std::endl;
std::cout << *response << std::endl;
#endif #endif
return kNoError; return kNoError;

@ -2,12 +2,7 @@
#define CSOAP_HTTP_CLIENT_H_ #define CSOAP_HTTP_CLIENT_H_
#include <array> #include <array>
#include <string>
// Don't use any deprecated definitions (e.g., io_service).
#define BOOST_ASIO_NO_DEPRECATED
#include "boost/asio/io_context.hpp" #include "boost/asio/io_context.hpp"
#include "csoap/common.h" #include "csoap/common.h"
namespace csoap { namespace csoap {

@ -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

@ -16,31 +16,36 @@ public:
// Base class for HTTP request and response messages. // Base class for HTTP request and response messages.
class HttpMessage { class HttpMessage {
public: public:
void set_version(HttpVersion version) { const std::string& start_line() const {
version_ = version; return start_line_;
}
void set_start_line(const std::string& start_line) {
start_line_ = start_line;
} }
size_t content_length() const { size_t content_length() const {
return content_length_; 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" // E.g., "text/xml; charset=utf-8"
void set_content_type(const std::string& content_type) { void SetContentType(const std::string& content_type) {
content_type_ = content_type; SetHeader(kContentType, content_type);
} }
void AddHeader(const std::string& name, const std::string& value) { void SetContentLength(size_t content_length) {
headers_.push_back({ name, value }); content_length_ = content_length;
SetHeader(kContentLength, std::to_string(content_length));
} }
const std::string& content() const { // Use move semantics to avoid copy.
return content_; void set_content(std::string&& content) {
} content_ = std::move(content);
void set_content(const std::string& content) {
content_ = content;
} }
void AppendContent(const char* data, size_t count) { void AppendContent(const char* data, size_t count) {
@ -52,19 +57,23 @@ public:
} }
bool IsContentFull() const { bool IsContentFull() const {
assert(content_length_ != kInvalidLength); assert(IsContentLengthValid());
return content_.length() >= content_length_; return content_.length() >= content_length_;
} }
bool IsContentLengthValid() const {
return content_length_ != kInvalidLength;
}
protected: protected:
HttpMessage() { HttpMessage() {
} }
protected: protected:
HttpVersion version_ = kHttpV11; // Start line with trailing "\r\n".
std::string start_line_;
size_t content_length_ = kInvalidLength; size_t content_length_ = kInvalidLength;
std::string content_type_;
std::vector<HttpHeader> headers_; std::vector<HttpHeader> headers_;

@ -14,10 +14,6 @@ HttpParser::HttpParser(HttpMessage* message)
, finished_(false) { , finished_(false) {
} }
void HttpParser::Reset() {
// TODO: Reset parsing state.
}
Error HttpParser::Parse(const char* data, size_t len) { Error HttpParser::Parse(const char* data, size_t len) {
if (header_parsed_) { if (header_parsed_) {
// Add the data to the content. // Add the data to the content.
@ -35,7 +31,7 @@ Error HttpParser::Parse(const char* data, size_t len) {
size_t off = 0; size_t off = 0;
while (true) { while (true) {
size_t pos = pending_data_.find(kCRLF, off); size_t pos = pending_data_.find("\r\n", off);
if (pos == std::string::npos) { if (pos == std::string::npos) {
break; break;
} }
@ -57,8 +53,7 @@ Error HttpParser::Parse(const char* data, size_t len) {
} else { } else {
// Currently, only Content-Length is important to us. // Currently, only Content-Length is important to us.
// Other fields are ignored. // Other fields are ignored.
if (message_->content_length() == kInvalidLength) { if (!message_->IsContentLengthValid()) {
// Not parsed yet.
ParseContentLength(line); ParseContentLength(line);
} }
} }
@ -69,7 +64,7 @@ Error HttpParser::Parse(const char* data, size_t len) {
if (header_parsed_) { if (header_parsed_) {
// Headers just ended. // Headers just ended.
if (message_->content_length() == kInvalidLength) { if (!message_->IsContentLengthValid()) {
// No Content-Length? // No Content-Length?
return kHttpContentLengthError; return kHttpContentLengthError;
} }
@ -96,7 +91,7 @@ void HttpParser::ParseContentLength(const std::string& line) {
std::string name = line.substr(0, pos); std::string name = line.substr(0, pos);
if (boost::iequals(name, kContentLengthName)) { if (boost::iequals(name, kContentLength)) {
++pos; // Skip ':'. ++pos; // Skip ':'.
while (line[pos] == ' ') { // Skip spaces. while (line[pos] == ' ') { // Skip spaces.
++pos; ++pos;
@ -105,7 +100,7 @@ void HttpParser::ParseContentLength(const std::string& line) {
std::string value = line.substr(pos); std::string value = line.substr(pos);
try { try {
message_->set_content_length(boost::lexical_cast<size_t>(value)); message_->SetContentLength(boost::lexical_cast<size_t>(value));
} catch (boost::bad_lexical_cast&) { } catch (boost::bad_lexical_cast&) {
// TODO // TODO
} }

@ -2,7 +2,6 @@
#define CSOAP_HTTP_PARSER_H_ #define CSOAP_HTTP_PARSER_H_
#include <string> #include <string>
#include "csoap/common.h" #include "csoap/common.h"
namespace csoap { namespace csoap {
@ -18,9 +17,6 @@ public:
return finished_; return finished_;
} }
// Reset parsing state.
void Reset();
Error Parse(const char* data, size_t len); Error Parse(const char* data, size_t len);
protected: protected:

@ -4,63 +4,66 @@
namespace csoap { namespace csoap {
////////////////////////////////////////////////////////////////////////////////
std::ostream& operator<<(std::ostream& os, const HttpRequest& request) { 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 { os << std::endl;
std::string headers;
// Start line os << request.content() << std::endl;
headers += "POST "; return os;
headers += url_; }
headers += " ";
if (version_ == kHttpV10) { void HttpRequest::SetURL(const std::string& url) {
headers += "HTTP/1.0"; url_ = url;
} else {
headers += "HTTP/1.1";
}
headers += kCRLF;
// Header fields start_line_ = "POST ";
start_line_ += url_;
start_line_ += " HTTP/1.1\r\n";
}
headers += kContentTypeName; void HttpRequest::SetHost(const std::string& host, const std::string& port) {
headers += ": "; host_ = host;
port_ = port;
if (!content_type_.empty()) { if (port.empty()) {
headers += content_type_; SetHeader(kHost, host);
} else { } else {
headers += kTextXmlUtf8; SetHeader(kHost, host + ":" + port);
} }
}
namespace misc_strings {
headers += kCRLF; const char NAME_VALUE_SEPARATOR[] = { ':', ' ' };
const char CRLF[] = { '\r', '\n' };
headers += kContentLengthName; } // misc_strings
headers += ": ";
headers += std::to_string(content_length_);
headers += kCRLF;
headers += "SOAPAction: "; // ATTENTION: The buffers don't hold the memory!
headers += soap_action_; std::vector<boost::asio::const_buffer> HttpRequest::ToBuffers() const {
headers += kCRLF; assert(IsContentLengthValid());
headers += "Host: "; std::vector<boost::asio::const_buffer> buffers;
headers += host_;
if (!port_.empty()) { buffers.push_back(boost::asio::buffer(start_line_));
headers += ":";
headers += port_; 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 } // namespace csoap

@ -2,21 +2,15 @@
#define CSOAP_HTTP_REQUEST_H_ #define CSOAP_HTTP_REQUEST_H_
#include <string> #include <string>
#include <vector> #include "boost/asio/buffer.hpp" // for const_buffer
#include "csoap/common.h"
#include "csoap/http_message.h" #include "csoap/http_message.h"
namespace csoap { namespace csoap {
////////////////////////////////////////////////////////////////////////////////
class HttpRequest; class HttpRequest;
std::ostream& operator<<(std::ostream& os, const HttpRequest& request); std::ostream& operator<<(std::ostream& os, const HttpRequest& request);
////////////////////////////////////////////////////////////////////////////////
// HTTP request. // HTTP request.
// NOTE: // NOTE:
// - Only POST method is supported. // - Only POST method is supported.
@ -27,18 +21,6 @@ class HttpRequest : public HttpMessage {
const HttpRequest& request); const HttpRequest& request);
public: 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 { const std::string& host() const {
return host_; return host_;
} }
@ -47,19 +29,21 @@ public:
return port_; 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 host Descriptive host name or numeric IP address.
// \param port Numeric port number, "80" will be used if it's empty. // \param port Numeric port number, "80" will be used if it's empty.
void set_host(const std::string& host, const std::string& port) { void SetHost(const std::string& host, const std::string& port);
host_ = host;
port_ = port;
}
// SOAP specific. // Convert the response into a vector of buffers. The buffers do not own the
void set_soap_action(const std::string& soap_action) { // underlying memory blocks, therefore the response object must remain valid
soap_action_ = soap_action; // and not be changed until the write operation has completed.
} std::vector<boost::asio::const_buffer> ToBuffers() const;
std::string GetHeaders() const;
private: private:
// Request URL. // Request URL.
@ -69,8 +53,6 @@ private:
std::string host_; std::string host_;
std::string port_; std::string port_;
std::string soap_action_;
}; };
} // namespace csoap } // namespace csoap

@ -57,20 +57,18 @@ bool HttpRequestHandler::RegisterService(SoapServicePtr soap_service) {
return true; return true;
} }
void HttpRequestHandler::HandleRequest(const HttpRequest& request, void HttpRequestHandler::HandleRequest(const HttpRequest& http_request,
HttpResponse& response) { HttpResponse* http_response) {
// Parse the SOAP request XML. // Parse the SOAP request XML.
SoapRequest soap_request; SoapRequest soap_request;
if (!soap_request.FromXml(request.content())) { if (!soap_request.FromXml(http_request.content())) {
// TODO: Bad request http_response->set_status(HttpStatus::BAD_REQUEST);
return; return;
} }
// TEST
SoapResponse soap_response; SoapResponse soap_response;
// Get service by URL. // TODO: Get service by URL.
for (SoapServicePtr& service : soap_services_) { for (SoapServicePtr& service : soap_services_) {
service->Handle(soap_request, &soap_response); service->Handle(soap_request, &soap_response);
@ -79,10 +77,10 @@ void HttpRequestHandler::HandleRequest(const HttpRequest& request,
std::string content; std::string content;
soap_response.ToXml(&content); soap_response.ToXml(&content);
response.set_status(HttpStatus::OK); http_response->set_status(HttpStatus::OK);
response.AddHeader(kContentTypeName, kTextXmlUtf8); http_response->SetContentType(kTextXmlUtf8);
response.AddHeader(kContentLengthName, std::to_string(content.length())); http_response->SetContentLength(content.length());
response.set_content(content); http_response->set_content(std::move(content));
#if 0 #if 0
// Decode URL to path. // Decode URL to path.

@ -1,9 +1,7 @@
#ifndef CSOAP_HTTP_REQUEST_HANDLER_H_ #ifndef CSOAP_HTTP_REQUEST_HANDLER_H_
#define CSOAP_HTTP_REQUEST_HANDLER_H_ #define CSOAP_HTTP_REQUEST_HANDLER_H_
#include <string>
#include <vector> #include <vector>
#include "csoap/soap_service.h" #include "csoap/soap_service.h"
namespace csoap { namespace csoap {
@ -21,8 +19,9 @@ public:
bool RegisterService(SoapServicePtr soap_service); bool RegisterService(SoapServicePtr soap_service);
// Handle a request and produce a reply. // Handle a request and produce a response.
void HandleRequest(const HttpRequest& request, HttpResponse& response); void HandleRequest(const HttpRequest& http_request,
HttpResponse* http_response);
private: private:
std::vector<SoapServicePtr> soap_services_; std::vector<SoapServicePtr> soap_services_;

@ -27,7 +27,7 @@ Error HttpRequestParser::ParseStartLine(const std::string& line) {
return kHttpStartLineError; return kHttpStartLineError;
} }
request_->set_url(strs[1]); request_->SetURL(strs[1]);
// TODO: strs[2]; // TODO: strs[2];

@ -8,8 +8,13 @@ namespace csoap {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::ostream& operator<<(std::ostream& os, const HttpResponse& response) { std::ostream& operator<<(std::ostream& os, const HttpResponse& response) {
// TODO os << response.start_line();
os << response.status() << std::endl;
for (const HttpHeader& h : response.headers_) {
os << h.name << ": " << h.value << std::endl;
}
os << std::endl;
// Pretty print the SOAP response XML. // Pretty print the SOAP response XML.
if (!xml::PrettyPrintXml(os, response.content())) { if (!xml::PrettyPrintXml(os, response.content())) {
@ -66,9 +71,10 @@ const char CRLF[] = { '\r', '\n' };
std::vector<boost::asio::const_buffer> HttpResponse::ToBuffers() const { std::vector<boost::asio::const_buffer> HttpResponse::ToBuffers() const {
std::vector<boost::asio::const_buffer> buffers; std::vector<boost::asio::const_buffer> buffers;
// Status line
buffers.push_back(status_strings::ToBuffer(status_)); buffers.push_back(status_strings::ToBuffer(status_));
// Header fields // Header fields (optional)
for (const HttpHeader& h : headers_) { for (const HttpHeader& h : headers_) {
buffers.push_back(boost::asio::buffer(h.name)); 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(misc_strings::NAME_VALUE_SEPARATOR));
@ -77,56 +83,22 @@ std::vector<boost::asio::const_buffer> HttpResponse::ToBuffers() const {
} }
buffers.push_back(boost::asio::buffer(misc_strings::CRLF)); buffers.push_back(boost::asio::buffer(misc_strings::CRLF));
// Content (optional)
if (IsContentLengthValid()) {
buffers.push_back(boost::asio::buffer(content_)); buffers.push_back(boost::asio::buffer(content_));
}
return buffers; 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) { HttpResponse HttpResponse::Fault(HttpStatus status) {
assert(status != HttpStatus::OK); assert(status != HttpStatus::OK);
HttpResponse response; 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); response.set_status(status);
return response; // TODO: Output parameter? return response;
} }
} // namespace csoap } // namespace csoap

@ -11,6 +11,8 @@ HttpResponseParser::HttpResponseParser(HttpResponse* response)
// TODO: Use split. // TODO: Use split.
Error HttpResponseParser::ParseStartLine(const std::string& line) { Error HttpResponseParser::ParseStartLine(const std::string& line) {
response_->set_start_line(line + "\r\n");
size_t off = 0; size_t off = 0;
size_t pos = line.find(' '); size_t pos = line.find(' ');
@ -37,7 +39,6 @@ Error HttpResponseParser::ParseStartLine(const std::string& line) {
} }
off = pos + 1; // Skip space. off = pos + 1; // Skip space.
//response_->set_reason(line.substr(off));
if (response_->status() != HttpStatus::OK) { if (response_->status() != HttpStatus::OK) {
return kHttpStatusError; return kHttpStatusError;

@ -1,9 +1,6 @@
#include "csoap/soap_client.h" #include "csoap/soap_client.h"
#include <cassert> #include <cassert>
#include <iostream>
#include "boost/lexical_cast.hpp"
#include "csoap/http_client.h" #include "csoap/http_client.h"
#include "csoap/http_request.h" #include "csoap/http_request.h"
@ -33,17 +30,17 @@ Error SoapClient::Call(const std::string& operation,
soap_request.AddParameter(parameters[i]); soap_request.AddParameter(parameters[i]);
} }
std::string http_request_body; std::string http_content;
soap_request.ToXml(&http_request_body); soap_request.ToXml(&http_content);
HttpRequest http_request; HttpRequest http_request;
http_request.set_version(kHttpV11); // TODO http_request.SetURL(url_);
http_request.set_url(url_); http_request.SetContentType(kTextXmlUtf8);
http_request.set_content_length(http_request_body.size()); http_request.SetContentLength(http_content.size());
http_request.set_content(http_request_body); // TODO: move http_request.SetHost(host_, port_);
http_request.set_host(host_, port_); http_request.SetHeader(kSOAPAction, operation);
http_request.set_soap_action(operation); http_request.set_content(std::move(http_content));
HttpResponse http_response; HttpResponse http_response;

@ -3,7 +3,6 @@
#include <string> #include <string>
#include "pugixml/pugixml.hpp" #include "pugixml/pugixml.hpp"
#include "csoap/common.h" #include "csoap/common.h"
namespace csoap { namespace csoap {
@ -12,27 +11,27 @@ namespace csoap {
class SoapMessage { class SoapMessage {
public: public:
// E.g., set as kSoapEnvNamespace. // 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; 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; service_ns_ = service_ns;
} }
SERVER_API const std::string& operation() const { const std::string& operation() const {
return operation_; return operation_;
} }
CLIENT_API void set_operation(const std::string& operation) { void set_operation(const std::string& operation) {
operation_ = operation; operation_ = operation;
} }
// Convert to SOAP request XML. // Convert to SOAP request XML.
CLIENT_API void ToXml(std::string* xml_string); void ToXml(std::string* xml_string);
// Parse from SOAP request XML. // Parse from SOAP request XML.
SERVER_API bool FromXml(const std::string& xml_string); bool FromXml(const std::string& xml_string);
protected: protected:
SoapMessage() { SoapMessage() {

@ -2,7 +2,6 @@
#define CSOAP_SOAP_REQUEST_H_ #define CSOAP_SOAP_REQUEST_H_
#include <vector> #include <vector>
#include "csoap/soap_message.h" #include "csoap/soap_message.h"
namespace csoap { namespace csoap {
@ -12,11 +11,11 @@ namespace csoap {
// request body. // request body.
class SoapRequest : public SoapMessage { class SoapRequest : public SoapMessage {
public: public:
CLIENT_API void AddParameter(const std::string& key, const std::string& value); void AddParameter(const std::string& key, const std::string& value);
CLIENT_API void AddParameter(const Parameter& parameter); void AddParameter(const Parameter& parameter);
// Get parameter value by key. // Get parameter value by key.
SERVER_API std::string GetParameter(const std::string& key) const; std::string GetParameter(const std::string& key) const;
protected: protected:
void ToXmlBody(pugi::xml_node xbody) override; void ToXmlBody(pugi::xml_node xbody) override;

@ -11,15 +11,15 @@ public:
// Could be "Price" for an operation/method like "GetXyzPrice". // Could be "Price" for an operation/method like "GetXyzPrice".
// Really depend on the service. // Really depend on the service.
// Most services use a general name "Result". // 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; result_name_ = result_name;
} }
CLIENT_API const std::string& result() const { const std::string& result() const {
return result_; return result_;
} }
SERVER_API void set_result(const std::string& result) { void set_result(const std::string& result) {
result_ = result; result_ = result;
} }
@ -29,7 +29,9 @@ protected:
bool FromXmlBody(pugi::xml_node xbody) override; bool FromXmlBody(pugi::xml_node xbody) override;
private: 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. // Result XML node name.
// Used to parse the response XML from client side. // Used to parse the response XML from client side.

@ -1 +0,0 @@
#include "csoap/soap_service.h"

@ -19,7 +19,7 @@ public:
} }
// Handle SOAP request, output the response. // Handle SOAP request, output the response.
virtual bool Handle(const SoapRequest& request, virtual bool Handle(const SoapRequest& soap_request,
SoapResponse* soap_response) = 0; SoapResponse* soap_response) = 0;
protected: protected:

@ -65,7 +65,7 @@ std::string GetNSAttr(pugi::xml_node& xnode,
// ... // ...
// std::string xml_string; // std::string xml_string;
// XmlStrRefWriter writer(&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 { class XmlStrRefWriter : public pugi::xml_writer {
public: public:
explicit XmlStrRefWriter(std::string* result) explicit XmlStrRefWriter(std::string* result)

@ -2,28 +2,28 @@
#include "csoap/http_server.h" #include "csoap/http_server.h"
#include "calculator_service.h" #include "calculator_service.h"
// TODO: Why need an address for a server?
static void PrintHelp(const char* argv0) { static void PrintHelp(const char* argv0) {
std::cout << "Usage: " << argv0 << " <address> <port>" << std::endl; std::cout << "Usage: " << argv0 << " <port>" << std::endl;
std::cout << " For IPv4, try:" << std::endl; std::cout << " E.g.," << std::endl;
std::cout << " " << argv0 << " 0.0.0.0 80" << std::endl; std::cout << " " << argv0 << " 8080" << std::endl;
std::cout << " For IPv6, try:" << std::endl;
std::cout << " " << argv0 << " 0::0 80" << std::endl;
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
if (argc != 3) { if (argc != 2) {
PrintHelp(argv[0]); PrintHelp(argv[0]);
return 1; return 1;
} }
const char* host = "0.0.0.0"; // TODO
try { try {
csoap::HttpServer server(argv[1], argv[2]); csoap::HttpServer server(host, argv[1]);
csoap::SoapServicePtr service(new CalculatorService); csoap::SoapServicePtr service(new CalculatorService);
server.RegisterService(service); server.RegisterService(service);
server.Run(); server.Run();
} catch (std::exception& e) { } catch (std::exception& e) {
std::cerr << "exception: " << e.what() << "\n"; std::cerr << "exception: " << e.what() << "\n";
return 1; return 1;

Loading…
Cancel
Save