diff --git a/README.md b/README.md
index 736fb28..0f29f0b 100644
--- a/README.md
+++ b/README.md
@@ -1,214 +1,21 @@
-# csoap
+# webcc
-A lightweight C++ SOAP client & server library based on Boost.Asio.
+A lightweight C++ REST and SOAP client and server library based on Boost.Asio.
-[中文教程](doc/ClientTutorial_zh-CN.md)
+## Tutorials
-## Client Usage
+**SOAP:**
+- [SOAP Client Tutorial](doc/SoapClientTutorial.md)
+- [SOAP Server Tutorial](doc/SoapServerTutorial.md)
-Firstly, please install **SoapUI** if you don't have it. We need SoapUI to generate sample requests for each web service operation. The open source version is good enough.
+- [SOAP 客户端教程](doc/SoapClientTutorial_zh-CN.md)
-Take the calculator web service provided by ParaSoft as an example. Download the WSDL from http://ws1.parasoft.com/glue/calculator.wsdl, create a SOAP project within SoapUI (remember to check "**Create sample requests for all operations?**"), you will see the sample request for "add" operation as the following:
-```xml
-
-
-
-
- 1
- 2
-
-
-
-```
-
-In order to call the "add" operation, we have to send a HTTP request with the above SOAP envelope as the content. Let's see how to do this with *csoap*.
-
-Firstly, create a class `CalculatorClient` which is derived from `csoap::SoapClient`:
-
-```cpp
-#include
-#include "csoap/soap_client.h"
-
-class CalculatorClient : public csoap::SoapClient {
-public:
- CalculatorClient() {
- Init();
- }
-```
-
-Initialize the URL, host, port, etc. in `Init()`:
-```cpp
-private:
- void Init() {
- url_ = "/glue/calculator";
- host_ = "ws1.parasoft.com";
- port_ = ""; // Default to "80".
- service_ns_ = { "cal", "http://www.parasoft.com/wsdl/calculator/" };
- result_name_ = "Result";
- }
-```
-
-Because four calculator operations (*add*, *subtract*, *multiply* and *divide*) all have two parameters, we create a wrapper for `SoapClient::Call()`, name is as `Calc`:
-```cpp
-bool Calc(const std::string& operation,
- const std::string& x_name,
- const std::string& y_name,
- double x,
- double y,
- double* result) {
- // Prepare parameters.
- std::vector parameters{
- { x_name, x },
- { y_name, y }
- };
-
- // Make the call.
- std::string result_str;
- csoap::Error error = Call(operation, std::move(parameters), &result_str);
-
- // Error handling if any.
- if (error != csoap::kNoError) {
- std::cerr << "Error: " << error;
- std::cerr << ", " << csoap::GetErrorMessage(error) << std::endl;
- return false;
- }
-
- // Convert the result from string to double.
- try {
- *result = boost::lexical_cast(result_str);
- } catch (boost::bad_lexical_cast&) {
- return false;
- }
-
- return true;
-}
-```
-
-Note that the local parameters are moved (with `std::move()`). This is to avoid expensive copy of long string parameters, e.g., XML strings.
-
-Finally, we implement the four operations simply as the following:
-```cpp
-bool Add(double x, double y, double* result) {
- return Calc("add", "x", "y", x, y, result);
-}
-
-bool Subtract(double x, double y, double* result) {
- return Calc("subtract", "x", "y", x, y, result);
-}
-
-bool Multiply(double x, double y, double* result) {
- return Calc("multiply", "x", "y", x, y, result);
-}
-
-bool Divide(double x, double y, double* result) {
- return Calc("divide", "numerator", "denominator", x, y, result);
-}
-```
-See? It's not that complicated. Check folder ***demo/calculator_client*** for the full example.
-
-## Server Usage
-
-*NOTE: The server part is currently under development, not stable enough to be used in a real production.*
-
-Suppose you want to provide a calculator web service just like the one from ParaSoft.
-
-Firstly, create a class `CalculatorService` which is derived from `csoap::SoapService`, override the `Handle` method:
-```cpp
-#include "csoap/soap_service.h"
-
-class CalculatorService : public csoap::SoapService {
-public:
- CalculatorService();
-
- bool Handle(const csoap::SoapRequest& soap_request,
- csoap::SoapResponse* soap_response) override;
-};
-```
-The `Handle` method has two parameters, one for request (input), one for response (output).
-The implementation is quite straightforward:
-- Get operation from request.
-- Get parameters from request.
-- Calculate the result.
-- Set namespaces, result name and so on to response.
-- Set result to response.
-
-```cpp
-#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& soap_request,
- csoap::SoapResponse* soap_response) {
- try {
- if (soap_request.operation() == "add") {
- double x = boost::lexical_cast(soap_request.GetParameter("x"));
- double y = boost::lexical_cast(soap_request.GetParameter("y"));
-
- double result = x + y;
-
- soap_response->set_soapenv_ns(csoap::kSoapEnvNamespace);
- soap_response->set_service_ns({ "cal", "http://mycalculator/" });
- soap_response->set_operation(soap_request.operation());
- soap_response->set_result_name("Result");
- soap_response->set_result(boost::lexical_cast(result));
-
- return true;
-
- } else {
- // NOT_IMPLEMENTED
- }
- } catch (boost::bad_lexical_cast&) {
- // BAD_REQUEST
- }
-
- return false;
-}
-```
-
-The `main` function would be:
-```cpp
-int main(int argc, char* argv[]) {
- if (argc != 2) {
- std::cout << "Usage: " << argv[0] << " " << std::endl;
- std::cout << " E.g.," << std::endl;
- std::cout << " " << argv[0] << " 8080" << std::endl;
- return 1;
- }
-
- const char* host = "0.0.0.0"; // TODO
-
- try {
- csoap::HttpServer server(host, argv[1]);
-
- csoap::SoapServicePtr service(new CalculatorService);
- server.RegisterService(service);
-
- server.Run();
-
- } catch (std::exception& e) {
- std::cerr << "exception: " << e.what() << "\n";
- return 1;
- }
-
- return 0;
-}
-```
-
-## Limitations
+## Dependencies
-- Only support `int`, `double`, `bool` and `string` parameters.
-- Only support UTF-8 encoded content.
-- One connection one call.
-- Connection is in synchronous (or blocking) mode; timeout (default to 30s) is configurable.
+- C++11
+- Boost 1.66+
+- pugixml (already included in the source tree) (SOAP only)
-## Dependencies
+## Build
-- Boost 1.66+.
-- XML processing is based on PugiXml, which is already included in the source tree.
-- Build system is CMake, but it should be quite easy to integrate into your own project.
+TODO
diff --git a/doc/SoapClientTutorial.md b/doc/SoapClientTutorial.md
new file mode 100644
index 0000000..71c47e8
--- /dev/null
+++ b/doc/SoapClientTutorial.md
@@ -0,0 +1,101 @@
+# SOAP Client Tutorial
+
+Firstly, please install **SoapUI** if you don't have it. We need SoapUI to generate sample requests for each web service operation. The open source version is good enough.
+
+Take the calculator web service provided by ParaSoft as an example. Download the WSDL from http://ws1.parasoft.com/glue/calculator.wsdl, create a SOAP project within SoapUI (remember to check "**Create sample requests for all operations?**"), you will see the sample request for "add" operation as the following:
+```xml
+
+
+
+
+ 1
+ 2
+
+
+
+```
+
+In order to call the "add" operation, we have to send a HTTP request with the above SOAP envelope as the content. Let's see how to do this with *webcc*.
+
+Firstly, create a class `CalcClient` which is derived from `webcc::SoapClient`:
+
+```cpp
+#include
+#include "webcc/soap_client.h"
+
+class CalcClient : public webcc::SoapClient {
+public:
+ CalcClient() {
+ Init();
+ }
+```
+
+Initialize the URL, host, port, etc. in `Init()`:
+```cpp
+private:
+ void Init() {
+ url_ = "/glue/calculator";
+ host_ = "ws1.parasoft.com";
+ port_ = ""; // Default to "80".
+ service_ns_ = { "cal", "http://www.parasoft.com/wsdl/calculator/" };
+ result_name_ = "Result";
+ }
+```
+
+Because four calculator operations (*add*, *subtract*, *multiply* and *divide*) all have two parameters, we create a wrapper for `SoapClient::Call()`, name is as `Calc`:
+```cpp
+bool Calc(const std::string& operation,
+ const std::string& x_name,
+ const std::string& y_name,
+ double x,
+ double y,
+ double* result) {
+ // Prepare parameters.
+ std::vector parameters{
+ { x_name, x },
+ { y_name, y }
+ };
+
+ // Make the call.
+ std::string result_str;
+ webcc::Error error = Call(operation, std::move(parameters), &result_str);
+
+ // Error handling if any.
+ if (error != webcc::kNoError) {
+ std::cerr << "Error: " << error;
+ std::cerr << ", " << webcc::GetErrorMessage(error) << std::endl;
+ return false;
+ }
+
+ // Convert the result from string to double.
+ try {
+ *result = boost::lexical_cast(result_str);
+ } catch (boost::bad_lexical_cast&) {
+ return false;
+ }
+
+ return true;
+}
+```
+
+Note that the local parameters are moved (with `std::move()`). This is to avoid expensive copy of long string parameters, e.g., XML strings.
+
+Finally, we implement the four operations simply as the following:
+```cpp
+bool Add(double x, double y, double* result) {
+ return Calc("add", "x", "y", x, y, result);
+}
+
+bool Subtract(double x, double y, double* result) {
+ return Calc("subtract", "x", "y", x, y, result);
+}
+
+bool Multiply(double x, double y, double* result) {
+ return Calc("multiply", "x", "y", x, y, result);
+}
+
+bool Divide(double x, double y, double* result) {
+ return Calc("divide", "numerator", "denominator", x, y, result);
+}
+```
+See? It's not that complicated. Check folder ***demo/calculator_client*** for the full example.
diff --git a/doc/ClientTutorial_zh-CN.md b/doc/SoapClientTutorial_zh-CN.md
similarity index 85%
rename from doc/ClientTutorial_zh-CN.md
rename to doc/SoapClientTutorial_zh-CN.md
index da0961a..979fdf1 100644
--- a/doc/ClientTutorial_zh-CN.md
+++ b/doc/SoapClientTutorial_zh-CN.md
@@ -1,4 +1,4 @@
-# cSoap 客户端使用指南
+# SOAP Client Tutorial (zh-CN)
## 背景
@@ -8,11 +8,12 @@
WWSAPI 的官方文档经常让人摸不着头脑,没有完整的示例,给出一段代码,常常需要几经调整才能使用。WWSAPI 自动生成的代码,是纯 C 的接口,在难用程度上,较 gSoap 有过之而无不及。在消息参数上,它强制使用双字节 Unicode,我们的输入输出都是 UTF8 的 `std::string`,于是莫名地多出很多编码转换。WWSAPI 需要你手动分配堆(heap),需要你指定消息的缓冲大小,而最严重的问题是,它不够稳定,特别是在子线程里调用时,莫名其妙连接就会断掉。
-于是,我就动手自己写了个 [cSoap](https://github.com/sprinfall/csoap)。
+于是,我就动手自己写了个 [webcc](https://github.com/sprinfall/webcc)。
+一开始 webcc 只支持 SOAP,名字就叫 csoap,后来支持了 REST,于是改名为 webcc,取 Web C++ 的意思。
## 原理
-cSoap 没有提供从 WSDL 自动生成代码的功能,一来是因为这一过程太复杂了,二来是自动生成的代码一般都不好用。所以 cSoap 最好搭配 [SoapUI](https://www.soapui.org) 一起使用。SoapUI 可以帮助我们为每一个 Web Service 操作(operation)生成请求的样例,基于请求样例,就很容易发起调用了,也避免了直接阅读 WSDL。
+Webcc 没有提供从 WSDL 自动生成代码的功能,一来是因为这一过程太复杂了,二来是自动生成的代码一般都不好用。所以 webcc 最好搭配 [SoapUI](https://www.soapui.org) 一起使用。SoapUI 可以帮助我们为每一个 Web Service 操作(operation)生成请求的样例,基于请求样例,就很容易发起调用了,也避免了直接阅读 WSDL。
下面以 ParaSoft 提供的 [Calculator](http://ws1.parasoft.com/glue/calculator.wsdl) 为例,首先下载 WSDL,然后在 SoapUI 里创建一个 SOAP 项目,记得勾上 "Create sample requests for all operations?" 这个选项,然后就能看到下面这样的请求样例了:
```xml
@@ -49,19 +50,19 @@ User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
```
-所以 cSoap 所做的,只不过是跟 `ws1.parasoft.com` 建立 TCP Socket 连接,然后发送上面这段内容而已。
+所以 webcc 所做的,只不过是跟 `ws1.parasoft.com` 建立 TCP Socket 连接,然后发送上面这段内容而已。
## 用法
-首先,创建一个类 `CalculatorClient`,继承自 `csoap::SoapClient`:
+首先,创建一个类 `CalcClient`,继承自 `webcc::SoapClient`:
```cpp
#include
-#include "csoap/soap_client.h"
+#include "webcc/soap_client.h"
-class CalculatorClient : public csoap::SoapClient {
+class CalcClient : public webcc::SoapClient {
public:
- CalculatorClient() {
+ CalcClient() {
Init();
}
```
@@ -87,19 +88,19 @@ bool Calc(const std::string& operation,
double y,
double* result) {
// Prepare parameters.
- std::vector parameters{
+ std::vector parameters{
{ x_name, x },
{ y_name, y }
};
// Make the call.
std::string result_str;
- csoap::Error error = Call(operation, std::move(parameters), &result_str);
+ webcc::Error error = Call(operation, std::move(parameters), &result_str);
// Error handling if any.
- if (error != csoap::kNoError) {
+ if (error != webcc::kNoError) {
std::cerr << "Error: " << error;
- std::cerr << ", " << csoap::GetErrorMessage(error) << std::endl;
+ std::cerr << ", " << webcc::GetErrorMessage(error) << std::endl;
return false;
}
@@ -138,7 +139,7 @@ bool Divide(double x, double y, double* result) {
## 局限
-当然,cSoap 有很多局限,比如:
+当然,webcc 有很多局限,比如:
- 只支持 `int`, `double`, `bool` 和 `string` 这几种参数类型;
- 只支持 UTF-8 编码的消息内容;
- 一次调用一个连接;
@@ -146,7 +147,7 @@ bool Divide(double x, double y, double* result) {
## 依赖
-在实现上,cSoap 有下面这些依赖:
+在实现上,webcc 有下面这些依赖:
- Boost 1.66+;
-- XML 解析和构造基于 PugiXml;
+- XML 解析和构造基于 pugixml;
- 构建系统是 CMake,应该可以很方便地集成到其他 C++ 项目中。
diff --git a/doc/SoapServerTutorial.md b/doc/SoapServerTutorial.md
new file mode 100644
index 0000000..927e4c5
--- /dev/null
+++ b/doc/SoapServerTutorial.md
@@ -0,0 +1,94 @@
+# SOAP Server Tutorial
+
+Suppose you want to provide a calculator web service just like the one from [ParaSoft](http://ws1.parasoft.com/glue/calculator.wsdl).
+
+Firstly, create a class `CalcService` which is derived from `webcc::SoapService`, override the `Handle` method:
+```cpp
+#include "webcc/soap_service.h"
+
+class CalcService : public webcc::SoapService {
+public:
+ CalcService() = default;
+
+ bool Handle(const webcc::SoapRequest& soap_request,
+ webcc::SoapResponse* soap_response) override;
+};
+```
+The `Handle` method has two parameters, one for request (input), one for response (output).
+The implementation is quite straightforward:
+- Get operation from request.
+- Get parameters from request.
+- Calculate the result.
+- Set namespaces, result name and so on to response.
+- Set result to response.
+
+```cpp
+#include "calc_service.h"
+
+#include "boost/lexical_cast.hpp"
+
+#include "webcc/soap_request.h"
+#include "webcc/soap_response.h"
+
+bool CalcService::Handle(const webcc::SoapRequest& soap_request,
+ webcc::SoapResponse* soap_response) {
+ try {
+ if (soap_request.operation() == "add") {
+ double x = boost::lexical_cast(soap_request.GetParameter("x"));
+ double y = boost::lexical_cast(soap_request.GetParameter("y"));
+
+ double result = x + y;
+
+ soap_response->set_soapenv_ns(webcc::kSoapEnvNamespace);
+ soap_response->set_service_ns({
+ "cal",
+ "http://www.example.com/calculator/"
+ });
+ soap_response->set_operation(soap_request.operation());
+ soap_response->set_result_name("Result");
+ soap_response->set_result(std::to_string(result));
+
+ return true;
+
+ } else {
+ // NOT_IMPLEMENTED
+ }
+ } catch (boost::bad_lexical_cast&) {
+ // BAD_REQUEST
+ }
+
+ return false;
+}
+```
+
+The `main` function would be:
+```cpp
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ std::cout << "Usage: " << argv[0] << " " << std::endl;
+ std::cout << " E.g.," << std::endl;
+ std::cout << " " << argv[0] << " 8080" << std::endl;
+ return 1;
+ }
+
+ unsigned short port = std::atoi(argv[1]);
+
+ // Number of worker threads.
+ std::size_t workers = 2;
+
+ try {
+ webcc::SoapServer server(port, workers);
+
+ server.RegisterService(std::make_shared(),
+ "/calculator");
+
+ server.Run();
+
+ } catch (std::exception& e) {
+ std::cerr << "exception: " << e.what() << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
+```