Support to create soap server. (draft)
parent
7b5f4f69b7
commit
692df1b937
@ -1,8 +1,5 @@
|
||||
if(CSOAP_USE_TINYXML)
|
||||
add_subdirectory(tinyxml)
|
||||
else()
|
||||
add_subdirectory(pugixml)
|
||||
endif()
|
||||
|
||||
add_subdirectory(pugixml)
|
||||
add_subdirectory(csoap)
|
||||
add_subdirectory(demo)
|
||||
add_subdirectory(demo/calculator_client)
|
||||
add_subdirectory(demo/calculator_server)
|
||||
add_subdirectory(demo/csdm_client)
|
||||
|
@ -0,0 +1,90 @@
|
||||
#include "csoap/connection.h"
|
||||
#include <vector>
|
||||
|
||||
#include "boost/asio/write.hpp"
|
||||
|
||||
#include "csoap/connection_manager.h"
|
||||
#include "csoap/http_request_handler.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
Connection::Connection(boost::asio::ip::tcp::socket socket,
|
||||
ConnectionManager& manager,
|
||||
HttpRequestHandler& handler)
|
||||
: socket_(std::move(socket))
|
||||
, connection_manager_(manager)
|
||||
, request_handler_(handler)
|
||||
, request_parser_(&request_) {
|
||||
}
|
||||
|
||||
void Connection::Start() {
|
||||
DoRead();
|
||||
}
|
||||
|
||||
void Connection::Stop() {
|
||||
socket_.close();
|
||||
}
|
||||
|
||||
void Connection::DoRead() {
|
||||
auto handler = std::bind(&Connection::HandleRead,
|
||||
shared_from_this(),
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2);
|
||||
socket_.async_read_some(boost::asio::buffer(buffer_), handler);
|
||||
}
|
||||
|
||||
void Connection::DoWrite() {
|
||||
auto handler = std::bind(&Connection::HandleWrite,
|
||||
shared_from_this(),
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2);
|
||||
boost::asio::async_write(socket_, response_.ToBuffers(), handler);
|
||||
}
|
||||
|
||||
void Connection::HandleRead(boost::system::error_code ec,
|
||||
std::size_t bytes_transferred) {
|
||||
if (ec) {
|
||||
if (ec != boost::asio::error::operation_aborted) {
|
||||
connection_manager_.Stop(shared_from_this());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ErrorCode error = request_parser_.Parse(buffer_.data(), bytes_transferred);
|
||||
|
||||
if (error != kNoError) {
|
||||
// Bad request.
|
||||
response_ = HttpResponse::Fault(HttpStatus::BAD_REQUEST);
|
||||
DoWrite();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!request_parser_.finished()) {
|
||||
// Continue to read the request.
|
||||
DoRead();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle request.
|
||||
request_handler_.HandleRequest(request_, response_);
|
||||
|
||||
// Send back the response.
|
||||
DoWrite();
|
||||
}
|
||||
|
||||
void Connection::HandleWrite(boost::system::error_code ec,
|
||||
size_t bytes_transferred) {
|
||||
if (!ec) {
|
||||
// Initiate graceful connection closure.
|
||||
boost::system::error_code ignored_ec;
|
||||
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both,
|
||||
ignored_ec);
|
||||
}
|
||||
|
||||
if (ec != boost::asio::error::operation_aborted) {
|
||||
connection_manager_.Stop(shared_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace csoap
|
@ -0,0 +1,76 @@
|
||||
#ifndef CSOAP_CONNECTION_H_
|
||||
#define CSOAP_CONNECTION_H_
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "boost/asio/ip/tcp.hpp" // for ip::tcp::socket
|
||||
|
||||
#include "csoap/common.h"
|
||||
#include "csoap/http_request.h"
|
||||
#include "csoap/http_request_parser.h"
|
||||
#include "csoap/http_response.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
class ConnectionManager;
|
||||
class HttpRequestHandler;
|
||||
|
||||
// Represents a single connection from a client.
|
||||
class Connection : public std::enable_shared_from_this<Connection> {
|
||||
public:
|
||||
Connection(const Connection&) = delete;
|
||||
Connection& operator=(const Connection&) = delete;
|
||||
|
||||
// Construct a connection with the given io_service.
|
||||
Connection(boost::asio::ip::tcp::socket socket,
|
||||
ConnectionManager& manager,
|
||||
HttpRequestHandler& handler);
|
||||
|
||||
// Start the first asynchronous operation for the connection.
|
||||
void Start();
|
||||
|
||||
// Stop all asynchronous operations associated with the connection.
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
void DoRead();
|
||||
|
||||
void DoWrite();
|
||||
|
||||
// Handle completion of a read operation.
|
||||
void HandleRead(boost::system::error_code ec,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
// Handle completion of a write operation.
|
||||
void HandleWrite(boost::system::error_code ec,
|
||||
size_t bytes_transferred);
|
||||
|
||||
private:
|
||||
// Socket for the connection.
|
||||
boost::asio::ip::tcp::socket socket_;
|
||||
|
||||
// The manager for this connection.
|
||||
ConnectionManager& connection_manager_;
|
||||
|
||||
// The handler used to process the incoming request.
|
||||
HttpRequestHandler& request_handler_;
|
||||
|
||||
// Buffer for incoming data.
|
||||
std::array<char, 8192> buffer_;
|
||||
|
||||
// The incoming request.
|
||||
HttpRequest request_;
|
||||
|
||||
// The parser for the incoming request.
|
||||
HttpRequestParser request_parser_;
|
||||
|
||||
// The reply to be sent back to the client.
|
||||
HttpResponse response_;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Connection> ConnectionPtr;
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_CONNECTION_H_
|
@ -0,0 +1,25 @@
|
||||
#include "csoap/connection_manager.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
ConnectionManager::ConnectionManager() {
|
||||
}
|
||||
|
||||
void ConnectionManager::Start(ConnectionPtr conn) {
|
||||
connections_.insert(conn);
|
||||
conn->Start();
|
||||
}
|
||||
|
||||
void ConnectionManager::Stop(ConnectionPtr conn) {
|
||||
connections_.erase(conn);
|
||||
conn->Stop();
|
||||
}
|
||||
|
||||
void ConnectionManager::StopAll() {
|
||||
for (const ConnectionPtr& conn : connections_) {
|
||||
conn->Stop();
|
||||
}
|
||||
connections_.clear();
|
||||
}
|
||||
|
||||
} // namespace csoap
|
@ -0,0 +1,35 @@
|
||||
#ifndef CSOAP_CONNECTION_MANAGER_H_
|
||||
#define CSOAP_CONNECTION_MANAGER_H_
|
||||
|
||||
#include <set>
|
||||
#include "csoap/connection.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
// ConnectionManager manages open connections so that they may be cleanly
|
||||
// stopped when the server needs to shut down.
|
||||
class ConnectionManager {
|
||||
public:
|
||||
ConnectionManager(const ConnectionManager&) = delete;
|
||||
ConnectionManager& operator=(const ConnectionManager&) = delete;
|
||||
|
||||
// Construct a connection manager.
|
||||
ConnectionManager();
|
||||
|
||||
// Add the specified connection to the manager and start it.
|
||||
void Start(ConnectionPtr conn);
|
||||
|
||||
// Stop the specified connection.
|
||||
void Stop(ConnectionPtr conn);
|
||||
|
||||
// Stop all connections.
|
||||
void StopAll();
|
||||
|
||||
private:
|
||||
// The managed connections.
|
||||
std::set<ConnectionPtr> connections_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_CONNECTION_MANAGER_H_
|
@ -0,0 +1,76 @@
|
||||
#ifndef CSOAP_HTTP_MESSAGE_H_
|
||||
#define CSOAP_HTTP_MESSAGE_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include "csoap/common.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
class HttpHeader {
|
||||
public:
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
// Base class for HTTP request and response messages.
|
||||
class HttpMessage {
|
||||
public:
|
||||
void set_version(HttpVersion version) {
|
||||
version_ = version;
|
||||
}
|
||||
|
||||
size_t content_length() const {
|
||||
return content_length_;
|
||||
}
|
||||
void set_content_length(size_t length) {
|
||||
content_length_ = length;
|
||||
}
|
||||
|
||||
// E.g., "text/xml; charset=utf-8"
|
||||
void set_content_type(const std::string& content_type) {
|
||||
content_type_ = content_type;
|
||||
}
|
||||
|
||||
void AddHeader(const std::string& name, const std::string& value) {
|
||||
headers_.push_back({ name, value });
|
||||
}
|
||||
|
||||
const std::string& content() const {
|
||||
return content_;
|
||||
}
|
||||
void set_content(const std::string& content) {
|
||||
content_ = content;
|
||||
}
|
||||
|
||||
void AppendContent(const char* data, size_t count) {
|
||||
content_.append(data, count);
|
||||
}
|
||||
|
||||
void AppendContent(const std::string& data) {
|
||||
content_.append(data);
|
||||
}
|
||||
|
||||
bool IsContentFull() const {
|
||||
assert(content_length_ != kInvalidLength);
|
||||
return content_.length() >= content_length_;
|
||||
}
|
||||
|
||||
protected:
|
||||
HttpMessage() {
|
||||
}
|
||||
|
||||
protected:
|
||||
HttpVersion version_ = kHttpV11;
|
||||
|
||||
size_t content_length_ = kInvalidLength;
|
||||
std::string content_type_;
|
||||
|
||||
std::vector<HttpHeader> headers_;
|
||||
|
||||
std::string content_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_HTTP_MESSAGE_H_
|
@ -0,0 +1,115 @@
|
||||
#include "csoap/http_parser.h"
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include "boost/lexical_cast.hpp"
|
||||
|
||||
#include "csoap/http_message.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
HttpParser::HttpParser(HttpMessage* message)
|
||||
: message_(message)
|
||||
, start_line_parsed_(false)
|
||||
, header_parsed_(false)
|
||||
, finished_(false) {
|
||||
}
|
||||
|
||||
void HttpParser::Reset() {
|
||||
// TODO: Reset parsing state.
|
||||
}
|
||||
|
||||
ErrorCode HttpParser::Parse(const char* data, size_t len) {
|
||||
if (header_parsed_) {
|
||||
// Add the data to the content.
|
||||
message_->AppendContent(data, len);
|
||||
|
||||
if (message_->IsContentFull()) {
|
||||
// All content has been read.
|
||||
finished_ = true;
|
||||
}
|
||||
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
pending_data_.append(data, len);
|
||||
size_t off = 0;
|
||||
|
||||
while (true) {
|
||||
size_t pos = pending_data_.find(kCRLF, off);
|
||||
if (pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos == off) { // End of headers.
|
||||
off = pos + 2; // Skip CRLF.
|
||||
header_parsed_ = true;
|
||||
break;
|
||||
}
|
||||
|
||||
std::string line = pending_data_.substr(off, pos - off);
|
||||
|
||||
if (!start_line_parsed_) {
|
||||
start_line_parsed_ = true;
|
||||
ErrorCode error = ParseStartLine(line);
|
||||
if (error != kNoError) {
|
||||
return error;
|
||||
}
|
||||
} else {
|
||||
// Currently, only Content-Length is important to us.
|
||||
// Other fields are ignored.
|
||||
if (message_->content_length() == kInvalidLength) {
|
||||
// Not parsed yet.
|
||||
ParseContentLength(line);
|
||||
}
|
||||
}
|
||||
|
||||
off = pos + 2; // Skip CRLF.
|
||||
}
|
||||
|
||||
if (header_parsed_) {
|
||||
// Headers just ended.
|
||||
|
||||
if (message_->content_length() == kInvalidLength) {
|
||||
// No Content-Length?
|
||||
return kHttpContentLengthError;
|
||||
}
|
||||
|
||||
message_->AppendContent(pending_data_.substr(off));
|
||||
|
||||
if (message_->IsContentFull()) {
|
||||
// All content has been read.
|
||||
finished_ = true;
|
||||
}
|
||||
} else {
|
||||
// Save the unparsed piece for next parsing.
|
||||
pending_data_ = pending_data_.substr(off);
|
||||
}
|
||||
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
void HttpParser::ParseContentLength(const std::string& line) {
|
||||
size_t pos = line.find(':');
|
||||
if (pos == std::string::npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string name = line.substr(0, pos);
|
||||
|
||||
if (boost::iequals(name, kContentLengthName)) {
|
||||
++pos; // Skip ':'.
|
||||
while (line[pos] == ' ') { // Skip spaces.
|
||||
++pos;
|
||||
}
|
||||
|
||||
std::string value = line.substr(pos);
|
||||
|
||||
try {
|
||||
message_->set_content_length(boost::lexical_cast<size_t>(value));
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace csoap
|
@ -0,0 +1,49 @@
|
||||
#ifndef CSOAP_HTTP_PARSER_H_
|
||||
#define CSOAP_HTTP_PARSER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "csoap/common.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
class HttpMessage;
|
||||
|
||||
// HttpParser parses HTTP request and response.
|
||||
class HttpParser {
|
||||
public:
|
||||
explicit HttpParser(HttpMessage* message);
|
||||
|
||||
bool finished() const {
|
||||
return finished_;
|
||||
}
|
||||
|
||||
// Reset parsing state.
|
||||
void Reset();
|
||||
|
||||
ErrorCode Parse(const char* data, size_t len);
|
||||
|
||||
protected:
|
||||
// Parse HTTP start line.
|
||||
virtual ErrorCode ParseStartLine(const std::string& line) = 0;
|
||||
|
||||
void ParseContentLength(const std::string& line);
|
||||
|
||||
protected:
|
||||
// The result HTTP message.
|
||||
HttpMessage* message_;
|
||||
|
||||
ErrorCode error_;
|
||||
|
||||
// Data waiting to be parsed.
|
||||
std::string pending_data_;
|
||||
|
||||
// Parsing helper flags.
|
||||
bool start_line_parsed_;
|
||||
bool header_parsed_;
|
||||
bool finished_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_HTTP_PARSER_H_
|
@ -0,0 +1,66 @@
|
||||
#include "csoap/http_request.h"
|
||||
|
||||
#include "boost/algorithm/string.hpp"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const HttpRequest& request) {
|
||||
return os << request.GetHeaders() << request.content();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string HttpRequest::GetHeaders() const {
|
||||
std::string headers;
|
||||
|
||||
// Start line
|
||||
|
||||
headers += "POST ";
|
||||
headers += url_;
|
||||
headers += " ";
|
||||
|
||||
if (version_ == kHttpV10) {
|
||||
headers += "HTTP/1.0";
|
||||
} else {
|
||||
headers += "HTTP/1.1";
|
||||
}
|
||||
headers += kCRLF;
|
||||
|
||||
// Header fields
|
||||
|
||||
headers += kContentTypeName;
|
||||
headers += ": ";
|
||||
|
||||
if (!content_type_.empty()) {
|
||||
headers += content_type_;
|
||||
} else {
|
||||
headers += kTextXmlUtf8;
|
||||
}
|
||||
|
||||
headers += kCRLF;
|
||||
|
||||
headers += kContentLengthName;
|
||||
headers += ": ";
|
||||
headers += std::to_string(content_length_);
|
||||
headers += kCRLF;
|
||||
|
||||
headers += "SOAPAction: ";
|
||||
headers += soap_action_;
|
||||
headers += kCRLF;
|
||||
|
||||
headers += "Host: ";
|
||||
headers += host_;
|
||||
if (!port_.empty()) {
|
||||
headers += ":";
|
||||
headers += port_;
|
||||
}
|
||||
headers += kCRLF;
|
||||
|
||||
headers += kCRLF; // End of Headers.
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
} // namespace csoap
|
@ -0,0 +1,78 @@
|
||||
#ifndef CSOAP_HTTP_REQUEST_H_
|
||||
#define CSOAP_HTTP_REQUEST_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "csoap/common.h"
|
||||
#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.
|
||||
// See https://stackoverflow.com/a/26339467
|
||||
//
|
||||
class HttpRequest : public HttpMessage {
|
||||
friend std::ostream& operator<<(std::ostream& os,
|
||||
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_;
|
||||
}
|
||||
|
||||
const std::string& port() const {
|
||||
return port_;
|
||||
}
|
||||
|
||||
// \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;
|
||||
}
|
||||
|
||||
// SOAP specific.
|
||||
void set_soap_action(const std::string& soap_action) {
|
||||
soap_action_ = soap_action;
|
||||
}
|
||||
|
||||
std::string GetHeaders() const;
|
||||
|
||||
private:
|
||||
// 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_;
|
||||
|
||||
std::string soap_action_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_HTTP_REQUEST_H_
|
@ -0,0 +1,110 @@
|
||||
#include "csoap/http_request_handler.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "csoap/common.h"
|
||||
#include "csoap/http_request.h"
|
||||
#include "csoap/http_response.h"
|
||||
#include "csoap/soap_request.h"
|
||||
#include "csoap/soap_response.h"
|
||||
#include "csoap/soap_service.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
#if 0
|
||||
// Perform URL-decoding on a string. Returns false if the encoding was invalid.
|
||||
static bool UrlDecode(const std::string& in, std::string& out) {
|
||||
out.clear();
|
||||
out.reserve(in.size());
|
||||
|
||||
for (std::size_t i = 0; i < in.size(); ++i) {
|
||||
if (in[i] == '%') {
|
||||
if (i + 3 <= in.size()) {
|
||||
int value = 0;
|
||||
std::istringstream is(in.substr(i + 1, 2));
|
||||
if (is >> std::hex >> value) {
|
||||
out += static_cast<char>(value);
|
||||
i += 2;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (in[i] == '+') {
|
||||
out += ' ';
|
||||
} else {
|
||||
out += in[i];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
HttpRequestHandler::HttpRequestHandler() {
|
||||
}
|
||||
|
||||
bool HttpRequestHandler::RegisterService(SoapServicePtr soap_service) {
|
||||
assert(soap_service);
|
||||
|
||||
if (std::find(soap_services_.begin(), soap_services_.end(), soap_service) !=
|
||||
soap_services_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
soap_services_.push_back(soap_service);
|
||||
return true;
|
||||
}
|
||||
|
||||
void HttpRequestHandler::HandleRequest(const HttpRequest& request,
|
||||
HttpResponse& response) {
|
||||
// Parse the SOAP request XML.
|
||||
SoapRequest soap_request;
|
||||
if (!soap_request.FromXml(request.content())) {
|
||||
// TODO: Bad request
|
||||
return;
|
||||
}
|
||||
|
||||
// TEST
|
||||
|
||||
SoapResponse soap_response;
|
||||
|
||||
// Get service by URL.
|
||||
|
||||
for (SoapServicePtr& service : soap_services_) {
|
||||
service->Handle(soap_request, &soap_response);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
#if 0
|
||||
// Decode URL to path.
|
||||
std::string request_path;
|
||||
if (!UrlDecode(request.uri, request_path)) {
|
||||
reply = HttpReply::StockReply(HttpReply::BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
// Request path must be absolute and not contain "..".
|
||||
if (request_path.empty() ||
|
||||
request_path[0] != '/' ||
|
||||
request_path.find("..") != std::string::npos) {
|
||||
reply = HttpReply::StockReply(HttpReply::BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
// If path ends in slash (i.e. is a directory) then add "index.html".
|
||||
if (request_path[request_path.size() - 1] == '/') {
|
||||
request_path += "index.html";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace csoap
|
@ -0,0 +1,33 @@
|
||||
#ifndef CSOAP_HTTP_REQUEST_HANDLER_H_
|
||||
#define CSOAP_HTTP_REQUEST_HANDLER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "csoap/soap_service.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
class HttpRequest;
|
||||
class HttpResponse;
|
||||
|
||||
// The common handler for all incoming requests.
|
||||
class HttpRequestHandler {
|
||||
public:
|
||||
HttpRequestHandler(const HttpRequestHandler&) = delete;
|
||||
HttpRequestHandler& operator=(const HttpRequestHandler&) = delete;
|
||||
|
||||
HttpRequestHandler();
|
||||
|
||||
bool RegisterService(SoapServicePtr soap_service);
|
||||
|
||||
// Handle a request and produce a reply.
|
||||
void HandleRequest(const HttpRequest& request, HttpResponse& response);
|
||||
|
||||
private:
|
||||
std::vector<SoapServicePtr> soap_services_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_HTTP_REQUEST_HANDLER_H_
|
@ -0,0 +1,37 @@
|
||||
#include "csoap/http_request_parser.h"
|
||||
|
||||
#include <vector>
|
||||
#include "boost/algorithm/string.hpp"
|
||||
|
||||
#include "csoap/http_request.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
HttpRequestParser::HttpRequestParser(HttpRequest* request)
|
||||
: HttpParser(request)
|
||||
, request_(request) {
|
||||
}
|
||||
|
||||
ErrorCode HttpRequestParser::ParseStartLine(const std::string& line) {
|
||||
// Example: POST / HTTP/1.1
|
||||
|
||||
std::vector<std::string> strs;
|
||||
boost::split(strs, line, boost::is_any_of(" "));
|
||||
|
||||
if (strs.size() != 3) {
|
||||
return kHttpStartLineError;
|
||||
}
|
||||
|
||||
if (strs[0] != "POST") {
|
||||
// Only POST method is supported.
|
||||
return kHttpStartLineError;
|
||||
}
|
||||
|
||||
request_->set_url(strs[1]);
|
||||
|
||||
// TODO: strs[2];
|
||||
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
} // namespace csoap
|
@ -0,0 +1,23 @@
|
||||
#ifndef CSOAP_HTTP_REQUEST_PARSER_H_
|
||||
#define CSOAP_HTTP_REQUEST_PARSER_H_
|
||||
|
||||
#include "csoap/http_parser.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
class HttpRequest;
|
||||
|
||||
class HttpRequestParser : public HttpParser {
|
||||
public:
|
||||
explicit HttpRequestParser(HttpRequest* request);
|
||||
|
||||
private:
|
||||
ErrorCode ParseStartLine(const std::string& line) override;
|
||||
|
||||
private:
|
||||
HttpRequest* request_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_HTTP_REQUEST_PARSER_H_
|
@ -0,0 +1,132 @@
|
||||
#include "csoap/http_response.h"
|
||||
|
||||
#include "csoap/common.h"
|
||||
#include "csoap/xml.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const HttpResponse& response) {
|
||||
// TODO
|
||||
os << response.status() << std::endl;
|
||||
|
||||
// Pretty print the SOAP response XML.
|
||||
if (!xml::PrettyPrintXml(os, response.content())) {
|
||||
os << response.content();
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace status_strings {
|
||||
|
||||
const std::string OK = "HTTP/1.1 200 OK\r\n";
|
||||
const std::string BAD_REQUEST = "HTTP/1.1 400 Bad Request\r\n";
|
||||
const std::string INTERNAL_SERVER_ERROR = "HTTP/1.1 500 Internal Server Error\r\n";
|
||||
const std::string NOT_IMPLEMENTED = "HTTP/1.1 501 Not Implemented\r\n";
|
||||
const std::string SERVICE_UNAVAILABLE = "HTTP/1.1 503 Service Unavailable\r\n";
|
||||
|
||||
boost::asio::const_buffer ToBuffer(int status) {
|
||||
switch (status) {
|
||||
case HttpStatus::OK:
|
||||
return boost::asio::buffer(OK);
|
||||
|
||||
case HttpStatus::BAD_REQUEST:
|
||||
return boost::asio::buffer(BAD_REQUEST);
|
||||
|
||||
case HttpStatus::INTERNAL_SERVER_ERROR:
|
||||
return boost::asio::buffer(INTERNAL_SERVER_ERROR);
|
||||
|
||||
case HttpStatus::NOT_IMPLEMENTED:
|
||||
return boost::asio::buffer(NOT_IMPLEMENTED);
|
||||
|
||||
case HttpStatus::SERVICE_UNAVAILABLE:
|
||||
return boost::asio::buffer(SERVICE_UNAVAILABLE);
|
||||
|
||||
default:
|
||||
return boost::asio::buffer(SERVICE_UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace status_strings
|
||||
|
||||
namespace misc_strings {
|
||||
|
||||
const char NAME_VALUE_SEPARATOR[] = { ':', ' ' };
|
||||
const char CRLF[] = { '\r', '\n' };
|
||||
|
||||
} // misc_strings
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// ATTENTION: The buffers don't hold the memory!
|
||||
std::vector<boost::asio::const_buffer> HttpResponse::ToBuffers() const {
|
||||
std::vector<boost::asio::const_buffer> buffers;
|
||||
|
||||
buffers.push_back(status_strings::ToBuffer(status_));
|
||||
|
||||
// Header fields
|
||||
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));
|
||||
}
|
||||
|
||||
buffers.push_back(boost::asio::buffer(misc_strings::CRLF));
|
||||
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?
|
||||
}
|
||||
|
||||
} // namespace csoap
|
@ -0,0 +1,47 @@
|
||||
#ifndef CSOAP_HTTP_RESPONSE_H_
|
||||
#define CSOAP_HTTP_RESPONSE_H_
|
||||
|
||||
#include <string>
|
||||
#include "boost/asio/buffer.hpp" // for const_buffer
|
||||
#include "csoap/http_message.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class HttpResponse;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const HttpResponse& response);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class HttpResponse : public HttpMessage {
|
||||
friend std::ostream& operator<<(std::ostream& os,
|
||||
const HttpResponse& response);
|
||||
|
||||
public:
|
||||
HttpResponse() {
|
||||
}
|
||||
|
||||
int status() const {
|
||||
return status_;
|
||||
}
|
||||
void set_status(int status) {
|
||||
status_ = status;
|
||||
}
|
||||
|
||||
// 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<boost::asio::const_buffer> ToBuffers() const;
|
||||
|
||||
// Get a fault response when HTTP status is not OK.
|
||||
static HttpResponse Fault(HttpStatus status);
|
||||
|
||||
private:
|
||||
int status_ = HttpStatus::OK; // TODO: HttpStatus
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_HTTP_RESPONSE_H_
|
@ -0,0 +1,49 @@
|
||||
#include "csoap/http_response_parser.h"
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "csoap/http_response.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
HttpResponseParser::HttpResponseParser(HttpResponse* response)
|
||||
: HttpParser(response)
|
||||
, response_(response) {
|
||||
}
|
||||
|
||||
// TODO: Use split.
|
||||
ErrorCode HttpResponseParser::ParseStartLine(const std::string& line) {
|
||||
size_t off = 0;
|
||||
|
||||
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 {
|
||||
response_->set_status(boost::lexical_cast<int>(status_str));
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
return kHttpStartLineError;
|
||||
}
|
||||
|
||||
off = pos + 1; // Skip space.
|
||||
//response_->set_reason(line.substr(off));
|
||||
|
||||
if (response_->status() != HttpStatus::OK) {
|
||||
return kHttpStatusError;
|
||||
}
|
||||
|
||||
return kNoError;
|
||||
}
|
||||
|
||||
} // namespace csoap
|
@ -0,0 +1,25 @@
|
||||
#ifndef CSOAP_HTTP_RESPONSE_PARSER_H_
|
||||
#define CSOAP_HTTP_RESPONSE_PARSER_H_
|
||||
|
||||
#include "csoap/http_parser.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
class HttpResponse;
|
||||
|
||||
class HttpResponseParser : public HttpParser {
|
||||
public:
|
||||
explicit HttpResponseParser(HttpResponse* response);
|
||||
|
||||
private:
|
||||
// Parse HTTP start line; E.g., "HTTP/1.1 200 OK".
|
||||
ErrorCode ParseStartLine(const std::string& line) override;
|
||||
|
||||
private:
|
||||
// The result response message.
|
||||
HttpResponse* response_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_HTTP_RESPONSE_PARSER_H_
|
@ -0,0 +1,83 @@
|
||||
#include "csoap/http_server.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include "csoap/soap_service.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
HttpServer::HttpServer(const std::string& address,
|
||||
const std::string& port)
|
||||
: io_context_(1) // TODO: concurrency_hint (threads)
|
||||
, signals_(io_context_)
|
||||
, acceptor_(io_context_) {
|
||||
|
||||
// Register to handle the signals that indicate when the server should exit.
|
||||
// It is safe to register for the same signal multiple times in a program,
|
||||
// provided all registration for the specified signal is made through asio.
|
||||
// TODO: Verify if this works for Windows.
|
||||
signals_.add(SIGINT);
|
||||
signals_.add(SIGTERM);
|
||||
#if defined(SIGQUIT)
|
||||
signals_.add(SIGQUIT);
|
||||
#endif
|
||||
|
||||
DoAwaitStop();
|
||||
|
||||
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
|
||||
// TODO: What does SO_REUSEADDR mean?
|
||||
// TODO: Why need an address?
|
||||
boost::asio::ip::tcp::resolver resolver(io_context_);
|
||||
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(address, port).begin();
|
||||
acceptor_.open(endpoint.protocol());
|
||||
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
|
||||
acceptor_.bind(endpoint);
|
||||
acceptor_.listen();
|
||||
|
||||
DoAccept();
|
||||
}
|
||||
|
||||
bool HttpServer::RegisterService(SoapServicePtr soap_service) {
|
||||
return request_handler_.RegisterService(soap_service);
|
||||
}
|
||||
|
||||
void HttpServer::Run() {
|
||||
// The io_context::run() call will block until all asynchronous operations
|
||||
// have finished. While the server is running, there is always at least one
|
||||
// asynchronous operation outstanding: the asynchronous accept call waiting
|
||||
// for new incoming connections.
|
||||
io_context_.run();
|
||||
}
|
||||
|
||||
void HttpServer::DoAccept() {
|
||||
acceptor_.async_accept(
|
||||
[this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) {
|
||||
// Check whether the server was stopped by a signal before this
|
||||
// completion handler had a chance to run.
|
||||
if (!acceptor_.is_open()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ec) {
|
||||
connection_manager_.Start(
|
||||
std::make_shared<Connection>(std::move(socket),
|
||||
connection_manager_,
|
||||
request_handler_));
|
||||
}
|
||||
|
||||
DoAccept();
|
||||
});
|
||||
}
|
||||
|
||||
void HttpServer::DoAwaitStop() {
|
||||
signals_.async_wait(
|
||||
[this](boost::system::error_code /*ec*/, int /*signo*/) {
|
||||
// The server is stopped by cancelling all outstanding asynchronous
|
||||
// operations. Once all operations have finished the io_context::run()
|
||||
// call will exit.
|
||||
acceptor_.close();
|
||||
connection_manager_.StopAll();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace csoap
|
@ -0,0 +1,59 @@
|
||||
#ifndef CSOAP_HTTP_SERVER_H_
|
||||
#define CSOAP_HTTP_SERVER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "boost/asio/io_context.hpp"
|
||||
#include "boost/asio/signal_set.hpp"
|
||||
#include "boost/asio/ip/tcp.hpp"
|
||||
|
||||
#include "csoap/connection.h"
|
||||
#include "csoap/connection_manager.h"
|
||||
#include "csoap/http_request_handler.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
// The top-level class of the HTTP server.
|
||||
class HttpServer {
|
||||
public:
|
||||
HttpServer(const HttpServer&) = delete;
|
||||
HttpServer& operator=(const HttpServer&) = delete;
|
||||
|
||||
// Construct the server to listen on the specified TCP address and port, and
|
||||
// serve up files from the given directory.
|
||||
HttpServer(const std::string& address,
|
||||
const std::string& port);
|
||||
|
||||
bool RegisterService(SoapServicePtr soap_service);
|
||||
|
||||
// Run the server's io_service loop.
|
||||
void Run();
|
||||
|
||||
private:
|
||||
// Initiate an asynchronous accept operation.
|
||||
void DoAccept();
|
||||
|
||||
// Wait for a request to stop the server.
|
||||
void DoAwaitStop();
|
||||
|
||||
private:
|
||||
// The io_context used to perform asynchronous operations.
|
||||
boost::asio::io_context io_context_;
|
||||
|
||||
// The signal_set is used to register for process termination notifications.
|
||||
boost::asio::signal_set signals_;
|
||||
|
||||
// Acceptor used to listen for incoming connections.
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
|
||||
// The connection manager which owns all live connections.
|
||||
ConnectionManager connection_manager_;
|
||||
|
||||
// The handler for all incoming requests.
|
||||
HttpRequestHandler request_handler_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_HTTP_SERVER_H_
|
@ -0,0 +1,75 @@
|
||||
#include "csoap/soap_client.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
|
||||
#include "csoap/http_client.h"
|
||||
#include "csoap/http_request.h"
|
||||
#include "csoap/http_response.h"
|
||||
#include "csoap/soap_request.h"
|
||||
#include "csoap/soap_response.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
bool SoapClient::Call(const std::string& operation,
|
||||
const csoap::Parameter* parameters,
|
||||
size_t count,
|
||||
std::string* result) {
|
||||
assert(!url_.empty() &&
|
||||
!host_.empty() &&
|
||||
!result_name_.empty() &&
|
||||
service_ns_.IsValid());
|
||||
|
||||
csoap::SoapRequest soap_request;
|
||||
|
||||
soap_request.set_soapenv_ns(kSoapEnvNamespace);
|
||||
soap_request.set_service_ns(service_ns_);
|
||||
|
||||
soap_request.set_operation(operation);
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
soap_request.AddParameter(parameters[i]);
|
||||
}
|
||||
|
||||
std::string http_request_body;
|
||||
soap_request.ToXml(&http_request_body);
|
||||
|
||||
csoap::HttpRequest http_request;
|
||||
|
||||
http_request.set_version(csoap::kHttpV11);
|
||||
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);
|
||||
|
||||
csoap::HttpResponse http_response;
|
||||
|
||||
csoap::HttpClient http_client;
|
||||
csoap::ErrorCode ec = http_client.SendRequest(http_request, &http_response);
|
||||
if (ec != csoap::kNoError) {
|
||||
std::cerr << csoap::GetErrorMessage(ec) << std::endl;
|
||||
|
||||
if (ec == csoap::kHttpStatusError) {
|
||||
//std::cerr << "\t"
|
||||
// << http_response.status() << ", "
|
||||
// << http_response.reason() << std::endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
csoap::SoapResponse soap_response;
|
||||
soap_response.set_result_name(result_name_);
|
||||
|
||||
if (soap_response.FromXml(http_response.content())) {
|
||||
*result = soap_response.result();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace csoap
|
@ -0,0 +1,46 @@
|
||||
#ifndef CSOAP_SOAP_CLIENT_H_
|
||||
#define CSOAP_SOAP_CLIENT_H_
|
||||
|
||||
#include <string>
|
||||
#include "csoap/common.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
// Base class for your SOAP client.
|
||||
// Set URL, host, port, etc. in your sub-class before make the call.
|
||||
//
|
||||
class SoapClient {
|
||||
public:
|
||||
virtual ~SoapClient() {
|
||||
}
|
||||
|
||||
protected:
|
||||
SoapClient() {
|
||||
}
|
||||
|
||||
// A generic wrapper to make a call.
|
||||
bool Call(const std::string& operation,
|
||||
const csoap::Parameter* parameters,
|
||||
size_t count,
|
||||
std::string* result);
|
||||
|
||||
protected:
|
||||
// Request URL.
|
||||
// Could be a complete URL (http://ws1.parasoft.com/glue/calculator)
|
||||
// or just the path component of it (/glue/calculator).
|
||||
std::string url_;
|
||||
|
||||
std::string host_;
|
||||
std::string port_; // Leave this empty to use default 80.
|
||||
|
||||
// The namespace of your service.
|
||||
csoap::Namespace service_ns_;
|
||||
|
||||
// Response result XML node name.
|
||||
// E.g., "Result".
|
||||
std::string result_name_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_SOAP_CLIENT_H_
|
@ -0,0 +1,54 @@
|
||||
#include "csoap/soap_message.h"
|
||||
|
||||
#include <cassert>
|
||||
#include "csoap/xml.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
void SoapMessage::ToXml(std::string* xml_string) {
|
||||
assert(soapenv_ns_.IsValid() &&
|
||||
service_ns_.IsValid() &&
|
||||
!operation_.empty());
|
||||
|
||||
pugi::xml_document xdoc;
|
||||
|
||||
// TODO:
|
||||
// When save with format_default, declaration will be generated
|
||||
// automatically but without encoding.
|
||||
// pugi::xml_node xdecl = xdoc.prepend_child(pugi::node_declaration);
|
||||
// xdecl.append_attribute("version").set_value("1.0");
|
||||
|
||||
pugi::xml_node xroot = xml::AddChild(xdoc, soapenv_ns_.name, "Envelope");
|
||||
|
||||
xml::AddNSAttr(xroot, soapenv_ns_.name, soapenv_ns_.url);
|
||||
|
||||
pugi::xml_node xbody = xml::AddChild(xroot, soapenv_ns_.name, "Body");
|
||||
|
||||
ToXmlBody(xbody);
|
||||
|
||||
xml::XmlStrRefWriter writer(xml_string);
|
||||
xdoc.save(writer, "\t", pugi::format_default, pugi::encoding_utf8);
|
||||
}
|
||||
|
||||
bool SoapMessage::FromXml(const std::string& xml_string) {
|
||||
pugi::xml_document xdoc;
|
||||
pugi::xml_parse_result result = xdoc.load_string(xml_string.c_str());
|
||||
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pugi::xml_node xroot = xdoc.document_element();
|
||||
|
||||
soapenv_ns_.name = xml::GetPrefix(xroot);
|
||||
soapenv_ns_.url = xml::GetNSAttr(xroot, soapenv_ns_.name);
|
||||
|
||||
pugi::xml_node xbody = xml::GetChild(xroot, soapenv_ns_.name, "Body");
|
||||
if (xbody) {
|
||||
return FromXmlBody(xbody);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace csoap
|
@ -0,0 +1,56 @@
|
||||
#ifndef CSOAP_SOAP_MESSAGE_H_
|
||||
#define CSOAP_SOAP_MESSAGE_H_
|
||||
|
||||
#include <string>
|
||||
#include "pugixml/pugixml.hpp"
|
||||
|
||||
#include "csoap/common.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
// Base class for SOAP request and response.
|
||||
class SoapMessage {
|
||||
public:
|
||||
// E.g., set as kSoapEnvNamespace.
|
||||
CLIENT_API void set_soapenv_ns(const Namespace& soapenv_ns) {
|
||||
soapenv_ns_ = soapenv_ns;
|
||||
}
|
||||
|
||||
CLIENT_API void set_service_ns(const Namespace& service_ns) {
|
||||
service_ns_ = service_ns;
|
||||
}
|
||||
|
||||
SERVER_API const std::string& operation() const {
|
||||
return operation_;
|
||||
}
|
||||
|
||||
CLIENT_API void set_operation(const std::string& operation) {
|
||||
operation_ = operation;
|
||||
}
|
||||
|
||||
// Convert to SOAP request XML.
|
||||
CLIENT_API void ToXml(std::string* xml_string);
|
||||
|
||||
// Parse from SOAP request XML.
|
||||
SERVER_API bool FromXml(const std::string& xml_string);
|
||||
|
||||
protected:
|
||||
SoapMessage() {
|
||||
}
|
||||
|
||||
// Convert to SOAP body XML.
|
||||
virtual void ToXmlBody(pugi::xml_node xbody) = 0;
|
||||
|
||||
// Parse from SOAP body XML.
|
||||
virtual bool FromXmlBody(pugi::xml_node xbody) = 0;
|
||||
|
||||
protected:
|
||||
Namespace soapenv_ns_; // SOAP envelope namespace.
|
||||
Namespace service_ns_; // Namespace for your web service.
|
||||
|
||||
std::string operation_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_SOAP_MESSAGE_H_
|
@ -1,84 +1,36 @@
|
||||
#include "csoap/soap_response.h"
|
||||
|
||||
#include <cassert>
|
||||
#include "csoap/xml.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
SoapResponse::SoapResponse() {
|
||||
}
|
||||
|
||||
bool SoapResponse::Parse(const std::string& content,
|
||||
const std::string& message_name,
|
||||
const std::string& element_name,
|
||||
std::string* element_value) {
|
||||
#ifdef CSOAP_USE_TINYXML
|
||||
|
||||
TiXmlDocument xdoc;
|
||||
xdoc.Parse(content.c_str());
|
||||
|
||||
if (xdoc.Error()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TiXmlElement* xroot = xdoc.RootElement();
|
||||
|
||||
soapenv_ns_ = xml::GetNsPrefix(xroot);
|
||||
|
||||
TiXmlElement* xbody = xml::GetChild(xroot, soapenv_ns_, "Body");
|
||||
if (xbody == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TiXmlElement* xmessage = xml::GetChildNoNS(xbody, message_name);
|
||||
if (xmessage == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TiXmlElement* xelement = xml::GetChildNoNS(xmessage, element_name);
|
||||
if (xelement == NULL) {
|
||||
return false;
|
||||
}
|
||||
void SoapResponse::ToXmlBody(pugi::xml_node xbody) {
|
||||
std::string rsp_operation = operation_ + "Response";
|
||||
pugi::xml_node xop = xml::AddChild(xbody, service_ns_.name, rsp_operation);
|
||||
xml::AddNSAttr(xop, service_ns_.name, service_ns_.url);
|
||||
|
||||
const char* text = xelement->GetText();
|
||||
if (text != NULL) {
|
||||
*element_value = text;
|
||||
} else {
|
||||
*element_value = "";
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
pugi::xml_document xdoc;
|
||||
pugi::xml_parse_result result = xdoc.load_string(content.c_str());
|
||||
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pugi::xml_node xroot = xdoc.document_element();
|
||||
|
||||
soapenv_ns_ = xml::GetNsPrefix(xroot);
|
||||
// TODO: Leave the user to decide the result name.
|
||||
pugi::xml_node xresult = xml::AddChild(xop, service_ns_.name, "Result");
|
||||
xresult.text().set(result_.c_str());
|
||||
}
|
||||
|
||||
pugi::xml_node xbody = xml::GetChild(xroot, soapenv_ns_, "Body");
|
||||
if (!xbody) {
|
||||
return false;
|
||||
}
|
||||
bool SoapResponse::FromXmlBody(pugi::xml_node xbody) {
|
||||
assert(!result_name_.empty());
|
||||
|
||||
pugi::xml_node xmessage = xml::GetChildNoNS(xbody, message_name);
|
||||
if (!xmessage) {
|
||||
return false;
|
||||
}
|
||||
pugi::xml_node xresponse = xbody.first_child();
|
||||
if (xresponse) {
|
||||
xml::SplitName(xresponse, &service_ns_.name, NULL);
|
||||
service_ns_.url = xml::GetNSAttr(xresponse, service_ns_.name);
|
||||
|
||||
pugi::xml_node xelement = xml::GetChildNoNS(xmessage, element_name);
|
||||
if (!xelement) {
|
||||
return false;
|
||||
pugi::xml_node xresult = xml::GetChildNoNS(xresponse, result_name_);
|
||||
if (xresult) {
|
||||
result_ = xresult.text().get();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*element_value = xelement.text().get();
|
||||
|
||||
#endif // CSOAP_USE_TINYXML
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace csoap
|
||||
|
@ -1,31 +1,44 @@
|
||||
#ifndef CSOAP_RESPONSE_H_
|
||||
#define CSOAP_RESPONSE_H_
|
||||
#ifndef CSOAP_SOAP_RESPONSE_H_
|
||||
#define CSOAP_SOAP_RESPONSE_H_
|
||||
|
||||
#include <string>
|
||||
#include "csoap/soap_message.h"
|
||||
|
||||
namespace csoap {
|
||||
|
||||
// SOAP response.
|
||||
// Used to parse the SOAP response XML which is returned as the HTTP response
|
||||
// body.
|
||||
class SoapResponse {
|
||||
class SoapResponse : public SoapMessage {
|
||||
public:
|
||||
SoapResponse();
|
||||
|
||||
bool Parse(const std::string& content,
|
||||
const std::string& message_name,
|
||||
const std::string& element_name,
|
||||
std::string* element_value);
|
||||
|
||||
const std::string& soapenv_ns() const {
|
||||
return soapenv_ns_;
|
||||
// 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) {
|
||||
result_name_ = result_name;
|
||||
}
|
||||
|
||||
CLIENT_API const std::string& result() const {
|
||||
return result_;
|
||||
}
|
||||
|
||||
SERVER_API void set_result(const std::string& result) {
|
||||
result_ = result;
|
||||
}
|
||||
|
||||
protected:
|
||||
void ToXmlBody(pugi::xml_node xbody) override;
|
||||
|
||||
bool FromXmlBody(pugi::xml_node xbody) override;
|
||||
|
||||
private:
|
||||
// Soap envelope namespace in the response XML.
|
||||
std::string soapenv_ns_;
|
||||
// TODO: Support multiple results.
|
||||
|
||||
// Result XML node name.
|
||||
// Used to parse the response XML from client side.
|
||||
std::string result_name_;
|
||||
|
||||
// Result value.
|
||||
std::string result_;
|
||||
};
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_RESPONSE_H_
|
||||
#endif // CSOAP_SOAP_RESPONSE_H_
|
||||
|
@ -0,0 +1 @@
|
||||
#include "csoap/soap_service.h"
|
@ -0,0 +1,35 @@
|
||||
#ifndef CSOAP_SOAP_SERVICE_H_
|
||||
#define CSOAP_SOAP_SERVICE_H_
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace csoap {
|
||||
|
||||
class SoapRequest;
|
||||
class SoapResponse;
|
||||
|
||||
// Base class for your SOAP service.
|
||||
class SoapService {
|
||||
public:
|
||||
SoapService() {
|
||||
}
|
||||
|
||||
virtual ~SoapService() {
|
||||
}
|
||||
|
||||
// Handle SOAP request, output the response.
|
||||
virtual bool Handle(const SoapRequest& request,
|
||||
SoapResponse* soap_response) = 0;
|
||||
|
||||
protected:
|
||||
// URL used to match the request.
|
||||
// E.g., "/", "/SomeService", etc.
|
||||
std::string url_;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<SoapService> SoapServicePtr;
|
||||
|
||||
} // namespace csoap
|
||||
|
||||
#endif // CSOAP_SOAP_SERVICE_H_
|
@ -1,16 +0,0 @@
|
||||
if(UNIX)
|
||||
add_definitions(-D_GLIBCXX_USE_WCHAR_T -std=c++11)
|
||||
endif()
|
||||
|
||||
file(GLOB SRCS *.cc *.h)
|
||||
|
||||
add_executable(demo ${SRCS})
|
||||
|
||||
if(CSOAP_USE_TINYXML)
|
||||
set(XML_LIB tinyxml)
|
||||
else()
|
||||
set(XML_LIB pugixml)
|
||||
endif()
|
||||
|
||||
target_link_libraries(demo csoap ${XML_LIB})
|
||||
|
@ -1,114 +0,0 @@
|
||||
#include "demo/calculator.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "csoap/csoap.h"
|
||||
|
||||
namespace demo {
|
||||
|
||||
Calculator::Calculator() {
|
||||
Init();
|
||||
}
|
||||
|
||||
bool Calculator::Add(double x, double y, double* result) {
|
||||
return Calc("add", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool Calculator::Subtract(double x, double y, double* result) {
|
||||
return Calc("subtract", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool Calculator::Multiply(double x, double y, double* result) {
|
||||
return Calc("multiply", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool Calculator::Divide(double x, double y, double* result) {
|
||||
return Calc("divide", "numerator", "denominator", x, y, result);
|
||||
}
|
||||
|
||||
void Calculator::Init() {
|
||||
url_ = "/glue/calculator";
|
||||
|
||||
host_ = "ws1.parasoft.com";
|
||||
port_ = ""; // Use default: 80
|
||||
|
||||
service_ns_ = { "ser", "http://www.parasoft.com/wsdl/calculator/" };
|
||||
}
|
||||
|
||||
bool Calculator::Calc(const std::string& operation,
|
||||
const std::string& x_name,
|
||||
const std::string& y_name,
|
||||
double x,
|
||||
double y,
|
||||
double* result) {
|
||||
csoap::Parameter parameters[] = {
|
||||
{ x_name, x },
|
||||
{ y_name, y }
|
||||
};
|
||||
|
||||
std::string result_str;
|
||||
if (!Call(operation, parameters, 2, &result_str)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
*result = boost::lexical_cast<double>(result_str);
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Calculator::Call(const std::string& operation,
|
||||
const csoap::Parameter* parameters,
|
||||
size_t count,
|
||||
std::string* result) {
|
||||
csoap::SoapRequest soap_request(operation);
|
||||
|
||||
soap_request.set_service_ns(service_ns_);
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
soap_request.AddParameter(parameters[i]);
|
||||
}
|
||||
|
||||
std::string http_request_body;
|
||||
soap_request.ToXmlString(&http_request_body);
|
||||
|
||||
csoap::HttpRequest http_request(csoap::kHttpV11);
|
||||
|
||||
http_request.set_url(url_);
|
||||
http_request.set_content_length(http_request_body.size());
|
||||
http_request.set_host(host_, port_);
|
||||
http_request.set_soap_action(operation);
|
||||
|
||||
csoap::HttpResponse http_response;
|
||||
|
||||
csoap::HttpClient http_client;
|
||||
csoap::ErrorCode ec = http_client.SendRequest(http_request,
|
||||
http_request_body,
|
||||
&http_response);
|
||||
if (ec != csoap::kNoError) {
|
||||
std::cerr << csoap::GetErrorMessage(ec) << std::endl;
|
||||
|
||||
if (ec == csoap::kHttpStatusError) {
|
||||
std::cerr << "\t"
|
||||
<< http_response.status() << ", "
|
||||
<< http_response.reason() << std::endl;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
csoap::SoapResponse soap_response;
|
||||
|
||||
std::string rsp_message_name = operation + "Response";
|
||||
std::string rsp_element_name = "Result";
|
||||
|
||||
return soap_response.Parse(http_response.content(),
|
||||
rsp_message_name,
|
||||
rsp_element_name,
|
||||
result);
|
||||
}
|
||||
|
||||
} // namespace demo
|
@ -1,56 +0,0 @@
|
||||
#ifndef DEMO_CALCULATOR_H_
|
||||
#define DEMO_CALCULATOR_H_
|
||||
|
||||
// Wrapper of calculator.wsdl.
|
||||
// See http://ws1.parasoft.com/glue/calculator.wsdl
|
||||
|
||||
#include <string>
|
||||
#include "csoap/common.h"
|
||||
|
||||
namespace demo {
|
||||
|
||||
class Calculator {
|
||||
public:
|
||||
Calculator();
|
||||
|
||||
bool Add(double x, double y, double* result);
|
||||
|
||||
bool Subtract(double x, double y, double* result);
|
||||
|
||||
bool Multiply(double x, double y, double* result);
|
||||
|
||||
bool Divide(double x, double y, double* result);
|
||||
|
||||
protected:
|
||||
void Init();
|
||||
|
||||
// A more concrete wrapper to make a call.
|
||||
bool Calc(const std::string& operation,
|
||||
const std::string& x_name,
|
||||
const std::string& y_name,
|
||||
double x,
|
||||
double y,
|
||||
double* result);
|
||||
|
||||
// A generic wrapper to make a call.
|
||||
bool Call(const std::string& operation,
|
||||
const csoap::Parameter* parameters,
|
||||
size_t count,
|
||||
std::string* result);
|
||||
|
||||
protected:
|
||||
// Request URL.
|
||||
// Could be a complete URL (http://ws1.parasoft.com/glue/calculator)
|
||||
// or just the path component of it (/glue/calculator).
|
||||
std::string url_;
|
||||
|
||||
std::string host_;
|
||||
std::string port_; // Leave this empty to use default 80.
|
||||
|
||||
// The namespace of your service.
|
||||
csoap::Namespace service_ns_;
|
||||
};
|
||||
|
||||
} // namespace demo
|
||||
|
||||
#endif // DEMO_CALCULATOR_H_
|
@ -0,0 +1,9 @@
|
||||
if(UNIX)
|
||||
add_definitions(-D_GLIBCXX_USE_WCHAR_T -std=c++11)
|
||||
endif()
|
||||
|
||||
file(GLOB SRCS *.cc *.h)
|
||||
|
||||
add_executable(calculator_client ${SRCS})
|
||||
|
||||
target_link_libraries(calculator_client csoap pugixml)
|
@ -0,0 +1,69 @@
|
||||
#include "calculator_client.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "csoap/csoap.h"
|
||||
|
||||
CalculatorClient::CalculatorClient() {
|
||||
Init();
|
||||
}
|
||||
|
||||
bool CalculatorClient::Add(double x, double y, double* result) {
|
||||
return Calc("add", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool CalculatorClient::Subtract(double x, double y, double* result) {
|
||||
return Calc("subtract", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool CalculatorClient::Multiply(double x, double y, double* result) {
|
||||
return Calc("multiply", "x", "y", x, y, result);
|
||||
}
|
||||
|
||||
bool CalculatorClient::Divide(double x, double y, double* result) {
|
||||
return Calc("divide", "numerator", "denominator", x, y, result);
|
||||
}
|
||||
|
||||
// Set to 0 to test our own calculator server created with csoap.
|
||||
#define ACCESS_PARASOFT 1
|
||||
|
||||
void CalculatorClient::Init() {
|
||||
#if ACCESS_PARASOFT
|
||||
url_ = "/glue/calculator";
|
||||
host_ = "ws1.parasoft.com";
|
||||
port_ = "80"; // Or leave it empty because 80 is the default HTTP port.
|
||||
service_ns_ = { "ser", "http://www.parasoft.com/wsdl/calculator/" };
|
||||
result_name_ = "Result";
|
||||
#else
|
||||
url_ = "/";
|
||||
host_ = "localhost";
|
||||
port_ = "8080";
|
||||
service_ns_ = { "ser", "http://mycalculator/" };
|
||||
result_name_ = "Result";
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CalculatorClient::Calc(const std::string& operation,
|
||||
const std::string& x_name,
|
||||
const std::string& y_name,
|
||||
double x,
|
||||
double y,
|
||||
double* result) {
|
||||
csoap::Parameter parameters[] = {
|
||||
{ x_name, x },
|
||||
{ y_name, y }
|
||||
};
|
||||
|
||||
std::string result_str;
|
||||
if (!Call(operation, parameters, 2, &result_str)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
*result = boost::lexical_cast<double>(result_str);
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
#ifndef CALCULATOR_CLIENT_H_
|
||||
#define CALCULATOR_CLIENT_H_
|
||||
|
||||
// Wrapper of calculator.wsdl.
|
||||
// See http://ws1.parasoft.com/glue/calculator.wsdl
|
||||
|
||||
#include <string>
|
||||
#include "csoap/soap_client.h"
|
||||
|
||||
class CalculatorClient : csoap::SoapClient {
|
||||
public:
|
||||
CalculatorClient();
|
||||
|
||||
bool Add(double x, double y, double* result);
|
||||
|
||||
bool Subtract(double x, double y, double* result);
|
||||
|
||||
bool Multiply(double x, double y, double* result);
|
||||
|
||||
bool Divide(double x, double y, double* result);
|
||||
|
||||
protected:
|
||||
void Init();
|
||||
|
||||
// A more concrete wrapper to make a call.
|
||||
bool Calc(const std::string& operation,
|
||||
const std::string& x_name,
|
||||
const std::string& y_name,
|
||||
double x,
|
||||
double y,
|
||||
double* result);
|
||||
};
|
||||
|
||||
#endif // CALCULATOR_CLIENT_H_
|
@ -0,0 +1,9 @@
|
||||
if(UNIX)
|
||||
add_definitions(-D_GLIBCXX_USE_WCHAR_T -std=c++11)
|
||||
endif()
|
||||
|
||||
file(GLOB SRCS *.cc *.h)
|
||||
|
||||
add_executable(calculator_server ${SRCS})
|
||||
|
||||
target_link_libraries(calculator_server csoap pugixml)
|
@ -0,0 +1,36 @@
|
||||
#include "calculator_service.h"
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
|
||||
#include "csoap/soap_request.h"
|
||||
#include "csoap/soap_response.h"
|
||||
|
||||
CalculatorService::CalculatorService() {
|
||||
}
|
||||
|
||||
bool CalculatorService::Handle(const csoap::SoapRequest& request,
|
||||
csoap::SoapResponse* response) {
|
||||
try {
|
||||
if (request.operation() == "add") {
|
||||
double x = boost::lexical_cast<double>(request.GetParameter("x"));
|
||||
double y = boost::lexical_cast<double>(request.GetParameter("y"));
|
||||
|
||||
double result = x + y;
|
||||
|
||||
response->set_soapenv_ns(csoap::kSoapEnvNamespace);
|
||||
response->set_service_ns({ "ser", "http://mycalculator/" });
|
||||
response->set_operation(request.operation());
|
||||
response->set_result_name("Result");
|
||||
response->set_result(boost::lexical_cast<std::string>(result));
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// NOT_IMPLEMENTED
|
||||
}
|
||||
} catch (boost::bad_lexical_cast&) {
|
||||
// BAD_REQUEST
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#ifndef CALCULATOR_SERVICE_H_
|
||||
#define CALCULATOR_SERVICE_H_
|
||||
|
||||
#include "csoap/soap_service.h"
|
||||
|
||||
class CalculatorService : public csoap::SoapService {
|
||||
public:
|
||||
CalculatorService();
|
||||
|
||||
bool Handle(const csoap::SoapRequest& request,
|
||||
csoap::SoapResponse* response) override;
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
#endif // CALCULATOR_SERVICE_H_
|
@ -0,0 +1,33 @@
|
||||
#include <iostream>
|
||||
#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 << " <address> <port>" << 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;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 3) {
|
||||
PrintHelp(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
csoap::HttpServer server(argv[1], argv[2]);
|
||||
|
||||
csoap::SoapServicePtr service(new CalculatorService);
|
||||
server.RegisterService(service);
|
||||
|
||||
server.Run();
|
||||
} catch (std::exception& e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
if(UNIX)
|
||||
add_definitions(-D_GLIBCXX_USE_WCHAR_T -std=c++11)
|
||||
endif()
|
||||
|
||||
file(GLOB SRCS *.cc *.h)
|
||||
|
||||
add_executable(csdm_client ${SRCS})
|
||||
|
||||
target_link_libraries(csdm_client csoap pugixml)
|
@ -0,0 +1,35 @@
|
||||
#include "csdm_client.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "csoap/csoap.h"
|
||||
|
||||
CsdmClient::CsdmClient() {
|
||||
Init();
|
||||
}
|
||||
|
||||
bool CsdmClient::GetVersion(std::string* result_xml) {
|
||||
return Call("getVersion", NULL, 0, result_xml);
|
||||
}
|
||||
|
||||
void CsdmClient::Init() {
|
||||
url_ = "/";
|
||||
|
||||
service_ns_ = { "ser", "http://service.csdm.carestream.com/" };
|
||||
|
||||
host_ = "127.0.0.1";
|
||||
port_ = "52540";
|
||||
|
||||
result_name_ = "Result";
|
||||
}
|
||||
|
||||
bool CsdmClient::Call1(const std::string& operation,
|
||||
const std::string& name,
|
||||
const std::string& value,
|
||||
std::string* result_xml) {
|
||||
csoap::Parameter parameters[] = {
|
||||
{ name, value },
|
||||
};
|
||||
|
||||
return Call(operation, parameters, 1, result_xml);
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
#ifndef CSDM_CLIENT_H_
|
||||
#define CSDM_CLIENT_H_
|
||||
|
||||
// CSDM (CS Data Manager) is a project of my business project.
|
||||
|
||||
#include <string>
|
||||
#include "csoap/soap_client.h"
|
||||
|
||||
class CsdmClient : csoap::SoapClient {
|
||||
public:
|
||||
CsdmClient();
|
||||
|
||||
bool GetVersion(std::string* result_xml);
|
||||
|
||||
protected:
|
||||
void Init();
|
||||
|
||||
// Call CSDM API with one parameter.
|
||||
bool Call1(const std::string& operation,
|
||||
const std::string& name,
|
||||
const std::string& value,
|
||||
std::string* result_xml);
|
||||
};
|
||||
|
||||
#endif // CSDM_CLIENT_H_
|
@ -0,0 +1,13 @@
|
||||
#include <iostream>
|
||||
#include "csdm_client.h"
|
||||
|
||||
int main() {
|
||||
CsdmClient csdm_client;
|
||||
|
||||
std::string version;
|
||||
if (csdm_client.GetVersion(&version)) {
|
||||
std::cout << version << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
# TinyXml uses STL.
|
||||
add_definitions(-DTIXML_USE_STL)
|
||||
|
||||
file(GLOB SRCS *.cpp *.h)
|
||||
|
||||
add_library(tinyxml ${SRCS})
|
@ -1,530 +0,0 @@
|
||||
/** @mainpage
|
||||
|
||||
<h1> TinyXML </h1>
|
||||
|
||||
TinyXML is a simple, small, C++ XML parser that can be easily
|
||||
integrated into other programs.
|
||||
|
||||
<h2> What it does. </h2>
|
||||
|
||||
In brief, TinyXML parses an XML document, and builds from that a
|
||||
Document Object Model (DOM) that can be read, modified, and saved.
|
||||
|
||||
XML stands for "eXtensible Markup Language." It allows you to create
|
||||
your own document markups. Where HTML does a very good job of marking
|
||||
documents for browsers, XML allows you to define any kind of document
|
||||
markup, for example a document that describes a "to do" list for an
|
||||
organizer application. XML is a very structured and convenient format.
|
||||
All those random file formats created to store application data can
|
||||
all be replaced with XML. One parser for everything.
|
||||
|
||||
The best place for the complete, correct, and quite frankly hard to
|
||||
read spec is at <a href="http://www.w3.org/TR/2004/REC-xml-20040204/">
|
||||
http://www.w3.org/TR/2004/REC-xml-20040204/</a>. An intro to XML
|
||||
(that I really like) can be found at
|
||||
<a href="http://skew.org/xml/tutorial/">http://skew.org/xml/tutorial</a>.
|
||||
|
||||
There are different ways to access and interact with XML data.
|
||||
TinyXML uses a Document Object Model (DOM), meaning the XML data is parsed
|
||||
into a C++ objects that can be browsed and manipulated, and then
|
||||
written to disk or another output stream. You can also construct an XML document
|
||||
from scratch with C++ objects and write this to disk or another output
|
||||
stream.
|
||||
|
||||
TinyXML is designed to be easy and fast to learn. It is two headers
|
||||
and four cpp files. Simply add these to your project and off you go.
|
||||
There is an example file - xmltest.cpp - to get you started.
|
||||
|
||||
TinyXML is released under the ZLib license,
|
||||
so you can use it in open source or commercial code. The details
|
||||
of the license are at the top of every source file.
|
||||
|
||||
TinyXML attempts to be a flexible parser, but with truly correct and
|
||||
compliant XML output. TinyXML should compile on any reasonably C++
|
||||
compliant system. It does not rely on exceptions or RTTI. It can be
|
||||
compiled with or without STL support. TinyXML fully supports
|
||||
the UTF-8 encoding, and the first 64k character entities.
|
||||
|
||||
|
||||
<h2> What it doesn't do. </h2>
|
||||
|
||||
TinyXML doesn't parse or use DTDs (Document Type Definitions) or XSLs
|
||||
(eXtensible Stylesheet Language.) There are other parsers out there
|
||||
(check out www.sourceforge.org, search for XML) that are much more fully
|
||||
featured. But they are also much bigger, take longer to set up in
|
||||
your project, have a higher learning curve, and often have a more
|
||||
restrictive license. If you are working with browsers or have more
|
||||
complete XML needs, TinyXML is not the parser for you.
|
||||
|
||||
The following DTD syntax will not parse at this time in TinyXML:
|
||||
|
||||
@verbatim
|
||||
<!DOCTYPE Archiv [
|
||||
<!ELEMENT Comment (#PCDATA)>
|
||||
]>
|
||||
@endverbatim
|
||||
|
||||
because TinyXML sees this as a !DOCTYPE node with an illegally
|
||||
embedded !ELEMENT node. This may be addressed in the future.
|
||||
|
||||
<h2> Tutorials. </h2>
|
||||
|
||||
For the impatient, here is a tutorial to get you going. A great way to get started,
|
||||
but it is worth your time to read this (very short) manual completely.
|
||||
|
||||
- @subpage tutorial0
|
||||
|
||||
<h2> Code Status. </h2>
|
||||
|
||||
TinyXML is mature, tested code. It is very stable. If you find
|
||||
bugs, please file a bug report on the sourceforge web site
|
||||
(www.sourceforge.net/projects/tinyxml). We'll get them straightened
|
||||
out as soon as possible.
|
||||
|
||||
There are some areas of improvement; please check sourceforge if you are
|
||||
interested in working on TinyXML.
|
||||
|
||||
<h2> Related Projects </h2>
|
||||
|
||||
TinyXML projects you may find useful! (Descriptions provided by the projects.)
|
||||
|
||||
<ul>
|
||||
<li> <b>TinyXPath</b> (http://tinyxpath.sourceforge.net). TinyXPath is a small footprint
|
||||
XPath syntax decoder, written in C++.</li>
|
||||
<li> <b>TinyXML++</b> (http://code.google.com/p/ticpp/). TinyXML++ is a completely new
|
||||
interface to TinyXML that uses MANY of the C++ strengths. Templates,
|
||||
exceptions, and much better error handling.</li>
|
||||
</ul>
|
||||
|
||||
<h2> Features </h2>
|
||||
|
||||
<h3> Using STL </h3>
|
||||
|
||||
TinyXML can be compiled to use or not use STL. When using STL, TinyXML
|
||||
uses the std::string class, and fully supports std::istream, std::ostream,
|
||||
operator<<, and operator>>. Many API methods have both 'const char*' and
|
||||
'const std::string&' forms.
|
||||
|
||||
When STL support is compiled out, no STL files are included whatsoever. All
|
||||
the string classes are implemented by TinyXML itself. API methods
|
||||
all use the 'const char*' form for input.
|
||||
|
||||
Use the compile time #define:
|
||||
|
||||
TIXML_USE_STL
|
||||
|
||||
to compile one version or the other. This can be passed by the compiler,
|
||||
or set as the first line of "tinyxml.h".
|
||||
|
||||
Note: If compiling the test code in Linux, setting the environment
|
||||
variable TINYXML_USE_STL=YES/NO will control STL compilation. In the
|
||||
Windows project file, STL and non STL targets are provided. In your project,
|
||||
It's probably easiest to add the line "#define TIXML_USE_STL" as the first
|
||||
line of tinyxml.h.
|
||||
|
||||
<h3> UTF-8 </h3>
|
||||
|
||||
TinyXML supports UTF-8 allowing to manipulate XML files in any language. TinyXML
|
||||
also supports "legacy mode" - the encoding used before UTF-8 support and
|
||||
probably best described as "extended ascii".
|
||||
|
||||
Normally, TinyXML will try to detect the correct encoding and use it. However,
|
||||
by setting the value of TIXML_DEFAULT_ENCODING in the header file, TinyXML
|
||||
can be forced to always use one encoding.
|
||||
|
||||
TinyXML will assume Legacy Mode until one of the following occurs:
|
||||
<ol>
|
||||
<li> If the non-standard but common "UTF-8 lead bytes" (0xef 0xbb 0xbf)
|
||||
begin the file or data stream, TinyXML will read it as UTF-8. </li>
|
||||
<li> If the declaration tag is read, and it has an encoding="UTF-8", then
|
||||
TinyXML will read it as UTF-8. </li>
|
||||
<li> If the declaration tag is read, and it has no encoding specified, then TinyXML will
|
||||
read it as UTF-8. </li>
|
||||
<li> If the declaration tag is read, and it has an encoding="something else", then TinyXML
|
||||
will read it as Legacy Mode. In legacy mode, TinyXML will work as it did before. It's
|
||||
not clear what that mode does exactly, but old content should keep working.</li>
|
||||
<li> Until one of the above criteria is met, TinyXML runs in Legacy Mode.</li>
|
||||
</ol>
|
||||
|
||||
What happens if the encoding is incorrectly set or detected? TinyXML will try
|
||||
to read and pass through text seen as improperly encoded. You may get some strange results or
|
||||
mangled characters. You may want to force TinyXML to the correct mode.
|
||||
|
||||
You may force TinyXML to Legacy Mode by using LoadFile( TIXML_ENCODING_LEGACY ) or
|
||||
LoadFile( filename, TIXML_ENCODING_LEGACY ). You may force it to use legacy mode all
|
||||
the time by setting TIXML_DEFAULT_ENCODING = TIXML_ENCODING_LEGACY. Likewise, you may
|
||||
force it to TIXML_ENCODING_UTF8 with the same technique.
|
||||
|
||||
For English users, using English XML, UTF-8 is the same as low-ASCII. You
|
||||
don't need to be aware of UTF-8 or change your code in any way. You can think
|
||||
of UTF-8 as a "superset" of ASCII.
|
||||
|
||||
UTF-8 is not a double byte format - but it is a standard encoding of Unicode!
|
||||
TinyXML does not use or directly support wchar, TCHAR, or Microsoft's _UNICODE at this time.
|
||||
It is common to see the term "Unicode" improperly refer to UTF-16, a wide byte encoding
|
||||
of unicode. This is a source of confusion.
|
||||
|
||||
For "high-ascii" languages - everything not English, pretty much - TinyXML can
|
||||
handle all languages, at the same time, as long as the XML is encoded
|
||||
in UTF-8. That can be a little tricky, older programs and operating systems
|
||||
tend to use the "default" or "traditional" code page. Many apps (and almost all
|
||||
modern ones) can output UTF-8, but older or stubborn (or just broken) ones
|
||||
still output text in the default code page.
|
||||
|
||||
For example, Japanese systems traditionally use SHIFT-JIS encoding.
|
||||
Text encoded as SHIFT-JIS can not be read by TinyXML.
|
||||
A good text editor can import SHIFT-JIS and then save as UTF-8.
|
||||
|
||||
The <a href="http://skew.org/xml/tutorial/">Skew.org link</a> does a great
|
||||
job covering the encoding issue.
|
||||
|
||||
The test file "utf8test.xml" is an XML containing English, Spanish, Russian,
|
||||
and Simplified Chinese. (Hopefully they are translated correctly). The file
|
||||
"utf8test.gif" is a screen capture of the XML file, rendered in IE. Note that
|
||||
if you don't have the correct fonts (Simplified Chinese or Russian) on your
|
||||
system, you won't see output that matches the GIF file even if you can parse
|
||||
it correctly. Also note that (at least on my Windows machine) console output
|
||||
is in a Western code page, so that Print() or printf() cannot correctly display
|
||||
the file. This is not a bug in TinyXML - just an OS issue. No data is lost or
|
||||
destroyed by TinyXML. The console just doesn't render UTF-8.
|
||||
|
||||
|
||||
<h3> Entities </h3>
|
||||
TinyXML recognizes the pre-defined "character entities", meaning special
|
||||
characters. Namely:
|
||||
|
||||
@verbatim
|
||||
& &
|
||||
< <
|
||||
> >
|
||||
" "
|
||||
' '
|
||||
@endverbatim
|
||||
|
||||
These are recognized when the XML document is read, and translated to there
|
||||
UTF-8 equivalents. For instance, text with the XML of:
|
||||
|
||||
@verbatim
|
||||
Far & Away
|
||||
@endverbatim
|
||||
|
||||
will have the Value() of "Far & Away" when queried from the TiXmlText object,
|
||||
and will be written back to the XML stream/file as an ampersand. Older versions
|
||||
of TinyXML "preserved" character entities, but the newer versions will translate
|
||||
them into characters.
|
||||
|
||||
Additionally, any character can be specified by its Unicode code point:
|
||||
The syntax " " or " " are both to the non-breaking space characher.
|
||||
|
||||
<h3> Printing </h3>
|
||||
TinyXML can print output in several different ways that all have strengths and limitations.
|
||||
|
||||
- Print( FILE* ). Output to a std-C stream, which includes all C files as well as stdout.
|
||||
- "Pretty prints", but you don't have control over printing options.
|
||||
- The output is streamed directly to the FILE object, so there is no memory overhead
|
||||
in the TinyXML code.
|
||||
- used by Print() and SaveFile()
|
||||
|
||||
- operator<<. Output to a c++ stream.
|
||||
- Integrates with standart C++ iostreams.
|
||||
- Outputs in "network printing" mode without line breaks. Good for network transmission
|
||||
and moving XML between C++ objects, but hard for a human to read.
|
||||
|
||||
- TiXmlPrinter. Output to a std::string or memory buffer.
|
||||
- API is less concise
|
||||
- Future printing options will be put here.
|
||||
- Printing may change slightly in future versions as it is refined and expanded.
|
||||
|
||||
<h3> Streams </h3>
|
||||
With TIXML_USE_STL on TinyXML supports C++ streams (operator <<,>>) streams as well
|
||||
as C (FILE*) streams. There are some differences that you may need to be aware of.
|
||||
|
||||
C style output:
|
||||
- based on FILE*
|
||||
- the Print() and SaveFile() methods
|
||||
|
||||
Generates formatted output, with plenty of white space, intended to be as
|
||||
human-readable as possible. They are very fast, and tolerant of ill formed
|
||||
XML documents. For example, an XML document that contains 2 root elements
|
||||
and 2 declarations, will still print.
|
||||
|
||||
C style input:
|
||||
- based on FILE*
|
||||
- the Parse() and LoadFile() methods
|
||||
|
||||
A fast, tolerant read. Use whenever you don't need the C++ streams.
|
||||
|
||||
C++ style output:
|
||||
- based on std::ostream
|
||||
- operator<<
|
||||
|
||||
Generates condensed output, intended for network transmission rather than
|
||||
readability. Depending on your system's implementation of the ostream class,
|
||||
these may be somewhat slower. (Or may not.) Not tolerant of ill formed XML:
|
||||
a document should contain the correct one root element. Additional root level
|
||||
elements will not be streamed out.
|
||||
|
||||
C++ style input:
|
||||
- based on std::istream
|
||||
- operator>>
|
||||
|
||||
Reads XML from a stream, making it useful for network transmission. The tricky
|
||||
part is knowing when the XML document is complete, since there will almost
|
||||
certainly be other data in the stream. TinyXML will assume the XML data is
|
||||
complete after it reads the root element. Put another way, documents that
|
||||
are ill-constructed with more than one root element will not read correctly.
|
||||
Also note that operator>> is somewhat slower than Parse, due to both
|
||||
implementation of the STL and limitations of TinyXML.
|
||||
|
||||
<h3> White space </h3>
|
||||
The world simply does not agree on whether white space should be kept, or condensed.
|
||||
For example, pretend the '_' is a space, and look at "Hello____world". HTML, and
|
||||
at least some XML parsers, will interpret this as "Hello_world". They condense white
|
||||
space. Some XML parsers do not, and will leave it as "Hello____world". (Remember
|
||||
to keep pretending the _ is a space.) Others suggest that __Hello___world__ should become
|
||||
Hello___world.
|
||||
|
||||
It's an issue that hasn't been resolved to my satisfaction. TinyXML supports the
|
||||
first 2 approaches. Call TiXmlBase::SetCondenseWhiteSpace( bool ) to set the desired behavior.
|
||||
The default is to condense white space.
|
||||
|
||||
If you change the default, you should call TiXmlBase::SetCondenseWhiteSpace( bool )
|
||||
before making any calls to Parse XML data, and I don't recommend changing it after
|
||||
it has been set.
|
||||
|
||||
|
||||
<h3> Handles </h3>
|
||||
|
||||
Where browsing an XML document in a robust way, it is important to check
|
||||
for null returns from method calls. An error safe implementation can
|
||||
generate a lot of code like:
|
||||
|
||||
@verbatim
|
||||
TiXmlElement* root = document.FirstChildElement( "Document" );
|
||||
if ( root )
|
||||
{
|
||||
TiXmlElement* element = root->FirstChildElement( "Element" );
|
||||
if ( element )
|
||||
{
|
||||
TiXmlElement* child = element->FirstChildElement( "Child" );
|
||||
if ( child )
|
||||
{
|
||||
TiXmlElement* child2 = child->NextSiblingElement( "Child" );
|
||||
if ( child2 )
|
||||
{
|
||||
// Finally do something useful.
|
||||
@endverbatim
|
||||
|
||||
Handles have been introduced to clean this up. Using the TiXmlHandle class,
|
||||
the previous code reduces to:
|
||||
|
||||
@verbatim
|
||||
TiXmlHandle docHandle( &document );
|
||||
TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement();
|
||||
if ( child2 )
|
||||
{
|
||||
// do something useful
|
||||
@endverbatim
|
||||
|
||||
Which is much easier to deal with. See TiXmlHandle for more information.
|
||||
|
||||
|
||||
<h3> Row and Column tracking </h3>
|
||||
Being able to track nodes and attributes back to their origin location
|
||||
in source files can be very important for some applications. Additionally,
|
||||
knowing where parsing errors occured in the original source can be very
|
||||
time saving.
|
||||
|
||||
TinyXML can tracks the row and column origin of all nodes and attributes
|
||||
in a text file. The TiXmlBase::Row() and TiXmlBase::Column() methods return
|
||||
the origin of the node in the source text. The correct tabs can be
|
||||
configured in TiXmlDocument::SetTabSize().
|
||||
|
||||
|
||||
<h2> Using and Installing </h2>
|
||||
|
||||
To Compile and Run xmltest:
|
||||
|
||||
A Linux Makefile and a Windows Visual C++ .dsw file is provided.
|
||||
Simply compile and run. It will write the file demotest.xml to your
|
||||
disk and generate output on the screen. It also tests walking the
|
||||
DOM by printing out the number of nodes found using different
|
||||
techniques.
|
||||
|
||||
The Linux makefile is very generic and runs on many systems - it
|
||||
is currently tested on mingw and
|
||||
MacOSX. You do not need to run 'make depend'. The dependecies have been
|
||||
hard coded.
|
||||
|
||||
<h3>Windows project file for VC6</h3>
|
||||
<ul>
|
||||
<li>tinyxml: tinyxml library, non-STL </li>
|
||||
<li>tinyxmlSTL: tinyxml library, STL </li>
|
||||
<li>tinyXmlTest: test app, non-STL </li>
|
||||
<li>tinyXmlTestSTL: test app, STL </li>
|
||||
</ul>
|
||||
|
||||
<h3>Makefile</h3>
|
||||
At the top of the makefile you can set:
|
||||
|
||||
PROFILE, DEBUG, and TINYXML_USE_STL. Details (such that they are) are in
|
||||
the makefile.
|
||||
|
||||
In the tinyxml directory, type "make clean" then "make". The executable
|
||||
file 'xmltest' will be created.
|
||||
|
||||
|
||||
|
||||
<h3>To Use in an Application:</h3>
|
||||
|
||||
Add tinyxml.cpp, tinyxml.h, tinyxmlerror.cpp, tinyxmlparser.cpp, tinystr.cpp, and tinystr.h to your
|
||||
project or make file. That's it! It should compile on any reasonably
|
||||
compliant C++ system. You do not need to enable exceptions or
|
||||
RTTI for TinyXML.
|
||||
|
||||
|
||||
<h2> How TinyXML works. </h2>
|
||||
|
||||
An example is probably the best way to go. Take:
|
||||
@verbatim
|
||||
<?xml version="1.0" standalone=no>
|
||||
<!-- Our to do list data -->
|
||||
<ToDo>
|
||||
<Item priority="1"> Go to the <bold>Toy store!</bold></Item>
|
||||
<Item priority="2"> Do bills</Item>
|
||||
</ToDo>
|
||||
@endverbatim
|
||||
|
||||
Its not much of a To Do list, but it will do. To read this file
|
||||
(say "demo.xml") you would create a document, and parse it in:
|
||||
@verbatim
|
||||
TiXmlDocument doc( "demo.xml" );
|
||||
doc.LoadFile();
|
||||
@endverbatim
|
||||
|
||||
And its ready to go. Now lets look at some lines and how they
|
||||
relate to the DOM.
|
||||
|
||||
@verbatim
|
||||
<?xml version="1.0" standalone=no>
|
||||
@endverbatim
|
||||
|
||||
The first line is a declaration, and gets turned into the
|
||||
TiXmlDeclaration class. It will be the first child of the
|
||||
document node.
|
||||
|
||||
This is the only directive/special tag parsed by TinyXML.
|
||||
Generally directive tags are stored in TiXmlUnknown so the
|
||||
commands wont be lost when it is saved back to disk.
|
||||
|
||||
@verbatim
|
||||
<!-- Our to do list data -->
|
||||
@endverbatim
|
||||
|
||||
A comment. Will become a TiXmlComment object.
|
||||
|
||||
@verbatim
|
||||
<ToDo>
|
||||
@endverbatim
|
||||
|
||||
The "ToDo" tag defines a TiXmlElement object. This one does not have
|
||||
any attributes, but does contain 2 other elements.
|
||||
|
||||
@verbatim
|
||||
<Item priority="1">
|
||||
@endverbatim
|
||||
|
||||
Creates another TiXmlElement which is a child of the "ToDo" element.
|
||||
This element has 1 attribute, with the name "priority" and the value
|
||||
"1".
|
||||
|
||||
@verbatim
|
||||
Go to the
|
||||
@endverbatim
|
||||
|
||||
A TiXmlText. This is a leaf node and cannot contain other nodes.
|
||||
It is a child of the "Item" TiXmlElement.
|
||||
|
||||
@verbatim
|
||||
<bold>
|
||||
@endverbatim
|
||||
|
||||
|
||||
Another TiXmlElement, this one a child of the "Item" element.
|
||||
|
||||
Etc.
|
||||
|
||||
Looking at the entire object tree, you end up with:
|
||||
@verbatim
|
||||
TiXmlDocument "demo.xml"
|
||||
TiXmlDeclaration "version='1.0'" "standalone=no"
|
||||
TiXmlComment " Our to do list data"
|
||||
TiXmlElement "ToDo"
|
||||
TiXmlElement "Item" Attribtutes: priority = 1
|
||||
TiXmlText "Go to the "
|
||||
TiXmlElement "bold"
|
||||
TiXmlText "Toy store!"
|
||||
TiXmlElement "Item" Attributes: priority=2
|
||||
TiXmlText "Do bills"
|
||||
@endverbatim
|
||||
|
||||
<h2> Documentation </h2>
|
||||
|
||||
The documentation is build with Doxygen, using the 'dox'
|
||||
configuration file.
|
||||
|
||||
<h2> License </h2>
|
||||
|
||||
TinyXML is released under the zlib license:
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
|
||||
<h2> References </h2>
|
||||
|
||||
The World Wide Web Consortium is the definitive standard body for
|
||||
XML, and their web pages contain huge amounts of information.
|
||||
|
||||
The definitive spec: <a href="http://www.w3.org/TR/2004/REC-xml-20040204/">
|
||||
http://www.w3.org/TR/2004/REC-xml-20040204/</a>
|
||||
|
||||
I also recommend "XML Pocket Reference" by Robert Eckstein and published by
|
||||
OReilly...the book that got the whole thing started.
|
||||
|
||||
<h2> Contributors, Contacts, and a Brief History </h2>
|
||||
|
||||
Thanks very much to everyone who sends suggestions, bugs, ideas, and
|
||||
encouragement. It all helps, and makes this project fun. A special thanks
|
||||
to the contributors on the web pages that keep it lively.
|
||||
|
||||
So many people have sent in bugs and ideas, that rather than list here
|
||||
we try to give credit due in the "changes.txt" file.
|
||||
|
||||
TinyXML was originally written by Lee Thomason. (Often the "I" still
|
||||
in the documentation.) Lee reviews changes and releases new versions,
|
||||
with the help of Yves Berquin, Andrew Ellerton, and the tinyXml community.
|
||||
|
||||
We appreciate your suggestions, and would love to know if you
|
||||
use TinyXML. Hopefully you will enjoy it and find it useful.
|
||||
Please post questions, comments, file bugs, or contact us at:
|
||||
|
||||
www.sourceforge.net/projects/tinyxml
|
||||
|
||||
Lee Thomason, Yves Berquin, Andrew Ellerton
|
||||
*/
|
@ -1,111 +0,0 @@
|
||||
/*
|
||||
www.sourceforge.net/projects/tinyxml
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TIXML_USE_STL
|
||||
|
||||
#include "tinystr.h"
|
||||
|
||||
// Error value for find primitive
|
||||
const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1);
|
||||
|
||||
|
||||
// Null rep.
|
||||
TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
|
||||
|
||||
|
||||
void TiXmlString::reserve (size_type cap)
|
||||
{
|
||||
if (cap > capacity())
|
||||
{
|
||||
TiXmlString tmp;
|
||||
tmp.init(length(), cap);
|
||||
memcpy(tmp.start(), data(), length());
|
||||
swap(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TiXmlString& TiXmlString::assign(const char* str, size_type len)
|
||||
{
|
||||
size_type cap = capacity();
|
||||
if (len > cap || cap > 3*(len + 8))
|
||||
{
|
||||
TiXmlString tmp;
|
||||
tmp.init(len);
|
||||
memcpy(tmp.start(), str, len);
|
||||
swap(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(start(), str, len);
|
||||
set_size(len);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
TiXmlString& TiXmlString::append(const char* str, size_type len)
|
||||
{
|
||||
size_type newsize = length() + len;
|
||||
if (newsize > capacity())
|
||||
{
|
||||
reserve (newsize + capacity());
|
||||
}
|
||||
memmove(finish(), str, len);
|
||||
set_size(newsize);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
TiXmlString operator + (const TiXmlString & a, const TiXmlString & b)
|
||||
{
|
||||
TiXmlString tmp;
|
||||
tmp.reserve(a.length() + b.length());
|
||||
tmp += a;
|
||||
tmp += b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
TiXmlString operator + (const TiXmlString & a, const char* b)
|
||||
{
|
||||
TiXmlString tmp;
|
||||
TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) );
|
||||
tmp.reserve(a.length() + b_len);
|
||||
tmp += a;
|
||||
tmp.append(b, b_len);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
TiXmlString operator + (const char* a, const TiXmlString & b)
|
||||
{
|
||||
TiXmlString tmp;
|
||||
TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) );
|
||||
tmp.reserve(a_len + b.length());
|
||||
tmp.append(a, a_len);
|
||||
tmp += b;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
#endif // TIXML_USE_STL
|
@ -1,305 +0,0 @@
|
||||
/*
|
||||
www.sourceforge.net/projects/tinyxml
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TIXML_USE_STL
|
||||
|
||||
#ifndef TIXML_STRING_INCLUDED
|
||||
#define TIXML_STRING_INCLUDED
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/* The support for explicit isn't that universal, and it isn't really
|
||||
required - it is used to check that the TiXmlString class isn't incorrectly
|
||||
used. Be nice to old compilers and macro it here:
|
||||
*/
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1200 )
|
||||
// Microsoft visual studio, version 6 and higher.
|
||||
#define TIXML_EXPLICIT explicit
|
||||
#elif defined(__GNUC__) && (__GNUC__ >= 3 )
|
||||
// GCC version 3 and higher.s
|
||||
#define TIXML_EXPLICIT explicit
|
||||
#else
|
||||
#define TIXML_EXPLICIT
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
TiXmlString is an emulation of a subset of the std::string template.
|
||||
Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
|
||||
Only the member functions relevant to the TinyXML project have been implemented.
|
||||
The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
|
||||
a string and there's no more room, we allocate a buffer twice as big as we need.
|
||||
*/
|
||||
class TiXmlString
|
||||
{
|
||||
public :
|
||||
// The size type used
|
||||
typedef size_t size_type;
|
||||
|
||||
// Error value for find primitive
|
||||
static const size_type npos; // = -1;
|
||||
|
||||
|
||||
// TiXmlString empty constructor
|
||||
TiXmlString () : rep_(&nullrep_)
|
||||
{
|
||||
}
|
||||
|
||||
// TiXmlString copy constructor
|
||||
TiXmlString ( const TiXmlString & copy) : rep_(0)
|
||||
{
|
||||
init(copy.length());
|
||||
memcpy(start(), copy.data(), length());
|
||||
}
|
||||
|
||||
// TiXmlString constructor, based on a string
|
||||
TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0)
|
||||
{
|
||||
init( static_cast<size_type>( strlen(copy) ));
|
||||
memcpy(start(), copy, length());
|
||||
}
|
||||
|
||||
// TiXmlString constructor, based on a string
|
||||
TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0)
|
||||
{
|
||||
init(len);
|
||||
memcpy(start(), str, len);
|
||||
}
|
||||
|
||||
// TiXmlString destructor
|
||||
~TiXmlString ()
|
||||
{
|
||||
quit();
|
||||
}
|
||||
|
||||
TiXmlString& operator = (const char * copy)
|
||||
{
|
||||
return assign( copy, (size_type)strlen(copy));
|
||||
}
|
||||
|
||||
TiXmlString& operator = (const TiXmlString & copy)
|
||||
{
|
||||
return assign(copy.start(), copy.length());
|
||||
}
|
||||
|
||||
|
||||
// += operator. Maps to append
|
||||
TiXmlString& operator += (const char * suffix)
|
||||
{
|
||||
return append(suffix, static_cast<size_type>( strlen(suffix) ));
|
||||
}
|
||||
|
||||
// += operator. Maps to append
|
||||
TiXmlString& operator += (char single)
|
||||
{
|
||||
return append(&single, 1);
|
||||
}
|
||||
|
||||
// += operator. Maps to append
|
||||
TiXmlString& operator += (const TiXmlString & suffix)
|
||||
{
|
||||
return append(suffix.data(), suffix.length());
|
||||
}
|
||||
|
||||
|
||||
// Convert a TiXmlString into a null-terminated char *
|
||||
const char * c_str () const { return rep_->str; }
|
||||
|
||||
// Convert a TiXmlString into a char * (need not be null terminated).
|
||||
const char * data () const { return rep_->str; }
|
||||
|
||||
// Return the length of a TiXmlString
|
||||
size_type length () const { return rep_->size; }
|
||||
|
||||
// Alias for length()
|
||||
size_type size () const { return rep_->size; }
|
||||
|
||||
// Checks if a TiXmlString is empty
|
||||
bool empty () const { return rep_->size == 0; }
|
||||
|
||||
// Return capacity of string
|
||||
size_type capacity () const { return rep_->capacity; }
|
||||
|
||||
|
||||
// single char extraction
|
||||
const char& at (size_type index) const
|
||||
{
|
||||
assert( index < length() );
|
||||
return rep_->str[ index ];
|
||||
}
|
||||
|
||||
// [] operator
|
||||
char& operator [] (size_type index) const
|
||||
{
|
||||
assert( index < length() );
|
||||
return rep_->str[ index ];
|
||||
}
|
||||
|
||||
// find a char in a string. Return TiXmlString::npos if not found
|
||||
size_type find (char lookup) const
|
||||
{
|
||||
return find(lookup, 0);
|
||||
}
|
||||
|
||||
// find a char in a string from an offset. Return TiXmlString::npos if not found
|
||||
size_type find (char tofind, size_type offset) const
|
||||
{
|
||||
if (offset >= length()) return npos;
|
||||
|
||||
for (const char* p = c_str() + offset; *p != '\0'; ++p)
|
||||
{
|
||||
if (*p == tofind) return static_cast< size_type >( p - c_str() );
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
void clear ()
|
||||
{
|
||||
//Lee:
|
||||
//The original was just too strange, though correct:
|
||||
// TiXmlString().swap(*this);
|
||||
//Instead use the quit & re-init:
|
||||
quit();
|
||||
init(0,0);
|
||||
}
|
||||
|
||||
/* Function to reserve a big amount of data when we know we'll need it. Be aware that this
|
||||
function DOES NOT clear the content of the TiXmlString if any exists.
|
||||
*/
|
||||
void reserve (size_type cap);
|
||||
|
||||
TiXmlString& assign (const char* str, size_type len);
|
||||
|
||||
TiXmlString& append (const char* str, size_type len);
|
||||
|
||||
void swap (TiXmlString& other)
|
||||
{
|
||||
Rep* r = rep_;
|
||||
rep_ = other.rep_;
|
||||
other.rep_ = r;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void init(size_type sz) { init(sz, sz); }
|
||||
void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; }
|
||||
char* start() const { return rep_->str; }
|
||||
char* finish() const { return rep_->str + rep_->size; }
|
||||
|
||||
struct Rep
|
||||
{
|
||||
size_type size, capacity;
|
||||
char str[1];
|
||||
};
|
||||
|
||||
void init(size_type sz, size_type cap)
|
||||
{
|
||||
if (cap)
|
||||
{
|
||||
// Lee: the original form:
|
||||
// rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
|
||||
// doesn't work in some cases of new being overloaded. Switching
|
||||
// to the normal allocation, although use an 'int' for systems
|
||||
// that are overly picky about structure alignment.
|
||||
const size_type bytesNeeded = sizeof(Rep) + cap;
|
||||
const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int );
|
||||
rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] );
|
||||
|
||||
rep_->str[ rep_->size = sz ] = '\0';
|
||||
rep_->capacity = cap;
|
||||
}
|
||||
else
|
||||
{
|
||||
rep_ = &nullrep_;
|
||||
}
|
||||
}
|
||||
|
||||
void quit()
|
||||
{
|
||||
if (rep_ != &nullrep_)
|
||||
{
|
||||
// The rep_ is really an array of ints. (see the allocator, above).
|
||||
// Cast it back before delete, so the compiler won't incorrectly call destructors.
|
||||
delete [] ( reinterpret_cast<int*>( rep_ ) );
|
||||
}
|
||||
}
|
||||
|
||||
Rep * rep_;
|
||||
static Rep nullrep_;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
inline bool operator == (const TiXmlString & a, const TiXmlString & b)
|
||||
{
|
||||
return ( a.length() == b.length() ) // optimization on some platforms
|
||||
&& ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare
|
||||
}
|
||||
inline bool operator < (const TiXmlString & a, const TiXmlString & b)
|
||||
{
|
||||
return strcmp(a.c_str(), b.c_str()) < 0;
|
||||
}
|
||||
|
||||
inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }
|
||||
inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; }
|
||||
inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }
|
||||
inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }
|
||||
|
||||
inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }
|
||||
inline bool operator == (const char* a, const TiXmlString & b) { return b == a; }
|
||||
inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }
|
||||
inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }
|
||||
|
||||
TiXmlString operator + (const TiXmlString & a, const TiXmlString & b);
|
||||
TiXmlString operator + (const TiXmlString & a, const char* b);
|
||||
TiXmlString operator + (const char* a, const TiXmlString & b);
|
||||
|
||||
|
||||
/*
|
||||
TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
|
||||
Only the operators that we need for TinyXML have been developped.
|
||||
*/
|
||||
class TiXmlOutStream : public TiXmlString
|
||||
{
|
||||
public :
|
||||
|
||||
// TiXmlOutStream << operator.
|
||||
TiXmlOutStream & operator << (const TiXmlString & in)
|
||||
{
|
||||
*this += in;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TiXmlOutStream << operator.
|
||||
TiXmlOutStream & operator << (const char * in)
|
||||
{
|
||||
*this += in;
|
||||
return *this;
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
#endif // TIXML_STRING_INCLUDED
|
||||
#endif // TIXML_USE_STL
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,52 +0,0 @@
|
||||
/*
|
||||
www.sourceforge.net/projects/tinyxml
|
||||
Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any
|
||||
damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any
|
||||
purpose, including commercial applications, and to alter it and
|
||||
redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must
|
||||
not claim that you wrote the original software. If you use this
|
||||
software in a product, an acknowledgment in the product documentation
|
||||
would be appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and
|
||||
must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#include "tinyxml.h"
|
||||
|
||||
// The goal of the seperate error file is to make the first
|
||||
// step towards localization. tinyxml (currently) only supports
|
||||
// english error messages, but the could now be translated.
|
||||
//
|
||||
// It also cleans up the code a bit.
|
||||
//
|
||||
|
||||
const char* TiXmlBase::errorString[ TiXmlBase::TIXML_ERROR_STRING_COUNT ] =
|
||||
{
|
||||
"No error",
|
||||
"Error",
|
||||
"Failed to open file",
|
||||
"Error parsing Element.",
|
||||
"Failed to read Element name",
|
||||
"Error reading Element value.",
|
||||
"Error reading Attributes.",
|
||||
"Error: empty tag.",
|
||||
"Error reading end tag.",
|
||||
"Error parsing Unknown.",
|
||||
"Error parsing Comment.",
|
||||
"Error parsing Declaration.",
|
||||
"Error document empty.",
|
||||
"Error null (0) or unexpected EOF found in input stream.",
|
||||
"Error parsing CDATA.",
|
||||
"Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
|
||||
};
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue