Many improvements (fix start line parsing; remove keep-alive; refine demo; ...
This commit is contained in:
@@ -4,6 +4,39 @@ namespace csoap {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const char* GetErrorMessage(ErrorCode error_code) {
|
||||
switch (error_code) {
|
||||
case kHostResolveError:
|
||||
return "Cannot resolve the host.";
|
||||
|
||||
case kEndpointConnectError:
|
||||
return "Cannot connect to remote endpoint.";
|
||||
|
||||
case kSocketReadError:
|
||||
return "Socket read error.";
|
||||
|
||||
case kSocketWriteError:
|
||||
return "Socket write error.";
|
||||
|
||||
case kHttpStartLineError:
|
||||
return "[HTTP Response] Start line is invalid.";
|
||||
|
||||
case kHttpStatusError:
|
||||
return "[HTTP Response] Status is not OK.";
|
||||
|
||||
case kHttpContentLengthError:
|
||||
return "[HTTP Response] Content-Length is invalid or missing.";
|
||||
|
||||
case kXmlError:
|
||||
return "XML error";
|
||||
|
||||
default:
|
||||
return "No error";
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Parameter::Parameter(const std::string& key, const std::string& value)
|
||||
: key_(key) {
|
||||
value_ = LexicalCast<std::string>(value, "");
|
||||
|
||||
@@ -25,6 +25,32 @@ To LexicalCast(const From& input, const To& default_output) {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum ErrorCode {
|
||||
kNoError = 0, // OK
|
||||
|
||||
kHostResolveError,
|
||||
kEndpointConnectError,
|
||||
|
||||
kSocketReadError,
|
||||
kSocketWriteError,
|
||||
|
||||
// Invalid start line in the HTTP response.
|
||||
kHttpStartLineError,
|
||||
|
||||
// Status is not 200 in the HTTP response.
|
||||
kHttpStatusError,
|
||||
|
||||
// Invalid or missing Content-Length in the HTTP response.
|
||||
kHttpContentLengthError,
|
||||
|
||||
kXmlError,
|
||||
};
|
||||
|
||||
// Return a descriptive message for the given error code.
|
||||
const char* GetErrorMessage(ErrorCode error_code);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// XML namespace name/url pair.
|
||||
// E.g., { "soapenv", "http://schemas.xmlsoap.org/soap/envelope/" }
|
||||
class Namespace {
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@
|
||||
// Include all csoap headers.
|
||||
|
||||
#include "csoap/http_client.h"
|
||||
#include "csoap/soap_request_envelope.h"
|
||||
#include "csoap/soap_response_parser.h"
|
||||
#include "csoap/soap_request.h"
|
||||
#include "csoap/soap_response.h"
|
||||
#include "csoap/xml.h"
|
||||
|
||||
#endif // CSOAP_CSOAP_H_
|
||||
|
||||
+102
-64
@@ -14,16 +14,22 @@ static const std::string kCRLF = "\r\n";
|
||||
// NOTE:
|
||||
// Each header field consists of a name followed by a colon (":") and the
|
||||
// field value. Field names are case-insensitive.
|
||||
// See http://stackoverflow.com/questions/5258977/are-http-headers-case-sensitive
|
||||
// See https://stackoverflow.com/a/5259004
|
||||
static const std::string kFieldContentTypeName = "Content-Type";
|
||||
static const std::string kFieldContentLengthName = "Content-Length";
|
||||
|
||||
static const size_t kInvalidContentLength = std::string::npos;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// NOTE (About Connection: keep-alive):
|
||||
// Keep-alive is deprecated and no longer documented in the current HTTP/1.1
|
||||
// specification.
|
||||
// See https://stackoverflow.com/a/43451440
|
||||
|
||||
HttpRequest::HttpRequest(HttpVersion version)
|
||||
: version_(version)
|
||||
, keep_alive_(true)
|
||||
, content_length_(std::string::npos) {
|
||||
: version_(version)
|
||||
, content_length_(0) {
|
||||
}
|
||||
|
||||
void HttpRequest::ToString(std::string& req_string) const {
|
||||
@@ -42,13 +48,17 @@ void HttpRequest::ToString(std::string& req_string) const {
|
||||
|
||||
// Header fields
|
||||
|
||||
req_string += kFieldContentTypeName;
|
||||
req_string += ": ";
|
||||
|
||||
if (!content_type_.empty()) {
|
||||
req_string += kFieldContentTypeName;
|
||||
req_string += ": ";
|
||||
req_string += content_type_;
|
||||
req_string += kCRLF;
|
||||
} else {
|
||||
req_string += "text/xml; charset=utf-8";
|
||||
}
|
||||
|
||||
req_string += kCRLF;
|
||||
|
||||
req_string += kFieldContentLengthName;
|
||||
req_string += ": ";
|
||||
req_string += LexicalCast<std::string>(content_length_, "0");
|
||||
@@ -66,25 +76,20 @@ void HttpRequest::ToString(std::string& req_string) const {
|
||||
}
|
||||
req_string += kCRLF;
|
||||
|
||||
if (keep_alive_) {
|
||||
req_string += "Connection: Keep-Alive";
|
||||
req_string += kCRLF;
|
||||
}
|
||||
|
||||
req_string += kCRLF;
|
||||
req_string += kCRLF; // End of Headers.
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
HttpResponse::HttpResponse()
|
||||
: status_(0)
|
||||
, content_length_(0)
|
||||
, content_length_(kInvalidContentLength)
|
||||
, start_line_parsed_(false)
|
||||
, header_parsed_(false)
|
||||
, finished_(false) {
|
||||
}
|
||||
|
||||
void HttpResponse::Parse(const char* data, size_t len) {
|
||||
ErrorCode HttpResponse::Parse(const char* data, size_t len) {
|
||||
if (header_parsed_) {
|
||||
// Add the data to the content.
|
||||
content_.append(data, len);
|
||||
@@ -92,10 +97,9 @@ void HttpResponse::Parse(const char* data, size_t len) {
|
||||
if (content_.length() >= content_length_) {
|
||||
// All content has been read.
|
||||
finished_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
pending_data_.append(data, len);
|
||||
@@ -108,7 +112,7 @@ void HttpResponse::Parse(const char* data, size_t len) {
|
||||
}
|
||||
|
||||
if (pos == off) { // End of headers.
|
||||
off = pos + 2; // Skip "\r\n".
|
||||
off = pos + 2; // Skip CRLF.
|
||||
header_parsed_ = true;
|
||||
break;
|
||||
}
|
||||
@@ -116,17 +120,30 @@ void HttpResponse::Parse(const char* data, size_t len) {
|
||||
std::string line = pending_data_.substr(off, pos - off);
|
||||
|
||||
if (!start_line_parsed_) {
|
||||
ParseStartLine(line); // TODO: Error handling.
|
||||
start_line_parsed_ = true;
|
||||
ErrorCode error = ParseStartLine(line);
|
||||
if (error != kNoError) {
|
||||
return error;
|
||||
}
|
||||
} else {
|
||||
ParseHeaderField(line);
|
||||
// Currently, only Content-Length is important to us.
|
||||
// Other fields are ignored.
|
||||
if (content_length_ == kInvalidContentLength) { // Not parsed yet.
|
||||
ParseContentLength(line);
|
||||
}
|
||||
}
|
||||
|
||||
off = pos + 2; // Skip "\r\n".
|
||||
off = pos + 2; // Skip CRLF.
|
||||
}
|
||||
|
||||
if (header_parsed_) {
|
||||
// Headers just ended.
|
||||
|
||||
if (content_length_ == kInvalidContentLength) {
|
||||
// No Content-Length?
|
||||
return kHttpContentLengthError;
|
||||
}
|
||||
|
||||
content_ += pending_data_.substr(off);
|
||||
|
||||
if (content_.length() >= content_length_) {
|
||||
@@ -137,55 +154,68 @@ void HttpResponse::Parse(const char* data, size_t len) {
|
||||
// Save the unparsed piece for next parsing.
|
||||
pending_data_ = pending_data_.substr(off);
|
||||
}
|
||||
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
bool HttpResponse::ParseStartLine(const std::string& line) {
|
||||
std::vector<std::string> parts;
|
||||
boost::split(parts, line, boost::is_any_of(" "), boost::token_compress_on);
|
||||
ErrorCode HttpResponse::ParseStartLine(const std::string& line) {
|
||||
size_t off = 0;
|
||||
|
||||
if (parts.size() != 3) {
|
||||
return false;
|
||||
size_t pos = line.find(' ');
|
||||
if (pos == std::string::npos) {
|
||||
return kHttpStartLineError;
|
||||
}
|
||||
|
||||
// HTTP version
|
||||
|
||||
off = pos + 1; // Skip space.
|
||||
|
||||
pos = line.find(' ', off);
|
||||
if (pos == std::string::npos) {
|
||||
return kHttpStartLineError;
|
||||
}
|
||||
|
||||
// Status code
|
||||
std::string status_str = line.substr(off, pos - off);
|
||||
|
||||
try {
|
||||
status_ = boost::lexical_cast<int>(parts[1]);
|
||||
status_ = boost::lexical_cast<int>(status_str);
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
return false;
|
||||
return kHttpStartLineError;
|
||||
}
|
||||
|
||||
reason_ = parts[2];
|
||||
off = pos + 1; // Skip space.
|
||||
reason_ = line.substr(off);
|
||||
|
||||
return true;
|
||||
if (status_ != kHttpOK) {
|
||||
return kHttpStatusError;
|
||||
}
|
||||
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
bool HttpResponse::ParseHeaderField(const std::string& line) {
|
||||
void HttpResponse::ParseContentLength(const std::string& line) {
|
||||
size_t pos = line.find(':');
|
||||
if (pos == std::string::npos) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string name = line.substr(0, pos);
|
||||
|
||||
++pos; // Skip ':'.
|
||||
while (line[pos] == ' ') { // Skip spaces.
|
||||
++pos;
|
||||
}
|
||||
if (boost::iequals(name, kFieldContentLengthName)) {
|
||||
++pos; // Skip ':'.
|
||||
while (line[pos] == ' ') { // Skip spaces.
|
||||
++pos;
|
||||
}
|
||||
|
||||
std::string value = line.substr(pos);
|
||||
std::string value = line.substr(pos);
|
||||
|
||||
if (boost::iequals(name, kFieldContentTypeName)) {
|
||||
content_type_ = value;
|
||||
} else if (boost::iequals(name, kFieldContentLengthName)) {
|
||||
try {
|
||||
content_length_ = boost::lexical_cast<size_t>(value);
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
// TODO
|
||||
}
|
||||
} else {
|
||||
// Unsupported, ignore.
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -193,11 +223,11 @@ bool HttpResponse::ParseHeaderField(const std::string& line) {
|
||||
HttpClient::HttpClient() {
|
||||
}
|
||||
|
||||
HttpClient::~HttpClient() {
|
||||
}
|
||||
ErrorCode HttpClient::SendRequest(const HttpRequest& request,
|
||||
const std::string& body,
|
||||
HttpResponse* response) {
|
||||
assert(response != NULL);
|
||||
|
||||
bool HttpClient::SendRequest(const HttpRequest& request,
|
||||
const std::string& body) {
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
tcp::socket socket(io_service_);
|
||||
@@ -211,17 +241,17 @@ bool HttpClient::SendRequest(const HttpRequest& request,
|
||||
|
||||
tcp::resolver::query query(request.host(), port);
|
||||
|
||||
boost::system::error_code error;
|
||||
tcp::resolver::iterator it = resolver.resolve(query, error);
|
||||
boost::system::error_code ec;
|
||||
tcp::resolver::iterator it = resolver.resolve(query, ec);
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
if (ec) {
|
||||
return kHostResolveError;
|
||||
}
|
||||
|
||||
socket.connect(*it, error);
|
||||
socket.connect(*it, ec);
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
if (ec) {
|
||||
return kEndpointConnectError;
|
||||
}
|
||||
|
||||
std::string request_str;
|
||||
@@ -231,23 +261,31 @@ bool HttpClient::SendRequest(const HttpRequest& request,
|
||||
boost::asio::write(socket, boost::asio::buffer(request_str));
|
||||
boost::asio::write(socket, boost::asio::buffer(body));
|
||||
} catch (boost::system::system_error&) {
|
||||
return false;
|
||||
return kSocketWriteError;
|
||||
}
|
||||
|
||||
// Read and parse HTTP response.
|
||||
|
||||
while (!response_.finished()) {
|
||||
try {
|
||||
size_t len = socket.read_some(boost::asio::buffer(bytes_));
|
||||
response_.Parse(bytes_.data(), len);
|
||||
// We must stop trying to read some once all content has been received,
|
||||
// because some servers will block extra call to read_some().
|
||||
while (!response->finished()) {
|
||||
size_t len = socket.read_some(boost::asio::buffer(bytes_), ec);
|
||||
|
||||
} catch (boost::system::system_error&) {
|
||||
// Should be EOF, but ...
|
||||
break;
|
||||
if (len == 0 || ec) {
|
||||
return kSocketReadError;
|
||||
}
|
||||
|
||||
// Parse the response piece just read.
|
||||
// If the content has been fully received, next time flag "finished_" will
|
||||
// be set.
|
||||
ErrorCode error = response->Parse(bytes_.data(), len);
|
||||
|
||||
if (error != kNoError) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
+25
-37
@@ -3,15 +3,7 @@
|
||||
|
||||
#include <string>
|
||||
#include "boost/asio.hpp"
|
||||
|
||||
// A little concept about URL (From HTTP The Definitive Guide):
|
||||
// Say you want to fetch the URL http://www.joes-hardware.com/seasonal/index-fall.html:
|
||||
// - The first part of the URL(http) is the URL scheme. The scheme tells a web client
|
||||
// *how* to access the resource. In this case, the URL says to use the HTTP protocol.
|
||||
// - The second part of the URL (www.joes-hardware.com) is the server location.
|
||||
// This tells the web client *where* the resource is hosted.
|
||||
// - The third part of the URL(/seasonal/index-fall.html) is the resource path. The
|
||||
// path tells *what* particular local resource on the server is being requested.
|
||||
#include "csoap/common.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
@@ -22,9 +14,10 @@ enum HttpVersion {
|
||||
kHttpV11,
|
||||
};
|
||||
|
||||
//enum HttpStatus {
|
||||
// kHttpOK = 200,
|
||||
//};
|
||||
enum HttpStatus {
|
||||
kHttpOK = 200,
|
||||
kHttpNotFound = 404,
|
||||
};
|
||||
|
||||
enum HeaderField {
|
||||
kHeaderContentType,
|
||||
@@ -37,15 +30,21 @@ enum HeaderField {
|
||||
// HTTP request.
|
||||
// NOTE:
|
||||
// - Only POST method is supported.
|
||||
// See http://stackoverflow.com/questions/26339317/do-soap-web-services-support-only-post-http-method
|
||||
// See https://stackoverflow.com/a/26339467
|
||||
class HttpRequest {
|
||||
public:
|
||||
HttpRequest(HttpVersion version);
|
||||
|
||||
void set_uri(const std::string& uri) {
|
||||
url_ = uri;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Default: "text/xml; charset=utf-8"
|
||||
void set_content_type(const std::string& content_type) {
|
||||
content_type_ = content_type;
|
||||
}
|
||||
@@ -54,10 +53,6 @@ public:
|
||||
content_length_ = content_length;
|
||||
}
|
||||
|
||||
void set_keep_alive(bool keep_alive) {
|
||||
keep_alive_ = keep_alive;
|
||||
}
|
||||
|
||||
const std::string& host() const {
|
||||
return host_;
|
||||
}
|
||||
@@ -67,7 +62,7 @@ public:
|
||||
}
|
||||
|
||||
// \param host Descriptive host name or numeric IP address.
|
||||
// \param port Numeric port number.
|
||||
// \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;
|
||||
@@ -84,7 +79,8 @@ private:
|
||||
HttpVersion version_;
|
||||
|
||||
// Request URL.
|
||||
// A complete URL naming the requested resource, or the path component of the URL.
|
||||
// A complete URL naming the requested resource, or the path component of
|
||||
// the URL.
|
||||
std::string url_;
|
||||
|
||||
std::string content_type_;
|
||||
@@ -93,8 +89,6 @@ private:
|
||||
std::string host_;
|
||||
std::string port_;
|
||||
|
||||
bool keep_alive_;
|
||||
|
||||
std::string soap_action_;
|
||||
};
|
||||
|
||||
@@ -104,7 +98,7 @@ class HttpResponse {
|
||||
public:
|
||||
HttpResponse();
|
||||
|
||||
void Parse(const char* data, size_t len);
|
||||
ErrorCode Parse(const char* data, size_t len);
|
||||
|
||||
bool finished() const {
|
||||
return finished_;
|
||||
@@ -124,18 +118,18 @@ public:
|
||||
|
||||
private:
|
||||
// Parse start line, e.g., "HTTP/1.1 200 OK".
|
||||
bool ParseStartLine(const std::string& line);
|
||||
ErrorCode ParseStartLine(const std::string& line);
|
||||
|
||||
// Parse a header line, e.g., "Content-Length: 19".
|
||||
bool ParseHeaderField(const std::string& line);
|
||||
void ParseContentLength(const std::string& line);
|
||||
|
||||
private:
|
||||
int status_; // HTTP status, e.g., 200.
|
||||
std::string reason_;
|
||||
std::string content_type_;
|
||||
size_t content_length_;
|
||||
std::string content_;
|
||||
|
||||
ErrorCode error_;
|
||||
|
||||
// Data waiting to be parsed.
|
||||
std::string pending_data_;
|
||||
|
||||
@@ -150,20 +144,14 @@ private:
|
||||
class HttpClient {
|
||||
public:
|
||||
HttpClient();
|
||||
~HttpClient();
|
||||
|
||||
bool SendRequest(const HttpRequest& request,
|
||||
const std::string& body);
|
||||
|
||||
const HttpResponse& response() const {
|
||||
return response_;
|
||||
}
|
||||
ErrorCode SendRequest(const HttpRequest& request,
|
||||
const std::string& body,
|
||||
HttpResponse* response);
|
||||
|
||||
private:
|
||||
boost::asio::io_service io_service_;
|
||||
std::array<char, 1024> bytes_;
|
||||
|
||||
HttpResponse response_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "csoap/soap_request.h"
|
||||
#include "csoap/xml.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Append "xmlns" attribute.
|
||||
static void AppendAttrNS(pugi::xml_node& xnode, const Namespace& ns) {
|
||||
xml::AppendAttr(xnode, "xmlns", ns.name, ns.url);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SoapRequest::SoapRequest(const std::string& operation)
|
||||
: operation_(operation) {
|
||||
soapenv_ns_.name = "soapenv";
|
||||
soapenv_ns_.url = "http://schemas.xmlsoap.org/soap/envelope/";
|
||||
}
|
||||
|
||||
void SoapRequest::AddParameter(const std::string& key,
|
||||
const std::string& value) {
|
||||
parameters_.push_back(Parameter(key, value));
|
||||
}
|
||||
|
||||
void SoapRequest::AddParameter(const Parameter& parameter) {
|
||||
parameters_.push_back(parameter);
|
||||
}
|
||||
|
||||
void SoapRequest::ToXmlString(std::string* xml_string) {
|
||||
pugi::xml_document xdoc;
|
||||
pugi::xml_node xroot = xml::AppendChild(xdoc, soapenv_ns_.name, "Envelope");
|
||||
|
||||
AppendAttrNS(xroot, soapenv_ns_);
|
||||
AppendAttrNS(xroot, service_ns_);
|
||||
|
||||
xml::AppendChild(xroot, soapenv_ns_.name, "Header");
|
||||
|
||||
pugi::xml_node xbody = xml::AppendChild(xroot, soapenv_ns_.name, "Body");
|
||||
pugi::xml_node xop = xml::AppendChild(xbody, service_ns_.name, operation_);
|
||||
|
||||
for (Parameter& p : parameters_) {
|
||||
pugi::xml_node xparam = xml::AppendChild(xop, service_ns_.name, p.c_key());
|
||||
xparam.text().set(p.c_value());
|
||||
}
|
||||
|
||||
xml::XmlStrRefWriter writer(xml_string);
|
||||
xdoc.print(writer, "\t", pugi::format_default, pugi::encoding_utf8);
|
||||
}
|
||||
|
||||
} // namespace csoap
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef CSOAP_SOAP_REQUEST_H_
|
||||
#define CSOAP_SOAP_REQUEST_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "csoap/common.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
// SOAP request.
|
||||
// Used to compose the SOAP request envelope XML which will be sent as the HTTP
|
||||
// request body.
|
||||
class SoapRequest {
|
||||
public:
|
||||
explicit SoapRequest(const std::string& operation);
|
||||
|
||||
// Set the name of SOAP envelope namespace if you don't like the default
|
||||
// name "soapenv".
|
||||
void set_soapenv_ns_name(const std::string& name) {
|
||||
soapenv_ns_.name = name;
|
||||
}
|
||||
|
||||
void set_service_ns(const Namespace& ns) {
|
||||
service_ns_ = ns;
|
||||
}
|
||||
|
||||
void AddParameter(const std::string& key, const std::string& value);
|
||||
void AddParameter(const Parameter& parameter);
|
||||
|
||||
void ToXmlString(std::string* xml_string);
|
||||
|
||||
private:
|
||||
// SOAP envelope namespace.
|
||||
// The URL is always "http://schemas.xmlsoap.org/soap/envelope/".
|
||||
// The name is "soapenv" by default.
|
||||
Namespace soapenv_ns_;
|
||||
|
||||
// Namespace for your web service.
|
||||
Namespace service_ns_;
|
||||
|
||||
std::string operation_;
|
||||
std::vector<Parameter> parameters_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_SOAP_REQUEST_H_
|
||||
@@ -1,67 +0,0 @@
|
||||
#include "csoap/soap_request_envelope.h"
|
||||
|
||||
#include "csoap/xml.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Append "xmlns" attribute.
|
||||
static void AppendAttrNS(pugi::xml_node& xnode, const Namespace& ns) {
|
||||
xml::AppendAttr(xnode, "xmlns", ns.name, ns.url);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SoapRequestEnvelope::SoapRequestEnvelope(const std::string& operation)
|
||||
: operation_(operation) {
|
||||
}
|
||||
|
||||
void SoapRequestEnvelope::SetNamespace(NSType ns_type, const Namespace& ns) {
|
||||
assert(ns_type < kCountNS);
|
||||
namespaces_[ns_type] = ns;
|
||||
}
|
||||
|
||||
void SoapRequestEnvelope::SetNamespace(NSType ns_type,
|
||||
const std::string& name,
|
||||
const std::string& url) {
|
||||
assert(ns_type < kCountNS);
|
||||
|
||||
namespaces_[ns_type].name = name;
|
||||
namespaces_[ns_type].url = url;
|
||||
}
|
||||
|
||||
void SoapRequestEnvelope::AddParameter(const std::string& key,
|
||||
const std::string& value) {
|
||||
parameters_.push_back(Parameter(key, value));
|
||||
}
|
||||
|
||||
void SoapRequestEnvelope::AddParameter(const Parameter& parameter) {
|
||||
parameters_.push_back(parameter);
|
||||
}
|
||||
|
||||
void SoapRequestEnvelope::ToXmlString(std::string* xml_string) {
|
||||
std::string& soapenv_ns = namespaces_[kSoapEnvelopeNS].name;
|
||||
std::string& srv_ns = namespaces_[kServiceNS].name;
|
||||
|
||||
pugi::xml_document xdoc;
|
||||
pugi::xml_node xroot = xml::AppendChild(xdoc, soapenv_ns, "Envelope");
|
||||
|
||||
AppendAttrNS(xroot, namespaces_[kSoapEnvelopeNS]);
|
||||
AppendAttrNS(xroot, namespaces_[kServiceNS]);
|
||||
|
||||
xml::AppendChild(xroot, soapenv_ns, "Header");
|
||||
|
||||
pugi::xml_node xbody = xml::AppendChild(xroot, soapenv_ns, "Body");
|
||||
pugi::xml_node xoperation = xml::AppendChild(xbody, srv_ns, operation_);
|
||||
|
||||
for (Parameter& p : parameters_) {
|
||||
pugi::xml_node xparam = xml::AppendChild(xoperation, srv_ns, p.c_key());
|
||||
xparam.text().set(p.c_value());
|
||||
}
|
||||
|
||||
xml::XmlStrRefWriter writer(xml_string);
|
||||
xdoc.print(writer, "\t", pugi::format_default, pugi::encoding_utf8);
|
||||
}
|
||||
|
||||
} // namespace csoap
|
||||
@@ -1,45 +0,0 @@
|
||||
#ifndef CSOAP_SOAP_REQUEST_ENVELOPE_H_
|
||||
#define CSOAP_SOAP_REQUEST_ENVELOPE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "csoap/common.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
// SOAP request envelope.
|
||||
// Used to compose the SOAP envelope XML which will be sent as the HTTP
|
||||
// request body.
|
||||
class SoapRequestEnvelope {
|
||||
public:
|
||||
enum NSType {
|
||||
kSoapEnvelopeNS = 0,
|
||||
kServiceNS,
|
||||
kCountNS,
|
||||
};
|
||||
|
||||
public:
|
||||
explicit SoapRequestEnvelope(const std::string& operation);
|
||||
|
||||
void SetNamespace(NSType ns_type, const Namespace& ns);
|
||||
|
||||
void SetNamespace(NSType ns_type,
|
||||
const std::string& name,
|
||||
const std::string& url);
|
||||
|
||||
void AddParameter(const std::string& key, const std::string& value);
|
||||
void AddParameter(const Parameter& parameter);
|
||||
|
||||
void ToXmlString(std::string* xml_string);
|
||||
|
||||
private:
|
||||
Namespace namespaces_[kCountNS];
|
||||
|
||||
std::string operation_;
|
||||
std::vector<Parameter> parameters_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_SOAP_REQUEST_ENVELOPE_H_
|
||||
@@ -1,16 +1,16 @@
|
||||
#include "csoap/soap_response_parser.h"
|
||||
#include "csoap/soap_response.h"
|
||||
|
||||
#include "csoap/xml.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
SoapResponseParser::SoapResponseParser() {
|
||||
SoapResponse::SoapResponse() {
|
||||
}
|
||||
|
||||
bool SoapResponseParser::Parse(const std::string& content,
|
||||
const std::string& message_name,
|
||||
const std::string& element_name,
|
||||
std::string* element_value) {
|
||||
bool SoapResponse::Parse(const std::string& content,
|
||||
const std::string& message_name,
|
||||
const std::string& element_name,
|
||||
std::string* element_value) {
|
||||
pugi::xml_document xdoc;
|
||||
pugi::xml_parse_result result = xdoc.load_string(content.c_str());
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
#ifndef CSOAP_RESPONSE_PARSER_H_
|
||||
#define CSOAP_RESPONSE_PARSER_H_
|
||||
#ifndef CSOAP_RESPONSE_H_
|
||||
#define CSOAP_RESPONSE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace csoap {
|
||||
|
||||
class SoapResponseParser {
|
||||
// SOAP response.
|
||||
// Used to parse the SOAP response XML which is returned as the HTTP response
|
||||
// body.
|
||||
class SoapResponse {
|
||||
public:
|
||||
SoapResponseParser();
|
||||
|
||||
// <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
// <soapenv:Body>
|
||||
// <ns:getPatientResponse xmlns:ns="http://service.csdm.carestream.com">
|
||||
// <ns:return> ...
|
||||
SoapResponse();
|
||||
|
||||
bool Parse(const std::string& content,
|
||||
const std::string& message_name,
|
||||
const std::string& element_name,
|
||||
@@ -29,4 +28,4 @@ private:
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_RESPONSE_PARSER_H_
|
||||
#endif // CSOAP_RESPONSE_H_
|
||||
Reference in New Issue
Block a user