Merge branch 'master' into sessions

This commit is contained in:
Vladislav Oleshko 2022-06-22 15:21:34 +03:00
commit 6b6eb5eb0b
41 changed files with 694 additions and 420 deletions

View File

@ -25,7 +25,7 @@ steps:
- export TRAVIS_JOB_ID=$DRONE_BUILD_NUMBER - export TRAVIS_JOB_ID=$DRONE_BUILD_NUMBER
- export COVERALLS_PULL_REQUEST=$DRONE_PULL_REQUEST - export COVERALLS_PULL_REQUEST=$DRONE_PULL_REQUEST
- apt-get -y update - apt-get -y update
- apt-get -y install libboost-all-dev doxygen mkdocs graphviz zlib1g-dev gcc clang clang-format make cmake python3 python3-pip git openssl libssl-dev jq wget curl - apt-get -y install libasio-dev doxygen mkdocs graphviz zlib1g-dev gcc clang clang-format make cmake python3 python3-pip git openssl libssl-dev jq wget curl
- git clone https://github.com/CrowCpp/cpp-coveralls.git - git clone https://github.com/CrowCpp/cpp-coveralls.git
- cd cpp-coveralls - cd cpp-coveralls
- pip3 install . --no-input - pip3 install . --no-input
@ -73,7 +73,7 @@ steps:
commands: commands:
- export DEBIAN_FRONTEND=noninteractive - export DEBIAN_FRONTEND=noninteractive
- apt-get -y update - apt-get -y update
- apt-get -y install libboost-all-dev zlib1g-dev gcc clang make cmake python3 openssl libssl-dev - apt-get -y install libasio-dev zlib1g-dev gcc clang make cmake python3 openssl libssl-dev
- mkdir build - mkdir build
- cd build - cd build
- cmake --version - cmake --version
@ -123,7 +123,7 @@ steps:
- export TRAVIS_BRANCH=$DRONE_REPO_BRANCH - export TRAVIS_BRANCH=$DRONE_REPO_BRANCH
- export TRAVIS_JOB_ID=$DRONE_BUILD_NUMBER - export TRAVIS_JOB_ID=$DRONE_BUILD_NUMBER
- apt-get -y update - apt-get -y update
- apt-get -y install libboost-all-dev doxygen mkdocs graphviz zlib1g-dev gcc clang make cmake python3 python3-pip git openssl libssl-dev - apt-get -y install libasio-dev doxygen mkdocs graphviz zlib1g-dev gcc clang make cmake python3 python3-pip git openssl libssl-dev
- pip3 install mkdocs-material mkdocs-redirects pyyaml mkdocs-meta-descriptions-plugin --no-input - pip3 install mkdocs-material mkdocs-redirects pyyaml mkdocs-meta-descriptions-plugin --no-input
- git clone https://github.com/CrowCpp/cpp-coveralls.git - git clone https://github.com/CrowCpp/cpp-coveralls.git
- cd cpp-coveralls - cd cpp-coveralls
@ -170,7 +170,7 @@ steps:
commands: commands:
- export DEBIAN_FRONTEND=noninteractive - export DEBIAN_FRONTEND=noninteractive
- apt-get -y update - apt-get -y update
- apt-get -y install libboost-all-dev zlib1g-dev gcc clang make cmake python3 openssl libssl-dev - apt-get -y install libasio-dev zlib1g-dev gcc clang make cmake python3 openssl libssl-dev
- mkdir build - mkdir build
- cd build - cd build
- cmake --version - cmake --version

View File

@ -28,7 +28,7 @@ env:
addons: addons:
apt: apt:
packages: packages:
- libboost-all-dev - libasio-dev
- doxygen - doxygen
- mkdocs - mkdocs
- graphviz - graphviz

View File

@ -52,14 +52,17 @@ target_include_directories(Crow
$<INSTALL_INTERFACE:include> $<INSTALL_INTERFACE:include>
) )
find_package(Boost 1.64 COMPONENTS system date_time REQUIRED) find_path(ASIO_INCLUDE_DIR asio.hpp REQUIRED)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
target_link_libraries(Crow target_link_libraries(Crow
INTERFACE INTERFACE
Boost::boost Boost::system Boost::date_time
Threads::Threads Threads::Threads
) )
target_include_directories(Crow
INTERFACE
${ASIO_INCLUDE_DIR}
)
if("compression" IN_LIST CROW_FEATURES) if("compression" IN_LIST CROW_FEATURES)
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)

View File

@ -103,7 +103,7 @@ CROW_ROUTE(app, "/add_json")
More examples can be found [here](https://github.com/crowcpp/crow/tree/master/examples). More examples can be found [here](https://github.com/crowcpp/crow/tree/master/examples).
## Setting Up / Building ## Setting Up / Building
Available [here](https://crowcpp.org/getting_started/setup). Available [here](https://crowcpp.org/master/getting_started/setup).
## Disclaimer ## Disclaimer
CrowCpp/Crow is a project based on ipkn/crow. Neither CrowCpp, it's members, or this project have been associated with, or endorsed or supported by ipkn (Jaeseung Ha) in any way. We do use ipkn/crow's source code under the BSD-3 clause license and sometimes refer to the public comments available on the github repository. But we do not in any way claim to be associated with or in contact with ipkn (Jaeseung Ha) regarding CrowCpp or CrowCpp/Crow CrowCpp/Crow is a project based on ipkn/crow. Neither CrowCpp, it's members, or this project have been associated with, or endorsed or supported by ipkn (Jaeseung Ha) in any way. We do use ipkn/crow's source code under the BSD-3 clause license and sometimes refer to the public comments available on the github repository. But we do not in any way claim to be associated with or in contact with ipkn (Jaeseung Ha) regarding CrowCpp or CrowCpp/Crow

View File

@ -1,7 +1,7 @@
@PACKAGE_INIT@ @PACKAGE_INIT@
include(CMakeFindDependencyMacro) include(CMakeFindDependencyMacro)
find_dependency(Boost 1.64 COMPONENTS system date_time) find_path(ASIO_INCLUDE_DIR asio.hpp REQUIRED)
find_dependency(Threads) find_dependency(Threads)
set(CROW_INSTALLED_FEATURES "@CROW_FEATURES@") set(CROW_INSTALLED_FEATURES "@CROW_FEATURES@")
@ -41,3 +41,8 @@ set_target_properties(Crow::Crow PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "${_CROW_ICD}" INTERFACE_COMPILE_DEFINITIONS "${_CROW_ICD}"
INTERFACE_LINK_LIBRARIES "${_CROW_ILL}" INTERFACE_LINK_LIBRARIES "${_CROW_ILL}"
) )
target_include_directories(Crow::Crow
INTERFACE
${ASIO_INCLUDE_DIR}
)

View File

@ -0,0 +1,129 @@
Hello World is a good start, but what if you want something a bit more fancy.. Something like an HTML document saying "Hello World". If that's what you want, follow along:
## Basic Webpage
Let's start our webpage with.. well.. a webpage. But before we create a webpage we need to place it somewhere Crow recognizes, for now this directory is going to be called `templates`, but we can [change it later](../../guides/templating/#page).
Once our `templates` folder is created, we can create our HTML document inside it, let's call it `fancypage.html`.
After that we can just place something simple inside it like:
``` html title="templates/fancypage.html"
<!DOCTYPE html>
<html>
<body>
<p>Hello World!</p>
</body>
</html>
```
<br>
Now that we have our HTML page ready, let's take our Hello World example from earlier:
``` cpp linenums="1"
#include "crow.h"
//#include "crow_all.h"
int main()
{
crow::SimpleApp app; //define your crow application
//define your endpoint at the root directory
CROW_ROUTE(app, "/")([](){
return "Hello world";
});
//set the port, set the app to run on multiple threads, and run the app
app.port(18080).multithreaded().run();
}
```
<br>
And now let's modify it so that it returns our cool page:
``` cpp title="/main.cpp" linenums="1" hl_lines="10 11"
#include "crow.h"
//#include "crow_all.h"
int main()
{
crow::SimpleApp app;
//define your endpoint at the root directory
CROW_ROUTE(app, "/")([](){
auto page = crow::mustache::load_text("fancypage.html");
return page;
});
app.port(18080).multithreaded().run();
}
```
Your project should look something something like:
```
./
|-templates/
| |-fancypage.html
|
|-main.cpp
|-crow_all.h
```
or
```
./
|-templates/
| |-fancypage.html
|
|-crow/
| |-include/...
| |-crow.h
|-main.cpp
```
Once the code is done compiling, if we call `http://localhost:18080/` we get our Hello World in an HTML document rather than just plain text.
!!! note
Compilation instructions are available for [Linux](../setup/linux#compiling-your-project), [MacOS](../setup/macos#compiling-using-a-compiler-directly), and [Windows](../setup/windows#getting-and-compiling-crow)
## Template Webpage with a variable
But we can make things even more exciting, we can greet a user by their name instead!!
Let's start with our webpage, and modify it with a little bit of [mustache](../../guides/templating) syntax:
``` html title="templates/fancypage.html" hl_lines="4"
<!DOCTYPE html>
<html>
<body>
<p>Hello {{person}}!</p> <!--(1)-->
</body>
</html>
```
1. `{{}}` in mustache define a simple variable
<br>
Now let's modify our C++ code to use the variable we just added to our webpage (or template):
``` cpp title="/main.cpp" linenums="1" hl_lines="9-12"
#include "crow.h"
//#include "crow_all.h"
int main()
{
crow::SimpleApp app;
//define your endpoint at the root directory
CROW_ROUTE(app, "/<string>")([](std::string name){ // (1)
auto page = crow::mustache::load("fancypage.html"); // (2)
crow::mustache::context ctx ({{"person", name}}); // (3)
return page.render(ctx); //(4)
});
app.port(18080).multithreaded().run();
}
```
1. We are adding a `string` variable to the URL and a counterpart (`std::string name`) to our Route, this can be anything the user wants.
2. We are using `load()` instead of `load_text()` since we have an actual variable now.
3. We are creating a new [context](../../guides/templating/#context) containing the `person` variable from our template and the `name` we got from the URL.
4. we are using `render(ctx)` to apply our context to the template.
Now (after compiling the code and running the executable a second time) calling `http://localhost:18080/Bob` should return a webpage containing "Hello Bob!". **We did it!**
For more details on templates and HTML pages in Crow please go [here](../../guides/templating/)

View File

@ -3,7 +3,7 @@ Here's how you can install Crow on your favorite GNU/Linux distro.
### Requirements ### Requirements
- C++ compiler with at least C++11 support. - C++ compiler with at least C++11 support.
- boost library & development headers (1.64 or later). - Asio development headers (1.10.9 or later).
- **(optional)** ZLib for HTTP Compression. - **(optional)** ZLib for HTTP Compression.
- **(optional)** OpenSSL for HTTPS support. - **(optional)** OpenSSL for HTTPS support.
- **(optional)** CMake for building tests, examples, and/or installing Crow. - **(optional)** CMake for building tests, examples, and/or installing Crow.
@ -91,6 +91,3 @@ All you need to do is run the following command:
g++ main.cpp -lpthread g++ main.cpp -lpthread
``` ```
You can use arguments like `-DCROW_ENABLE_DEBUG`, `-DCROW_ENABLE_COMPRESSION -lz` for HTTP Compression, or `-DCROW_ENABLE_SSL -lssl` for HTTPS support, or even replace g++ with clang++. You can use arguments like `-DCROW_ENABLE_DEBUG`, `-DCROW_ENABLE_COMPRESSION -lz` for HTTP Compression, or `-DCROW_ENABLE_SSL -lssl` for HTTPS support, or even replace g++ with clang++.
!!! warning
If you're using a version of boost prior to 1.69, you'll need to add the argument `-lboost_system` in order for you Crow application to compile correctly.

View File

@ -24,20 +24,20 @@ This will generate a `crow_all.h` file which you can use in the following steps
## Setting up your Crow project ## Setting up your Crow project
### Using XCode ### Using XCode
1. Download and install [Homebrew](https://brew.sh). 1. Download and install [Homebrew](https://brew.sh).
2. Run `brew install boost` in your terminal. 2. Run `brew install asio` in your terminal.
3. Create a new XCode project (macOS -> Command Line Tool). 3. Create a new XCode project (macOS -> Command Line Tool).
4. Change the following project settings: 4. Change the following project settings:
=== "Multiple Headers" === "Multiple Headers"
1. Add header search paths for crow's include folder and boost's folder (`/usr/local/include`, `/usr/local/Cellar/boost/include`, and where you placed Crow's `include` folder) 1. Add header search paths for crow's include folder and asio's folder (`/usr/local/include`, `/usr/local/Cellar/asio/include`, and where you placed Crow's `include` folder)
2. Add linker flags (`-lpthread` and `-lboost_system` if you're running an old version of boost) 2. Add linker flags (`-lpthread`)
=== "Single Header" === "Single Header"
1. Place `crow_all.h` inside your project folder and add it to the project in XCode (you need to use File -> Add files to "project_name") 1. Place `crow_all.h` inside your project folder and add it to the project in XCode (you need to use File -> Add files to "project_name")
2. Add header search paths for boost's folder (`/usr/local/include`, and `/usr/local/Cellar/boost/include`) 2. Add header search paths for asio's folder (`/usr/local/include`, and `/usr/local/Cellar/asio/include`)
3. Add linker flags (`-lpthread` and `-lboost_system` if you're running an old version of boost) 3. Add linker flags (`-lpthread`)
5. Write your Crow application in `main.cpp` (something like the Hello World example will work). 5. Write your Crow application in `main.cpp` (something like the Hello World example will work).
6. Press `▶` to compile and run your Crow application. 6. Press `▶` to compile and run your Crow application.
@ -49,7 +49,7 @@ This will generate a `crow_all.h` file which you can use in the following steps
This tutorial can be used for Crow projects built with CMake as well This tutorial can be used for Crow projects built with CMake as well
1. Download and install [Homebrew](https://brew.sh). 1. Download and install [Homebrew](https://brew.sh).
2. Run `brew install cmake boost` in your terminal. 2. Run `brew install cmake asio` in your terminal.
3. Get Crow's source code (the entire source code). 3. Get Crow's source code (the entire source code).
3. Run the following Commands: 3. Run the following Commands:
1. `mkdir build` 1. `mkdir build`
@ -70,6 +70,3 @@ g++ main.cpp -lpthread
You'll need to install GCC via `brew install gcc`. the Clang compiler should be part of XCode or XCode command line tools. You'll need to install GCC via `brew install gcc`. the Clang compiler should be part of XCode or XCode command line tools.
You can use arguments like `-DCROW_ENABLE_DEBUG`, `-DCROW_ENABLE_COMPRESSION -lz` for HTTP Compression, or `-DCROW_ENABLE_SSL -lssl` for HTTPS support, or even replace g++ with clang++. You can use arguments like `-DCROW_ENABLE_DEBUG`, `-DCROW_ENABLE_COMPRESSION -lz` for HTTP Compression, or `-DCROW_ENABLE_SSL -lssl` for HTTPS support, or even replace g++ with clang++.
!!! warning
If you're using a version of boost prior to 1.69, you'll need to add the argument `-lboost_system` in order for you Crow application to compile correctly.

View File

@ -38,7 +38,7 @@ Please note that the `port()` and `multithreaded()` methods aren't needed, Thoug
Once you've followed all the steps above, your code should look similar to this Once you've followed all the steps above, your code should look similar to this
``` cpp linenums="1" ``` cpp title="main.cpp" linenums="1"
#include "crow.h" #include "crow.h"
//#include "crow_all.h" //#include "crow_all.h"
@ -55,4 +55,7 @@ int main()
app.port(18080).multithreaded().run(); app.port(18080).multithreaded().run();
} }
``` ```
After building and running your .cpp file, you should be able to access your endpoint at [http://localhost:18080](http://localhost:18080). Opening this URL in your browser will show a white screen with "Hello world" typed on it.
You then need to compile your code on your [Linux](../setup/linux#compiling-your-project), [MacOS](../setup/macos#compiling-using-a-compiler-directly), or [Windows](../setup/windows#getting-and-compiling-crow) machine
After building your `.cpp` file and running the resulting executable, you should be able to access your endpoint at [http://localhost:18080](http://localhost:18080). Opening this URL in your browser will show a white screen with "Hello world" typed on it.

View File

@ -20,7 +20,7 @@ JSON read value, used for taking a JSON string and parsing it into `crow::json`.
You can read individual items of the rvalue, but you cannot add items to it.<br> You can read individual items of the rvalue, but you cannot add items to it.<br>
To do that, you need to convert it to a `wvalue`, which can be done by simply writing `#!cpp crow::json::wvalue wval (rval);` (assuming `rval` is your `rvalue`).<br><br> To do that, you need to convert it to a `wvalue`, which can be done by simply writing `#!cpp crow::json::wvalue wval (rval);` (assuming `rval` is your `rvalue`).<br><br>
For more info on read values go [here](/reference/classcrow_1_1json_1_1rvalue.html).<br><br> For more info on read values go [here](../../reference/classcrow_1_1json_1_1rvalue.html).<br><br>
## wvalue ## wvalue
JSON write value, used for creating, editing and converting JSON to a string.<br><br> JSON write value, used for creating, editing and converting JSON to a string.<br><br>

View File

@ -10,7 +10,7 @@ To enable SSL, first your application needs to define either a `.crt` and `.key`
You also need to define `CROW_ENABLE_SSL` in your compiler definitions (`g++ main.cpp -DCROW_ENABLE_SSL` for example) or `set(CROW_FEATURES ssl)` in `CMakeLists.txt`. You also need to define `CROW_ENABLE_SSL` in your compiler definitions (`g++ main.cpp -DCROW_ENABLE_SSL` for example) or `set(CROW_FEATURES ssl)` in `CMakeLists.txt`.
You can also set your own SSL context (by using `boost::asio::ssl::context ctx`) and then applying it via the `#!cpp app.ssl(ctx)` method.<br><br> You can also set your own SSL context (by using `asio::ssl::context ctx`) and then applying it via the `#!cpp app.ssl(ctx)` method.<br><br>
!!! warning !!! warning

View File

@ -5,11 +5,15 @@ To create a websocket in Crow, you need a websocket route.<br>
A websocket route differs from a normal route quite a bit. It uses A slightly altered `CROW_WEBSOCKET_ROUTE(app, "/url")` macro, which is then followed by a series of methods (with handlers inside) for each event. These are (sorted by order of execution): A websocket route differs from a normal route quite a bit. It uses A slightly altered `CROW_WEBSOCKET_ROUTE(app, "/url")` macro, which is then followed by a series of methods (with handlers inside) for each event. These are (sorted by order of execution):
- `#!cpp onaccept([&](const crow::request&){handler code goes here})` (This handler has to return `bool`) - `#!cpp onaccept([&](const crow::request& req, void** userdata){handler code goes here})`
- `#!cpp onopen([&](crow::websocket::connection& conn){handler code goes here})` - `#!cpp onopen([&](crow::websocket::connection& conn){handler code goes here})`
- `#!cpp onmessage([&](crow::websocket::connection& conn, const std::string message, bool is_binary){handler code goes here})` - `#!cpp onmessage([&](crow::websocket::connection& conn, const std::string& message, bool is_binary){handler code goes here})`
- `#!cpp onerror([&](crow::websocket::connection& conn){handler code goes here})` - `#!cpp onerror([&](crow::websocket::connection& conn, const std::string& error_message){handler code goes here})`
- `#!cpp onclose([&](crow::websocket::connection& conn, const std::string reason){handler code goes here})` - `#!cpp onclose([&](crow::websocket::connection& conn, const std::string& reason){handler code goes here})`
!!! note
`onaccept` must return a boolean. In case `false` is returned, the connection is shut down, deleted, and no further communication is done.
!!! Warning !!! Warning

View File

@ -94,7 +94,7 @@
<div class="md-footer-meta__inner md-grid"> <div class="md-footer-meta__inner md-grid">
<!-- Copyright and theme information --> <!-- Copyright and theme information -->
<div class="md-footer-copyright"> <div class="md-footer-copyright" style="flex: 1;display: flex;justify-content: left;">
{% if config.copyright %} {% if config.copyright %}
<div class="md-footer-copyright__highlight"> <div class="md-footer-copyright__highlight">
{{ config.copyright }} {{ config.copyright }}
@ -103,7 +103,7 @@
{{ extracopyright }} {{ extracopyright }}
</div> </div>
<a style="margin: auto .6rem; font-size: .64rem;" href="/privacy_policy.html">Privacy Policy</a> <a style="margin: auto .6rem; font-size: .64rem;text-align: center;flex: 1;display: flex;justify-content: center;" href="/privacy_policy.html">Privacy Policy</a>
<!-- Social links --> <!-- Social links -->
{% include "partials/social.html" %} {% include "partials/social.html" %}

View File

@ -35,6 +35,8 @@
--home-border-color: #ffffff20; --home-border-color: #ffffff20;
--home-shadow-color: #00000040; --home-shadow-color: #00000040;
--home-image-border: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.3) 50%, rgba(255, 255, 255, 0) 100%); --home-image-border: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.3) 50%, rgba(255, 255, 255, 0) 100%);
--md-admonition-bg-color: #272a2b;
--md-code-hl-color: rgba(255, 255, 0, 0.18);
} }

View File

@ -38,12 +38,6 @@ add_custom_target(example_ws_copy ALL DEPENDS ws.html)
add_executable(basic_example example.cpp) add_executable(basic_example example.cpp)
add_warnings_optimizations(basic_example) add_warnings_optimizations(basic_example)
target_link_libraries(basic_example PUBLIC Crow::Crow) target_link_libraries(basic_example PUBLIC Crow::Crow)
add_custom_command(OUTPUT example_test.py
COMMAND ${CMAKE_COMMAND} -E
copy ${PROJECT_SOURCE_DIR}/example_test.py ${CMAKE_CURRENT_BINARY_DIR}/example_test.py
DEPENDS ${PROJECT_SOURCE_DIR}/example_test.py
)
add_custom_target(example_copy ALL DEPENDS example_test.py)
if(CROW_AMALGAMATE) if(CROW_AMALGAMATE)
add_executable(example_with_all example_with_all.cpp) add_executable(example_with_all example_with_all.cpp)

View File

@ -1,7 +1,5 @@
#include "crow.h" #include "crow.h"
#include <sstream>
class ExampleLogHandler : public crow::ILogHandler class ExampleLogHandler : public crow::ILogHandler
{ {
public: public:
@ -179,7 +177,7 @@ int main()
// To see in action submit something like '/params?pew=42' // To see in action submit something like '/params?pew=42'
if (req.url_params.get("pew") != nullptr) if (req.url_params.get("pew") != nullptr)
{ {
double countD = boost::lexical_cast<double>(req.url_params.get("pew")); double countD = crow::utility::lexical_cast<double>(req.url_params.get("pew"));
os << "The value of 'pew' is " << countD << '\n'; os << "The value of 'pew' is " << countD << '\n';
} }

View File

@ -1,19 +0,0 @@
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
@app.route("/about/<path:path>/hello")
def hello1(path):
return "about1"
@app.route("/about")
def hello2():
return "about2"
print app.url_map
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8888)

View File

@ -1,44 +0,0 @@
import urllib
assert "Hello World!" == urllib.urlopen('http://localhost:18080').read()
assert "About Crow example." == urllib.urlopen('http://localhost:18080/about').read()
assert 404 == urllib.urlopen('http://localhost:18080/list').getcode()
assert "3 bottles of beer!" == urllib.urlopen('http://localhost:18080/hello/3').read()
assert "100 bottles of beer!" == urllib.urlopen('http://localhost:18080/hello/100').read()
assert 400 == urllib.urlopen('http://localhost:18080/hello/500').getcode()
assert "3" == urllib.urlopen('http://localhost:18080/add_json', data='{"a":1,"b":2}').read()
assert "3" == urllib.urlopen('http://localhost:18080/add/1/2').read()
# test persistent connection
import socket
import time
s = socket.socket()
s.connect(('localhost', 18080))
for i in xrange(10):
s.send('''GET / HTTP/1.1
Host: localhost\r\n\r\n''');
assert 'Hello World!' in s.recv(1024)
# test large
s = socket.socket()
s.connect(('localhost', 18080))
s.send('''GET /large HTTP/1.1
Host: localhost\r\nConnection: close\r\n\r\n''')
r = ''
while True:
d = s.recv(1024*1024)
if not d:
break;
r += d
print len(r), len(d)
print len(r), r[:100]
assert len(r) > 512*1024
# test timeout
s = socket.socket()
s.connect(('localhost', 18080))
# invalid request, connection will be closed after timeout
s.send('''GET / HTTP/1.1
hHhHHefhwjkefhklwejfklwejf
''')
print s.recv(1024)

View File

@ -1,7 +1,5 @@
#include "crow.h" #include "crow.h"
#include <sstream>
class ExampleLogHandler : public crow::ILogHandler class ExampleLogHandler : public crow::ILogHandler
{ {
public: public:
@ -110,7 +108,7 @@ int main()
os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n"; os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n";
if (req.url_params.get("pew") != nullptr) if (req.url_params.get("pew") != nullptr)
{ {
double countD = boost::lexical_cast<double>(req.url_params.get("pew")); double countD = crow::utility::lexical_cast<double>(req.url_params.get("pew"));
os << "The value of 'pew' is " << countD << '\n'; os << "The value of 'pew' is " << countD << '\n';
} }
auto count = req.url_params.get_list("count"); auto count = req.url_params.get_list("count");

View File

@ -1,7 +1,5 @@
#include "crow_all.h" #include "crow_all.h"
#include <sstream>
class ExampleLogHandler : public crow::ILogHandler class ExampleLogHandler : public crow::ILogHandler
{ {
public: public:
@ -100,7 +98,7 @@ int main()
os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n"; os << "The key 'foo' was " << (req.url_params.get("foo") == nullptr ? "not " : "") << "found.\n";
if (req.url_params.get("pew") != nullptr) if (req.url_params.get("pew") != nullptr)
{ {
double countD = boost::lexical_cast<double>(req.url_params.get("pew")); double countD = crow::utility::lexical_cast<double>(req.url_params.get("pew"));
os << "The value of 'pew' is " << countD << '\n'; os << "The value of 'pew' is " << countD << '\n';
} }
auto count = req.url_params.get_list("count"); auto count = req.url_params.get_list("count");

View File

@ -14,7 +14,7 @@ int main()
// Use .pem file // Use .pem file
//app.port(18080).ssl_file("test.pem").run(); //app.port(18080).ssl_file("test.pem").run();
// Use custom context; see boost::asio::ssl::context // Use custom context; see asio::ssl::context
/* /*
* crow::ssl_context_t ctx; * crow::ssl_context_t ctx;
* ctx.set_verify_mode(...) * ctx.set_verify_mode(...)

View File

@ -39,7 +39,7 @@
namespace crow namespace crow
{ {
#ifdef CROW_ENABLE_SSL #ifdef CROW_ENABLE_SSL
using ssl_context_t = boost::asio::ssl::context; using ssl_context_t = asio::ssl::context;
#endif #endif
/// The main server application /// The main server application
@ -293,14 +293,6 @@ namespace crow
} }
} }
/// Notify anything using `wait_for_server_start()` to proceed
void notify_server_start()
{
std::unique_lock<std::mutex> lock(start_mutex_);
server_started_ = true;
cv_started_.notify_all();
}
/// Run the server /// Run the server
void run() void run()
{ {
@ -370,7 +362,7 @@ namespace crow
void remove_websocket(crow::websocket::connection* conn) void remove_websocket(crow::websocket::connection* conn)
{ {
std::remove(websockets_.begin(), websockets_.end(), conn); websockets_.erase(std::remove(websockets_.begin(), websockets_.end(), conn), websockets_.end());
} }
/// Print the routing paths defined for each HTTP method /// Print the routing paths defined for each HTTP method
@ -387,12 +379,12 @@ namespace crow
self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename) self_t& ssl_file(const std::string& crt_filename, const std::string& key_filename)
{ {
ssl_used_ = true; ssl_used_ = true;
ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); ssl_context_.set_verify_mode(asio::ssl::verify_peer);
ssl_context_.set_verify_mode(boost::asio::ssl::verify_client_once); ssl_context_.set_verify_mode(asio::ssl::verify_client_once);
ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem); ssl_context_.use_certificate_file(crt_filename, ssl_context_t::pem);
ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem); ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem);
ssl_context_.set_options( ssl_context_.set_options(
boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3); asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
return *this; return *this;
} }
@ -400,11 +392,11 @@ namespace crow
self_t& ssl_file(const std::string& pem_filename) self_t& ssl_file(const std::string& pem_filename)
{ {
ssl_used_ = true; ssl_used_ = true;
ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); ssl_context_.set_verify_mode(asio::ssl::verify_peer);
ssl_context_.set_verify_mode(boost::asio::ssl::verify_client_once); ssl_context_.set_verify_mode(asio::ssl::verify_client_once);
ssl_context_.load_verify_file(pem_filename); ssl_context_.load_verify_file(pem_filename);
ssl_context_.set_options( ssl_context_.set_options(
boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3); asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
return *this; return *this;
} }
@ -412,16 +404,16 @@ namespace crow
self_t& ssl_chainfile(const std::string& crt_filename, const std::string& key_filename) self_t& ssl_chainfile(const std::string& crt_filename, const std::string& key_filename)
{ {
ssl_used_ = true; ssl_used_ = true;
ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer); ssl_context_.set_verify_mode(asio::ssl::verify_peer);
ssl_context_.set_verify_mode(boost::asio::ssl::verify_client_once); ssl_context_.set_verify_mode(asio::ssl::verify_client_once);
ssl_context_.use_certificate_chain_file(crt_filename); ssl_context_.use_certificate_chain_file(crt_filename);
ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem); ssl_context_.use_private_key_file(key_filename, ssl_context_t::pem);
ssl_context_.set_options( ssl_context_.set_options(
boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3); asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::no_sslv3);
return *this; return *this;
} }
self_t& ssl(boost::asio::ssl::context&& ctx) self_t& ssl(asio::ssl::context&& ctx)
{ {
ssl_used_ = true; ssl_used_ = true;
ssl_context_ = std::move(ctx); ssl_context_ = std::move(ctx);
@ -491,12 +483,19 @@ namespace crow
/// Wait until the server has properly started /// Wait until the server has properly started
void wait_for_server_start() void wait_for_server_start()
{
{ {
std::unique_lock<std::mutex> lock(start_mutex_); std::unique_lock<std::mutex> lock(start_mutex_);
if (server_started_) if (!server_started_)
return;
cv_started_.wait(lock); cv_started_.wait(lock);
} }
if (server_)
server_->wait_for_start();
#ifdef CROW_ENABLE_SSL
else if (ssl_server_)
ssl_server_->wait_for_start();
#endif
}
private: private:
template<typename... Ts> template<typename... Ts>
@ -508,6 +507,14 @@ namespace crow
black_magic::tuple_extract<Middlewares, decltype(fwd)>(fwd))...); black_magic::tuple_extract<Middlewares, decltype(fwd)>(fwd))...);
} }
/// Notify anything using `wait_for_server_start()` to proceed
void notify_server_start()
{
std::unique_lock<std::mutex> lock(start_mutex_);
server_started_ = true;
cv_started_.notify_all();
}
private: private:
std::uint8_t timeout_{5}; std::uint8_t timeout_{5};
@ -533,7 +540,7 @@ namespace crow
#ifdef CROW_ENABLE_SSL #ifdef CROW_ENABLE_SSL
std::unique_ptr<ssl_server_t> ssl_server_; std::unique_ptr<ssl_server_t> ssl_server_;
bool ssl_used_{false}; bool ssl_used_{false};
ssl_context_t ssl_context_{boost::asio::ssl::context::sslv23}; ssl_context_t ssl_context_{asio::ssl::context::sslv23};
#endif #endif
std::unique_ptr<server_t> server_; std::unique_ptr<server_t> server_;

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <boost/algorithm/string/predicate.hpp> #include <locale>
#include <boost/functional/hash.hpp>
#include <unordered_map> #include <unordered_map>
#include "crow/utility.h"
namespace crow namespace crow
{ {
@ -15,12 +15,17 @@ namespace crow
std::locale locale; std::locale locale;
for (auto c : key) for (auto c : key)
{ hash_combine(seed, std::toupper(c, locale));
boost::hash_combine(seed, std::toupper(c, locale));
}
return seed; return seed;
} }
private:
static inline void hash_combine(std::size_t& seed, char v)
{
std::hash<char> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
}; };
/// Equals function for ci_map (unordered_multimap). /// Equals function for ci_map (unordered_multimap).
@ -28,7 +33,7 @@ namespace crow
{ {
bool operator()(const std::string& l, const std::string& r) const bool operator()(const std::string& l, const std::string& r) const
{ {
return boost::iequals(l, r); return utility::string_equals(l, r);
} }
}; };

View File

@ -1,8 +1,6 @@
#pragma once #pragma once
#include <boost/asio.hpp> #define ASIO_STANDALONE
#include <boost/algorithm/string/predicate.hpp> #include <asio.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/array.hpp>
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include <vector> #include <vector>
@ -18,10 +16,10 @@
#include "crow/middleware.h" #include "crow/middleware.h"
#include "crow/socket_adaptors.h" #include "crow/socket_adaptors.h"
#include "crow/compression.h" #include "crow/compression.h"
#include "crow/utility.h"
namespace crow namespace crow
{ {
using namespace boost;
using tcp = asio::ip::tcp; using tcp = asio::ip::tcp;
@ -37,7 +35,7 @@ namespace crow
public: public:
Connection( Connection(
boost::asio::io_service& io_service, asio::io_service& io_service,
Handler* handler, Handler* handler,
const std::string& server_name, const std::string& server_name,
std::tuple<Middlewares...>* middlewares, std::tuple<Middlewares...>* middlewares,
@ -79,7 +77,7 @@ namespace crow
void start() void start()
{ {
adaptor_.start([this](const boost::system::error_code& ec) { adaptor_.start([this](const asio::error_code& ec) {
if (!ec) if (!ec)
{ {
start_deadline(); start_deadline();
@ -144,7 +142,7 @@ namespace crow
} }
} }
CROW_LOG_INFO << "Request: " << boost::lexical_cast<std::string>(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << (char)(req.http_ver_major + '0') << "." << (char)(req.http_ver_minor + '0') << ' ' << method_name(req.method) << " " << req.url; CROW_LOG_INFO << "Request: " << utility::lexical_cast<std::string>(adaptor_.remote_endpoint()) << " " << this << " HTTP/" << (char)(req.http_ver_major + '0') << "." << (char)(req.http_ver_minor + '0') << ' ' << method_name(req.method) << " " << req.url;
need_to_call_after_handlers_ = false; need_to_call_after_handlers_ = false;
@ -374,7 +372,7 @@ namespace crow
void do_write_static() void do_write_static()
{ {
is_writing = true; is_writing = true;
boost::asio::write(adaptor_.socket(), buffers_); asio::write(adaptor_.socket(), buffers_);
if (res.file_info.statResult == 0) if (res.file_info.statResult == 0)
{ {
@ -383,7 +381,7 @@ namespace crow
while (is.read(buf, sizeof(buf)).gcount() > 0) while (is.read(buf, sizeof(buf)).gcount() > 0)
{ {
std::vector<asio::const_buffer> buffers; std::vector<asio::const_buffer> buffers;
buffers.push_back(boost::asio::buffer(buf)); buffers.push_back(asio::buffer(buf));
do_write_sync(buffers); do_write_sync(buffers);
} }
} }
@ -420,7 +418,7 @@ namespace crow
else else
{ {
is_writing = true; is_writing = true;
boost::asio::write(adaptor_.socket(), buffers_); // Write the response start / headers asio::write(adaptor_.socket(), buffers_); // Write the response start / headers
cancel_deadline_timer(); cancel_deadline_timer();
if (res.body.length() > 0) if (res.body.length() > 0)
{ {
@ -433,7 +431,7 @@ namespace crow
buf = res.body.substr(0, 16384); buf = res.body.substr(0, 16384);
res.body = res.body.substr(16384); res.body = res.body.substr(16384);
buffers.clear(); buffers.clear();
buffers.push_back(boost::asio::buffer(buf)); buffers.push_back(asio::buffer(buf));
do_write_sync(buffers); do_write_sync(buffers);
} }
// Collect whatever is left (less than 16KB) and send it down the socket // Collect whatever is left (less than 16KB) and send it down the socket
@ -442,7 +440,7 @@ namespace crow
res.body.clear(); res.body.clear();
buffers.clear(); buffers.clear();
buffers.push_back(boost::asio::buffer(buf)); buffers.push_back(asio::buffer(buf));
do_write_sync(buffers); do_write_sync(buffers);
} }
is_writing = false; is_writing = false;
@ -465,8 +463,8 @@ namespace crow
//auto self = this->shared_from_this(); //auto self = this->shared_from_this();
is_reading = true; is_reading = true;
adaptor_.socket().async_read_some( adaptor_.socket().async_read_some(
boost::asio::buffer(buffer_), asio::buffer(buffer_),
[this](const boost::system::error_code& ec, std::size_t bytes_transferred) { [this](const asio::error_code& ec, std::size_t bytes_transferred) {
bool error_while_reading = true; bool error_while_reading = true;
if (!ec) if (!ec)
{ {
@ -512,9 +510,9 @@ namespace crow
{ {
//auto self = this->shared_from_this(); //auto self = this->shared_from_this();
is_writing = true; is_writing = true;
boost::asio::async_write( asio::async_write(
adaptor_.socket(), buffers_, adaptor_.socket(), buffers_,
[&](const boost::system::error_code& ec, std::size_t /*bytes_transferred*/) { [&](const asio::error_code& ec, std::size_t /*bytes_transferred*/) {
is_writing = false; is_writing = false;
res.clear(); res.clear();
res_body_copy_.clear(); res_body_copy_.clear();
@ -539,7 +537,7 @@ namespace crow
inline void do_write_sync(std::vector<asio::const_buffer>& buffers) inline void do_write_sync(std::vector<asio::const_buffer>& buffers)
{ {
boost::asio::write(adaptor_.socket(), buffers, [&](std::error_code ec, std::size_t) { asio::write(adaptor_.socket(), buffers, [&](asio::error_code ec, std::size_t) {
if (!ec) if (!ec)
{ {
return false; return false;
@ -590,7 +588,7 @@ namespace crow
Adaptor adaptor_; Adaptor adaptor_;
Handler* handler_; Handler* handler_;
boost::array<char, 4096> buffer_; std::array<char, 4096> buffer_;
HTTPParser<Connection> parser_; HTTPParser<Connection> parser_;
request req_; request req_;
@ -599,7 +597,7 @@ namespace crow
bool close_connection_ = false; bool close_connection_ = false;
const std::string& server_name_; const std::string& server_name_;
std::vector<boost::asio::const_buffer> buffers_; std::vector<asio::const_buffer> buffers_;
std::string content_length_; std::string content_length_;
std::string date_str_; std::string date_str_;

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <boost/asio.hpp> #define ASIO_STANDALONE
#include <asio.hpp>
#include "crow/common.h" #include "crow/common.h"
#include "crow/ci_map.h" #include "crow/ci_map.h"
@ -35,7 +36,7 @@ namespace crow
void* middleware_context{}; void* middleware_context{};
void* middleware_container{}; void* middleware_container{};
boost::asio::io_service* io_service{}; asio::io_service* io_service{};
/// Construct an empty request. (sets the method to `GET`) /// Construct an empty request. (sets the method to `GET`)
request(): request():

View File

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <chrono> #include <chrono>
#include <boost/date_time/posix_time/posix_time.hpp> #define ASIO_STANDALONE
#include <boost/asio.hpp> #include <asio.hpp>
#ifdef CROW_ENABLE_SSL #ifdef CROW_ENABLE_SSL
#include <boost/asio/ssl.hpp> #include <asio/ssl.hpp>
#endif #endif
#include <cstdint> #include <cstdint>
#include <atomic> #include <atomic>
@ -19,7 +19,6 @@
namespace crow namespace crow
{ {
using namespace boost;
using tcp = asio::ip::tcp; using tcp = asio::ip::tcp;
template<typename Handler, typename Adaptor = SocketAdaptor, typename... Middlewares> template<typename Handler, typename Adaptor = SocketAdaptor, typename... Middlewares>
@ -27,7 +26,7 @@ namespace crow
{ {
public: public:
Server(Handler* handler, std::string bindaddr, uint16_t port, std::string server_name = std::string("Crow/") + VERSION, std::tuple<Middlewares...>* middlewares = nullptr, uint16_t concurrency = 1, uint8_t timeout = 5, typename Adaptor::context* adaptor_ctx = nullptr): Server(Handler* handler, std::string bindaddr, uint16_t port, std::string server_name = std::string("Crow/") + VERSION, std::tuple<Middlewares...>* middlewares = nullptr, uint16_t concurrency = 1, uint8_t timeout = 5, typename Adaptor::context* adaptor_ctx = nullptr):
acceptor_(io_service_, tcp::endpoint(boost::asio::ip::address::from_string(bindaddr), port)), acceptor_(io_service_, tcp::endpoint(asio::ip::address::from_string(bindaddr), port)),
signals_(io_service_), signals_(io_service_),
tick_timer_(io_service_), tick_timer_(io_service_),
handler_(handler), handler_(handler),
@ -50,8 +49,8 @@ namespace crow
void on_tick() void on_tick()
{ {
tick_function_(); tick_function_();
tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count())); tick_timer_.expires_after(std::chrono::milliseconds(tick_interval_.count()));
tick_timer_.async_wait([this](const boost::system::error_code& ec) { tick_timer_.async_wait([this](const asio::error_code& ec) {
if (ec) if (ec)
return; return;
on_tick(); on_tick();
@ -62,7 +61,7 @@ namespace crow
{ {
uint16_t worker_thread_count = concurrency_ - 1; uint16_t worker_thread_count = concurrency_ - 1;
for (int i = 0; i < worker_thread_count; i++) for (int i = 0; i < worker_thread_count; i++)
io_service_pool_.emplace_back(new boost::asio::io_service()); io_service_pool_.emplace_back(new asio::io_service());
get_cached_date_str_pool_.resize(worker_thread_count); get_cached_date_str_pool_.resize(worker_thread_count);
task_timer_pool_.resize(worker_thread_count); task_timer_pool_.resize(worker_thread_count);
@ -125,9 +124,9 @@ namespace crow
if (tick_function_ && tick_interval_.count() > 0) if (tick_function_ && tick_interval_.count() > 0)
{ {
tick_timer_.expires_from_now(boost::posix_time::milliseconds(tick_interval_.count())); tick_timer_.expires_after(std::chrono::milliseconds(tick_interval_.count()));
tick_timer_.async_wait( tick_timer_.async_wait(
[this](const boost::system::error_code& ec) { [this](const asio::error_code& ec) {
if (ec) if (ec)
return; return;
on_tick(); on_tick();
@ -142,7 +141,7 @@ namespace crow
CROW_LOG_INFO << "Call `app.loglevel(crow::LogLevel::Warning)` to hide Info level logs."; CROW_LOG_INFO << "Call `app.loglevel(crow::LogLevel::Warning)` to hide Info level logs.";
signals_.async_wait( signals_.async_wait(
[&](const boost::system::error_code& /*error*/, int /*signal_number*/) { [&](const asio::error_code& /*error*/, int /*signal_number*/) {
stop(); stop();
}); });
@ -153,6 +152,7 @@ namespace crow
std::thread( std::thread(
[this] { [this] {
notify_start();
io_service_.run(); io_service_.run();
CROW_LOG_INFO << "Exiting."; CROW_LOG_INFO << "Exiting.";
}) })
@ -175,6 +175,14 @@ namespace crow
io_service_.stop(); // Close main io_service io_service_.stop(); // Close main io_service
} }
/// Wait until the server has properly started
void wait_for_start()
{
std::unique_lock<std::mutex> lock(start_mutex_);
if (!server_started_)
cv_started_.wait(lock);
}
void signal_clear() void signal_clear()
{ {
signals_.clear(); signals_.clear();
@ -217,7 +225,7 @@ namespace crow
acceptor_.async_accept( acceptor_.async_accept(
p->socket(), p->socket(),
[this, p, &is, service_idx](boost::system::error_code ec) { [this, p, &is, service_idx](asio::error_code ec) {
if (!ec) if (!ec)
{ {
is.post( is.post(
@ -236,6 +244,14 @@ namespace crow
} }
} }
/// Notify anything using `wait_for_start()` to proceed
void notify_start()
{
std::unique_lock<std::mutex> lock(start_mutex_);
server_started_ = true;
cv_started_.notify_all();
}
private: private:
asio::io_service io_service_; asio::io_service io_service_;
std::vector<std::unique_ptr<asio::io_service>> io_service_pool_; std::vector<std::unique_ptr<asio::io_service>> io_service_pool_;
@ -243,8 +259,12 @@ namespace crow
std::vector<std::function<std::string()>> get_cached_date_str_pool_; std::vector<std::function<std::string()>> get_cached_date_str_pool_;
tcp::acceptor acceptor_; tcp::acceptor acceptor_;
bool shutting_down_ = false; bool shutting_down_ = false;
boost::asio::signal_set signals_; bool server_started_{false};
boost::asio::deadline_timer tick_timer_; std::condition_variable cv_started_;
std::mutex start_mutex_;
asio::signal_set signals_;
asio::basic_waitable_timer<std::chrono::high_resolution_clock> tick_timer_;
Handler* handler_; Handler* handler_;
uint16_t concurrency_{2}; uint16_t concurrency_{2};

View File

@ -12,9 +12,6 @@
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/operators.hpp>
#include <vector> #include <vector>
#include <cmath> #include <cmath>
@ -117,7 +114,7 @@ namespace crow
namespace detail namespace detail
{ {
/// A read string implementation with comparison functionality. /// A read string implementation with comparison functionality.
struct r_string : boost::less_than_comparable<r_string>, boost::less_than_comparable<r_string, std::string>, boost::equality_comparable<r_string>, boost::equality_comparable<r_string, std::string> struct r_string
{ {
r_string(){}; r_string(){};
r_string(char* s, char* e): r_string(char* s, char* e):
@ -186,31 +183,85 @@ namespace crow
owned_ = 1; owned_ = 1;
} }
friend rvalue crow::json::load(const char* data, size_t size); friend rvalue crow::json::load(const char* data, size_t size);
friend bool operator==(const r_string& l, const r_string& r);
friend bool operator==(const std::string& l, const r_string& r);
friend bool operator==(const r_string& l, const std::string& r);
template<typename T, typename U>
inline static bool equals(const T& l, const U& r)
{
if (l.size() != r.size())
return false;
for (size_t i = 0; i < l.size(); i++)
{
if (*(l.begin() + i) != *(r.begin() + i))
return false;
}
return true;
}
}; };
inline bool operator<(const r_string& l, const r_string& r) inline bool operator<(const r_string& l, const r_string& r)
{ {
return boost::lexicographical_compare(l, r); return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
} }
inline bool operator<(const r_string& l, const std::string& r) inline bool operator<(const r_string& l, const std::string& r)
{ {
return boost::lexicographical_compare(l, r); return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
}
inline bool operator<(const std::string& l, const r_string& r)
{
return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
}
inline bool operator>(const r_string& l, const r_string& r)
{
return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
} }
inline bool operator>(const r_string& l, const std::string& r) inline bool operator>(const r_string& l, const std::string& r)
{ {
return boost::lexicographical_compare(r, l); return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
}
inline bool operator>(const std::string& l, const r_string& r)
{
return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
} }
inline bool operator==(const r_string& l, const r_string& r) inline bool operator==(const r_string& l, const r_string& r)
{ {
return boost::equals(l, r); return r_string::equals(l, r);
} }
inline bool operator==(const r_string& l, const std::string& r) inline bool operator==(const r_string& l, const std::string& r)
{ {
return boost::equals(l, r); return r_string::equals(l, r);
}
inline bool operator==(const std::string& l, const r_string& r)
{
return r_string::equals(l, r);
}
inline bool operator!=(const r_string& l, const r_string& r)
{
return !(l == r);
}
inline bool operator!=(const r_string& l, const std::string& r)
{
return !(l == r);
}
inline bool operator!=(const std::string& l, const r_string& r)
{
return !(l == r);
} }
} // namespace detail } // namespace detail
@ -349,13 +400,13 @@ namespace crow
{ {
case type::Number: case type::Number:
case type::String: case type::String:
return boost::lexical_cast<int64_t>(start_, end_ - start_); return utility::lexical_cast<int64_t>(start_, end_ - start_);
default: default:
const std::string msg = "expected number, got: " + std::string(get_type_str(t())); const std::string msg = "expected number, got: " + std::string(get_type_str(t()));
throw std::runtime_error(msg); throw std::runtime_error(msg);
} }
#endif #endif
return boost::lexical_cast<int64_t>(start_, end_ - start_); return utility::lexical_cast<int64_t>(start_, end_ - start_);
} }
/// The unsigned integer value. /// The unsigned integer value.
@ -366,12 +417,12 @@ namespace crow
{ {
case type::Number: case type::Number:
case type::String: case type::String:
return boost::lexical_cast<uint64_t>(start_, end_ - start_); return utility::lexical_cast<uint64_t>(start_, end_ - start_);
default: default:
throw std::runtime_error(std::string("expected number, got: ") + get_type_str(t())); throw std::runtime_error(std::string("expected number, got: ") + get_type_str(t()));
} }
#endif #endif
return boost::lexical_cast<uint64_t>(start_, end_ - start_); return utility::lexical_cast<uint64_t>(start_, end_ - start_);
} }
/// The double precision floating-point number value. /// The double precision floating-point number value.
@ -381,7 +432,7 @@ namespace crow
if (t() != type::Number) if (t() != type::Number)
throw std::runtime_error("value is not number"); throw std::runtime_error("value is not number");
#endif #endif
return boost::lexical_cast<double>(start_, end_ - start_); return utility::lexical_cast<double>(start_, end_ - start_);
} }
/// The boolean value. /// The boolean value.
@ -630,7 +681,7 @@ namespace crow
return (option_ & error_bit) != 0; return (option_ & error_bit) != 0;
} }
std::vector<std::string> keys() std::vector<std::string> keys() const
{ {
#ifndef CROW_JSON_NO_ERROR_CHECK #ifndef CROW_JSON_NO_ERROR_CHECK
if (t() != type::Object) if (t() != type::Object)
@ -1422,6 +1473,7 @@ namespace crow
wvalue& operator=(wvalue&& r) wvalue& operator=(wvalue&& r)
{ {
t_ = r.t_; t_ = r.t_;
nt = r.nt;
num = r.num; num = r.num;
s = std::move(r.s); s = std::move(r.s);
l = std::move(r.l); l = std::move(r.l);
@ -1784,12 +1836,12 @@ namespace crow
enum enum
{ {
start, start,
decp, decp, // Decimal point
zero zero
} f_state; } f_state;
char outbuf[128]; char outbuf[128];
MSC_COMPATIBLE_SPRINTF(outbuf, "%f", v.num.d); MSC_COMPATIBLE_SPRINTF(outbuf, "%f", v.num.d);
char *p = &outbuf[0], *o = nullptr; char *p = &outbuf[0], *o = nullptr; // o is the position of the first trailing 0
f_state = start; f_state = start;
while (*p != '\0') while (*p != '\0')
{ {
@ -1797,15 +1849,17 @@ namespace crow
char ch = *p; char ch = *p;
switch (f_state) switch (f_state)
{ {
case start: case start: // Loop and lookahead until a decimal point is found
if (ch == '.') if (ch == '.')
{ {
if (p + 1 && *(p + 1) == '0') p++; char fch = *(p + 1);
// if the first character is 0, leave it be (this is so that "1.00000" becomes "1.0" and not "1.")
if (fch != '\0' && fch == '0') p++;
f_state = decp; f_state = decp;
} }
p++; p++;
break; break;
case decp: case decp: // Loop until a 0 is found, if found, record its position
if (ch == '0') if (ch == '0')
{ {
f_state = zero; f_state = zero;
@ -1813,7 +1867,7 @@ namespace crow
} }
p++; p++;
break; break;
case zero: case zero: // if a non 0 is found (e.g. 1.00004) remove the earlier recorded 0 position and look for more trailing 0s
if (ch != '0') if (ch != '0')
{ {
o = nullptr; o = nullptr;
@ -1823,7 +1877,7 @@ namespace crow
break; break;
} }
} }
if (o != nullptr) if (o != nullptr) // if any trailing 0s are found, terminate the string where they begin
*o = '\0'; *o = '\0';
out += outbuf; out += outbuf;
#undef MSC_COMPATIBLE_SPRINTF #undef MSC_COMPATIBLE_SPRINTF
@ -1898,7 +1952,7 @@ namespace crow
//std::vector<boost::asio::const_buffer> dump_ref(wvalue& v) //std::vector<asio::const_buffer> dump_ref(wvalue& v)
//{ //{
//} //}
} // namespace json } // namespace json

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <iomanip> #include <iomanip>
#include <boost/optional.hpp> #include <memory>
#include <boost/algorithm/string/trim.hpp> #include "crow/utility.h"
#include "crow/http_request.h" #include "crow/http_request.h"
#include "crow/http_response.h" #include "crow/http_response.h"
@ -69,7 +69,7 @@ namespace crow
if (expires_at_) if (expires_at_)
{ {
ss << DIVIDER << "Expires=" ss << DIVIDER << "Expires="
<< std::put_time(expires_at_.get_ptr(), HTTP_DATE_FORMAT); << std::put_time(expires_at_.get(), HTTP_DATE_FORMAT);
} }
if (max_age_) if (max_age_)
{ {
@ -106,14 +106,14 @@ namespace crow
// Expires attribute // Expires attribute
Cookie& expires(const std::tm& time) Cookie& expires(const std::tm& time)
{ {
expires_at_ = time; expires_at_ = std::unique_ptr<std::tm>(new std::tm(time));
return *this; return *this;
} }
// Max-Age attribute // Max-Age attribute
Cookie& max_age(long long seconds) Cookie& max_age(long long seconds)
{ {
max_age_ = seconds; max_age_ = std::unique_ptr<long long>(new long long(seconds));
return *this; return *this;
} }
@ -148,10 +148,28 @@ namespace crow
// SameSite attribute // SameSite attribute
Cookie& same_site(SameSitePolicy ssp) Cookie& same_site(SameSitePolicy ssp)
{ {
same_site_ = ssp; same_site_ = std::unique_ptr<SameSitePolicy>(new SameSitePolicy(ssp));
return *this; return *this;
} }
Cookie(const Cookie& c):
key_(c.key_),
value_(c.value_),
domain_(c.domain_),
path_(c.path_),
secure_(c.secure_),
httponly_(c.httponly_)
{
if (c.max_age_)
max_age_ = std::unique_ptr<long long>(new long long(*c.max_age_));
if (c.expires_at_)
expires_at_ = std::unique_ptr<std::tm>(new std::tm(*c.expires_at_));
if (c.same_site_)
same_site_ = std::unique_ptr<SameSitePolicy>(new SameSitePolicy(*c.same_site_));
}
private: private:
Cookie() = default; Cookie() = default;
@ -167,13 +185,13 @@ namespace crow
private: private:
std::string key_; std::string key_;
std::string value_; std::string value_;
boost::optional<long long> max_age_{}; std::unique_ptr<long long> max_age_{};
std::string domain_ = ""; std::string domain_ = "";
std::string path_ = ""; std::string path_ = "";
bool secure_ = false; bool secure_ = false;
bool httponly_ = false; bool httponly_ = false;
boost::optional<std::tm> expires_at_{}; std::unique_ptr<std::tm> expires_at_{};
boost::optional<SameSitePolicy> same_site_{}; std::unique_ptr<SameSitePolicy> same_site_{};
static constexpr const char* DIVIDER = "; "; static constexpr const char* DIVIDER = "; ";
}; };
@ -227,17 +245,15 @@ namespace crow
if (pos_equal == cookies.npos) if (pos_equal == cookies.npos)
break; break;
std::string name = cookies.substr(pos, pos_equal - pos); std::string name = cookies.substr(pos, pos_equal - pos);
boost::trim(name); name = utility::trim(name);
pos = pos_equal + 1; pos = pos_equal + 1;
while (pos < cookies.size() && cookies[pos] == ' ')
pos++;
if (pos == cookies.size()) if (pos == cookies.size())
break; break;
size_t pos_semicolon = cookies.find(';', pos); size_t pos_semicolon = cookies.find(';', pos);
std::string value = cookies.substr(pos, pos_semicolon - pos); std::string value = cookies.substr(pos, pos_semicolon - pos);
boost::trim(value); value = utility::trim(value);
if (value[0] == '"' && value[value.size() - 1] == '"') if (value[0] == '"' && value[value.size() - 1] == '"')
{ {
value = value.substr(1, value.size() - 2); value = value.substr(1, value.size() - 2);
@ -249,8 +265,6 @@ namespace crow
if (pos == cookies.npos) if (pos == cookies.npos)
break; break;
pos++; pos++;
while (pos < cookies.size() && cookies[pos] == ' ')
pos++;
} }
} }

View File

@ -258,7 +258,7 @@ namespace crow
} }
break; break;
default: default:
throw std::runtime_error("not implemented tag type" + boost::lexical_cast<std::string>(static_cast<int>(ctx.t()))); throw std::runtime_error("not implemented tag type" + utility::lexical_cast<std::string>(static_cast<int>(ctx.t())));
} }
} }
break; break;
@ -324,7 +324,7 @@ namespace crow
current = action.pos; current = action.pos;
break; break;
default: default:
throw std::runtime_error("{{#: not implemented context type: " + boost::lexical_cast<std::string>(static_cast<int>(ctx.t()))); throw std::runtime_error("{{#: not implemented context type: " + utility::lexical_cast<std::string>(static_cast<int>(ctx.t())));
break; break;
} }
break; break;
@ -333,7 +333,7 @@ namespace crow
stack.pop_back(); stack.pop_back();
break; break;
default: default:
throw std::runtime_error("not implemented " + boost::lexical_cast<std::string>(static_cast<int>(action.t))); throw std::runtime_error("not implemented " + utility::lexical_cast<std::string>(static_cast<int>(action.t)));
} }
current++; current++;
} }
@ -379,6 +379,12 @@ namespace crow
return rendered_template(ret); return rendered_template(ret);
} }
/// Apply the values from the context provided and output a returnable template from this mustache template
rendered_template render(context&& ctx) const
{
return render(ctx);
}
/// Output a returnable template from this mustache template /// Output a returnable template from this mustache template
std::string render_string() const std::string render_string() const
{ {

View File

@ -2,7 +2,6 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <boost/algorithm/string.hpp>
#include <algorithm> #include <algorithm>
#include "crow/http_parser_merged.h" #include "crow/http_parser_merged.h"

View File

@ -6,7 +6,7 @@
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <iostream> #include <iostream>
#include <boost/optional.hpp> #include <memory>
namespace crow namespace crow
{ {
@ -106,10 +106,13 @@ inline int qs_parse(char* qs, char* qs_kv[], int qs_kv_size, bool parse_url = tr
// find the beginning of the k/v substrings or the fragment // find the beginning of the k/v substrings or the fragment
substr_ptr = parse_url ? qs + strcspn(qs, "?#") : qs; substr_ptr = parse_url ? qs + strcspn(qs, "?#") : qs;
if (parse_url)
{
if (substr_ptr[0] != '\0') if (substr_ptr[0] != '\0')
substr_ptr++; substr_ptr++;
else else
return 0; // no query or fragment return 0; // no query or fragment
}
i=0; i=0;
while(i<qs_kv_size) while(i<qs_kv_size)
@ -200,7 +203,7 @@ inline char * qs_k2v(const char * key, char * const * qs_kv, int qs_kv_size, int
return nullptr; return nullptr;
} }
inline boost::optional<std::pair<std::string, std::string>> qs_dict_name2kv(const char * dict_name, char * const * qs_kv, int qs_kv_size, int nth = 0) inline std::unique_ptr<std::pair<std::string, std::string>> qs_dict_name2kv(const char * dict_name, char * const * qs_kv, int qs_kv_size, int nth = 0)
{ {
int i; int i;
size_t name_len, skip_to_eq, skip_to_brace_open, skip_to_brace_close; size_t name_len, skip_to_eq, skip_to_brace_open, skip_to_brace_close;
@ -229,7 +232,7 @@ inline boost::optional<std::pair<std::string, std::string>> qs_dict_name2kv(cons
{ {
auto key = std::string(qs_kv[i] + skip_to_brace_open, skip_to_brace_close - skip_to_brace_open); auto key = std::string(qs_kv[i] + skip_to_brace_open, skip_to_brace_close - skip_to_brace_open);
auto value = std::string(qs_kv[i] + skip_to_eq); auto value = std::string(qs_kv[i] + skip_to_eq);
return boost::make_optional(std::make_pair(key, value)); return std::unique_ptr<std::pair<std::string, std::string>>(new std::pair<std::string, std::string>(key, value));
} }
else else
{ {
@ -239,7 +242,7 @@ inline boost::optional<std::pair<std::string, std::string>> qs_dict_name2kv(cons
} }
#endif // _qsSORTING #endif // _qsSORTING
return boost::none; return nullptr;
} }

View File

@ -5,7 +5,6 @@
#include <tuple> #include <tuple>
#include <unordered_map> #include <unordered_map>
#include <memory> #include <memory>
#include <boost/lexical_cast.hpp>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <type_traits> #include <type_traits>
@ -509,8 +508,8 @@ namespace crow
std::function<void(crow::websocket::connection&)> open_handler_; std::function<void(crow::websocket::connection&)> open_handler_;
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_; std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
std::function<void(crow::websocket::connection&, const std::string&)> close_handler_; std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
std::function<void(crow::websocket::connection&)> error_handler_; std::function<void(crow::websocket::connection&, const std::string&)> error_handler_;
std::function<bool(const crow::request&)> accept_handler_; std::function<bool(const crow::request&, void**)> accept_handler_;
uint64_t max_payload_; uint64_t max_payload_;
bool max_payload_override_ = false; bool max_payload_override_ = false;
}; };

View File

@ -1,28 +1,29 @@
#pragma once #pragma once
#include <boost/asio.hpp> #define ASIO_STANDALONE
#include <asio.hpp>
#ifdef CROW_ENABLE_SSL #ifdef CROW_ENABLE_SSL
#include <boost/asio/ssl.hpp> #include <asio/ssl.hpp>
#endif #endif
#include "crow/settings.h" #include "crow/settings.h"
#if BOOST_VERSION >= 107000 #include <asio/version.hpp>
#define GET_IO_SERVICE(s) ((boost::asio::io_context&)(s).get_executor().context()) #if ASIO_VERSION >= 101300 // 1.13.0
#define GET_IO_SERVICE(s) ((asio::io_context&)(s).get_executor().context())
#else #else
#define GET_IO_SERVICE(s) ((s).get_io_service()) #define GET_IO_SERVICE(s) ((s).get_io_service())
#endif #endif
namespace crow namespace crow
{ {
using namespace boost;
using tcp = asio::ip::tcp; using tcp = asio::ip::tcp;
/// A wrapper for the asio::ip::tcp::socket and asio::ssl::stream /// A wrapper for the asio::ip::tcp::socket and asio::ssl::stream
struct SocketAdaptor struct SocketAdaptor
{ {
using context = void; using context = void;
SocketAdaptor(boost::asio::io_service& io_service, context*): SocketAdaptor(asio::io_service& io_service, context*):
socket_(io_service) socket_(io_service)
{} {}
boost::asio::io_service& get_io_service() asio::io_service& get_io_service()
{ {
return GET_IO_SERVICE(socket_); return GET_IO_SERVICE(socket_);
} }
@ -51,32 +52,32 @@ namespace crow
void close() void close()
{ {
boost::system::error_code ec; asio::error_code ec;
socket_.close(ec); socket_.close(ec);
} }
void shutdown_readwrite() void shutdown_readwrite()
{ {
boost::system::error_code ec; asio::error_code ec;
socket_.shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, ec); socket_.shutdown(asio::socket_base::shutdown_type::shutdown_both, ec);
} }
void shutdown_write() void shutdown_write()
{ {
boost::system::error_code ec; asio::error_code ec;
socket_.shutdown(boost::asio::socket_base::shutdown_type::shutdown_send, ec); socket_.shutdown(asio::socket_base::shutdown_type::shutdown_send, ec);
} }
void shutdown_read() void shutdown_read()
{ {
boost::system::error_code ec; asio::error_code ec;
socket_.shutdown(boost::asio::socket_base::shutdown_type::shutdown_receive, ec); socket_.shutdown(asio::socket_base::shutdown_type::shutdown_receive, ec);
} }
template<typename F> template<typename F>
void start(F f) void start(F f)
{ {
f(boost::system::error_code()); f(asio::error_code());
} }
tcp::socket socket_; tcp::socket socket_;
@ -85,13 +86,13 @@ namespace crow
#ifdef CROW_ENABLE_SSL #ifdef CROW_ENABLE_SSL
struct SSLAdaptor struct SSLAdaptor
{ {
using context = boost::asio::ssl::context; using context = asio::ssl::context;
using ssl_socket_t = boost::asio::ssl::stream<tcp::socket>; using ssl_socket_t = asio::ssl::stream<tcp::socket>;
SSLAdaptor(boost::asio::io_service& io_service, context* ctx): SSLAdaptor(asio::io_service& io_service, context* ctx):
ssl_socket_(new ssl_socket_t(io_service, *ctx)) ssl_socket_(new ssl_socket_t(io_service, *ctx))
{} {}
boost::asio::ssl::stream<tcp::socket>& socket() asio::ssl::stream<tcp::socket>& socket()
{ {
return *ssl_socket_; return *ssl_socket_;
} }
@ -116,7 +117,7 @@ namespace crow
{ {
if (is_open()) if (is_open())
{ {
boost::system::error_code ec; asio::error_code ec;
raw_socket().close(ec); raw_socket().close(ec);
} }
} }
@ -125,8 +126,8 @@ namespace crow
{ {
if (is_open()) if (is_open())
{ {
boost::system::error_code ec; asio::error_code ec;
raw_socket().shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, ec); raw_socket().shutdown(asio::socket_base::shutdown_type::shutdown_both, ec);
} }
} }
@ -134,8 +135,8 @@ namespace crow
{ {
if (is_open()) if (is_open())
{ {
boost::system::error_code ec; asio::error_code ec;
raw_socket().shutdown(boost::asio::socket_base::shutdown_type::shutdown_send, ec); raw_socket().shutdown(asio::socket_base::shutdown_type::shutdown_send, ec);
} }
} }
@ -143,12 +144,12 @@ namespace crow
{ {
if (is_open()) if (is_open())
{ {
boost::system::error_code ec; asio::error_code ec;
raw_socket().shutdown(boost::asio::socket_base::shutdown_type::shutdown_receive, ec); raw_socket().shutdown(asio::socket_base::shutdown_type::shutdown_receive, ec);
} }
} }
boost::asio::io_service& get_io_service() asio::io_service& get_io_service()
{ {
return GET_IO_SERVICE(raw_socket()); return GET_IO_SERVICE(raw_socket());
} }
@ -156,13 +157,13 @@ namespace crow
template<typename F> template<typename F>
void start(F f) void start(F f)
{ {
ssl_socket_->async_handshake(boost::asio::ssl::stream_base::server, ssl_socket_->async_handshake(asio::ssl::stream_base::server,
[f](const boost::system::error_code& ec) { [f](const asio::error_code& ec) {
f(ec); f(ec);
}); });
} }
std::unique_ptr<boost::asio::ssl::stream<tcp::socket>> ssl_socket_; std::unique_ptr<asio::ssl::stream<tcp::socket>> ssl_socket_;
}; };
#endif #endif
} // namespace crow } // namespace crow

View File

@ -1,6 +1,9 @@
#pragma once #pragma once
#include <boost/asio.hpp> #define ASIO_STANDALONE
#include <asio.hpp>
#include <asio/basic_waitable_timer.hpp>
#include <chrono> #include <chrono>
#include <functional> #include <functional>
#include <map> #include <map>
@ -25,15 +28,15 @@ namespace crow
using time_type = clock_type::time_point; using time_type = clock_type::time_point;
public: public:
task_timer(boost::asio::io_service& io_service): task_timer(asio::io_service& io_service):
io_service_(io_service), deadline_timer_(io_service_) io_service_(io_service), timer_(io_service_)
{ {
deadline_timer_.expires_from_now(boost::posix_time::seconds(1)); timer_.expires_after(std::chrono::seconds(1));
deadline_timer_.async_wait( timer_.async_wait(
std::bind(&task_timer::tick_handler, this, std::placeholders::_1)); std::bind(&task_timer::tick_handler, this, std::placeholders::_1));
} }
~task_timer() { deadline_timer_.cancel(); } ~task_timer() { timer_.cancel(); }
void cancel(identifier_type id) void cancel(identifier_type id)
{ {
@ -107,21 +110,21 @@ namespace crow
if (tasks_.empty()) highest_id_ = 0; if (tasks_.empty()) highest_id_ = 0;
} }
void tick_handler(const boost::system::error_code& ec) void tick_handler(const asio::error_code& ec)
{ {
if (ec) return; if (ec) return;
process_tasks(); process_tasks();
deadline_timer_.expires_from_now(boost::posix_time::seconds(1)); timer_.expires_after(std::chrono::seconds(1));
deadline_timer_.async_wait( timer_.async_wait(
std::bind(&task_timer::tick_handler, this, std::placeholders::_1)); std::bind(&task_timer::tick_handler, this, std::placeholders::_1));
} }
private: private:
std::uint8_t default_timeout_{5}; std::uint8_t default_timeout_{5};
boost::asio::io_service& io_service_; asio::io_service& io_service_;
boost::asio::deadline_timer deadline_timer_; asio::basic_waitable_timer<clock_type> timer_;
std::map<identifier_type, std::pair<time_type, task_type>> tasks_; std::map<identifier_type, std::pair<time_type, task_type>> tasks_;
// A continuosly increasing number to be issued to threads to identify them. // A continuosly increasing number to be issued to threads to identify them.

View File

@ -5,8 +5,10 @@
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <cstring> #include <cstring>
#include <cctype>
#include <functional> #include <functional>
#include <string> #include <string>
#include <sstream>
#include <unordered_map> #include <unordered_map>
#include <random> #include <random>
@ -505,7 +507,6 @@ namespace crow
{ {
static constexpr auto value = get_index_of_element_from_tuple_by_type_impl<T, N + 1, Args...>::value; static constexpr auto value = get_index_of_element_from_tuple_by_type_impl<T, N + 1, Args...>::value;
}; };
} // namespace detail } // namespace detail
namespace utility namespace utility
@ -810,5 +811,90 @@ namespace crow
#endif #endif
} }
/**
* @brief Checks two string for equality.
* Always returns false if strings differ in size.
* Defaults to case-insensitive comparison.
*/
inline static bool string_equals(const std::string& l, const std::string& r, bool case_sensitive = false)
{
if (l.length() != r.length())
return false;
for (size_t i = 0; i < l.length(); i++)
{
if (case_sensitive)
{
if (l[i] != r[i])
return false;
}
else
{
if (std::toupper(l[i]) != std::toupper(r[i]))
return false;
}
}
return true;
}
template<typename T, typename U>
inline static T lexical_cast(const U& v)
{
std::stringstream stream;
T res;
stream << v;
stream >> res;
return res;
}
template<typename T>
inline static T lexical_cast(const char* v, size_t count)
{
std::stringstream stream;
T res;
stream.write(v, count);
stream >> res;
return res;
}
/// Return a copy of the given string with its
/// leading and trailing whitespaces removed.
inline static std::string trim(const std::string& v)
{
if (v.empty())
return "";
size_t begin = 0, end = v.length();
size_t i;
for (i = 0; i < v.length(); i++)
{
if (!std::isspace(v[i]))
{
begin = i;
break;
}
}
if (i == v.length())
return "";
for (i = v.length(); i > 0; i--)
{
if (!std::isspace(v[i - 1]))
{
end = i;
break;
}
}
return v.substr(begin, end - begin);
}
} // namespace utility } // namespace utility
} // namespace crow } // namespace crow

View File

@ -1,10 +1,10 @@
#pragma once #pragma once
#include <boost/algorithm/string/predicate.hpp> #include <array>
#include <boost/array.hpp>
#include "crow/logging.h" #include "crow/logging.h"
#include "crow/socket_adaptors.h" #include "crow/socket_adaptors.h"
#include "crow/http_request.h" #include "crow/http_request.h"
#include "crow/TinySHA1.hpp" #include "crow/TinySHA1.hpp"
#include "crow/utility.h"
namespace crow namespace crow
{ {
@ -75,8 +75,8 @@ namespace crow
std::function<void(crow::websocket::connection&)> open_handler, std::function<void(crow::websocket::connection&)> open_handler,
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler, std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler,
std::function<void(crow::websocket::connection&, const std::string&)> close_handler, std::function<void(crow::websocket::connection&, const std::string&)> close_handler,
std::function<void(crow::websocket::connection&)> error_handler, std::function<void(crow::websocket::connection&, const std::string&)> error_handler,
std::function<bool(const crow::request&)> accept_handler): std::function<bool(const crow::request&, void**)> accept_handler):
adaptor_(std::move(adaptor)), adaptor_(std::move(adaptor)),
handler_(handler), handler_(handler),
max_payload_bytes_(max_payload), max_payload_bytes_(max_payload),
@ -86,7 +86,7 @@ namespace crow
error_handler_(std::move(error_handler)), error_handler_(std::move(error_handler)),
accept_handler_(std::move(accept_handler)) accept_handler_(std::move(accept_handler))
{ {
if (!boost::iequals(req.get_header_value("upgrade"), "websocket")) if (!utility::string_equals(req.get_header_value("upgrade"), "websocket"))
{ {
adaptor.close(); adaptor.close();
handler_->remove_websocket(this); handler_->remove_websocket(this);
@ -96,13 +96,15 @@ namespace crow
if (accept_handler_) if (accept_handler_)
{ {
if (!accept_handler_(req)) void* ud = nullptr;
if (!accept_handler_(req, &ud))
{ {
adaptor.close(); adaptor.close();
handler_->remove_websocket(this); handler_->remove_websocket(this);
delete this; delete this;
return; return;
} }
userdata(ud);
} }
// Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== // Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
@ -265,16 +267,25 @@ namespace crow
/// Reading the actual payload.<br> /// Reading the actual payload.<br>
void do_read() void do_read()
{ {
if (has_sent_close_ && has_recv_close_)
{
close_connection_ = true;
adaptor_.shutdown_readwrite();
adaptor_.close();
check_destroy();
return;
}
is_reading = true; is_reading = true;
switch (state_) switch (state_)
{ {
case WebSocketReadState::MiniHeader: case WebSocketReadState::MiniHeader:
{ {
mini_header_ = 0; mini_header_ = 0;
//boost::asio::async_read(adaptor_.socket(), boost::asio::buffer(&mini_header_, 1), //asio::async_read(adaptor_.socket(), asio::buffer(&mini_header_, 1),
adaptor_.socket().async_read_some( adaptor_.socket().async_read_some(
boost::asio::buffer(&mini_header_, 2), asio::buffer(&mini_header_, 2),
[this](const boost::system::error_code& ec, std::size_t [this](const asio::error_code& ec, std::size_t
#ifdef CROW_ENABLE_DEBUG #ifdef CROW_ENABLE_DEBUG
bytes_transferred bytes_transferred
#endif #endif
@ -304,7 +315,7 @@ namespace crow
adaptor_.shutdown_readwrite(); adaptor_.shutdown_readwrite();
adaptor_.close(); adaptor_.close();
if (error_handler_) if (error_handler_)
error_handler_(*this); error_handler_(*this, "Client connection not masked.");
check_destroy(); check_destroy();
#endif #endif
} }
@ -330,7 +341,7 @@ namespace crow
adaptor_.shutdown_readwrite(); adaptor_.shutdown_readwrite();
adaptor_.close(); adaptor_.close();
if (error_handler_) if (error_handler_)
error_handler_(*this); error_handler_(*this, ec.message());
check_destroy(); check_destroy();
} }
}); });
@ -340,9 +351,9 @@ namespace crow
{ {
remaining_length_ = 0; remaining_length_ = 0;
remaining_length16_ = 0; remaining_length16_ = 0;
boost::asio::async_read( asio::async_read(
adaptor_.socket(), boost::asio::buffer(&remaining_length16_, 2), adaptor_.socket(), asio::buffer(&remaining_length16_, 2),
[this](const boost::system::error_code& ec, std::size_t [this](const asio::error_code& ec, std::size_t
#ifdef CROW_ENABLE_DEBUG #ifdef CROW_ENABLE_DEBUG
bytes_transferred bytes_transferred
#endif #endif
@ -368,7 +379,7 @@ namespace crow
adaptor_.shutdown_readwrite(); adaptor_.shutdown_readwrite();
adaptor_.close(); adaptor_.close();
if (error_handler_) if (error_handler_)
error_handler_(*this); error_handler_(*this, ec.message());
check_destroy(); check_destroy();
} }
}); });
@ -376,9 +387,9 @@ namespace crow
break; break;
case WebSocketReadState::Len64: case WebSocketReadState::Len64:
{ {
boost::asio::async_read( asio::async_read(
adaptor_.socket(), boost::asio::buffer(&remaining_length_, 8), adaptor_.socket(), asio::buffer(&remaining_length_, 8),
[this](const boost::system::error_code& ec, std::size_t [this](const asio::error_code& ec, std::size_t
#ifdef CROW_ENABLE_DEBUG #ifdef CROW_ENABLE_DEBUG
bytes_transferred bytes_transferred
#endif #endif
@ -403,7 +414,7 @@ namespace crow
adaptor_.shutdown_readwrite(); adaptor_.shutdown_readwrite();
adaptor_.close(); adaptor_.close();
if (error_handler_) if (error_handler_)
error_handler_(*this); error_handler_(*this, ec.message());
check_destroy(); check_destroy();
} }
}); });
@ -415,14 +426,14 @@ namespace crow
close_connection_ = true; close_connection_ = true;
adaptor_.close(); adaptor_.close();
if (error_handler_) if (error_handler_)
error_handler_(*this); error_handler_(*this, "Message length exceeds maximum paylaod.");
check_destroy(); check_destroy();
} }
else if (has_mask_) else if (has_mask_)
{ {
boost::asio::async_read( asio::async_read(
adaptor_.socket(), boost::asio::buffer((char*)&mask_, 4), adaptor_.socket(), asio::buffer((char*)&mask_, 4),
[this](const boost::system::error_code& ec, std::size_t [this](const asio::error_code& ec, std::size_t
#ifdef CROW_ENABLE_DEBUG #ifdef CROW_ENABLE_DEBUG
bytes_transferred bytes_transferred
#endif #endif
@ -444,7 +455,7 @@ namespace crow
{ {
close_connection_ = true; close_connection_ = true;
if (error_handler_) if (error_handler_)
error_handler_(*this); error_handler_(*this, ec.message());
adaptor_.shutdown_readwrite(); adaptor_.shutdown_readwrite();
adaptor_.close(); adaptor_.close();
check_destroy(); check_destroy();
@ -463,8 +474,8 @@ namespace crow
if (remaining_length_ < to_read) if (remaining_length_ < to_read)
to_read = remaining_length_; to_read = remaining_length_;
adaptor_.socket().async_read_some( adaptor_.socket().async_read_some(
boost::asio::buffer(buffer_, static_cast<std::size_t>(to_read)), asio::buffer(buffer_, static_cast<std::size_t>(to_read)),
[this](const boost::system::error_code& ec, std::size_t bytes_transferred) { [this](const asio::error_code& ec, std::size_t bytes_transferred) {
is_reading = false; is_reading = false;
if (!ec) if (!ec)
@ -473,10 +484,12 @@ namespace crow
remaining_length_ -= bytes_transferred; remaining_length_ -= bytes_transferred;
if (remaining_length_ == 0) if (remaining_length_ == 0)
{ {
handle_fragment(); if (handle_fragment())
{
state_ = WebSocketReadState::MiniHeader; state_ = WebSocketReadState::MiniHeader;
do_read(); do_read();
} }
}
else else
do_read(); do_read();
} }
@ -484,7 +497,7 @@ namespace crow
{ {
close_connection_ = true; close_connection_ = true;
if (error_handler_) if (error_handler_)
error_handler_(*this); error_handler_(*this, ec.message());
adaptor_.shutdown_readwrite(); adaptor_.shutdown_readwrite();
adaptor_.close(); adaptor_.close();
check_destroy(); check_destroy();
@ -511,7 +524,7 @@ namespace crow
/// ///
/// Unmasks the fragment, checks the opcode, merges fragments into 1 message body, and calls the appropriate handler. /// Unmasks the fragment, checks the opcode, merges fragments into 1 message body, and calls the appropriate handler.
void handle_fragment() bool handle_fragment()
{ {
if (has_mask_) if (has_mask_)
{ {
@ -576,6 +589,7 @@ namespace crow
is_close_handler_called_ = true; is_close_handler_called_ = true;
} }
check_destroy(); check_destroy();
return false;
} }
} }
break; break;
@ -592,6 +606,7 @@ namespace crow
} }
fragment_.clear(); fragment_.clear();
return true;
} }
/// Send the buffers' data through the socket. /// Send the buffers' data through the socket.
@ -603,15 +618,15 @@ namespace crow
if (sending_buffers_.empty()) if (sending_buffers_.empty())
{ {
sending_buffers_.swap(write_buffers_); sending_buffers_.swap(write_buffers_);
std::vector<boost::asio::const_buffer> buffers; std::vector<asio::const_buffer> buffers;
buffers.reserve(sending_buffers_.size()); buffers.reserve(sending_buffers_.size());
for (auto& s : sending_buffers_) for (auto& s : sending_buffers_)
{ {
buffers.emplace_back(boost::asio::buffer(s)); buffers.emplace_back(asio::buffer(s));
} }
boost::asio::async_write( asio::async_write(
adaptor_.socket(), buffers, adaptor_.socket(), buffers,
[&](const boost::system::error_code& ec, std::size_t /*bytes_transferred*/) { [&](const asio::error_code& ec, std::size_t /*bytes_transferred*/) {
sending_buffers_.clear(); sending_buffers_.clear();
if (!ec && !close_connection_) if (!ec && !close_connection_)
{ {
@ -648,7 +663,7 @@ namespace crow
std::vector<std::string> sending_buffers_; std::vector<std::string> sending_buffers_;
std::vector<std::string> write_buffers_; std::vector<std::string> write_buffers_;
boost::array<char, 4096> buffer_; std::array<char, 4096> buffer_;
bool is_binary_; bool is_binary_;
std::string message_; std::string message_;
std::string fragment_; std::string fragment_;
@ -670,8 +685,8 @@ namespace crow
std::function<void(crow::websocket::connection&)> open_handler_; std::function<void(crow::websocket::connection&)> open_handler_;
std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_; std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
std::function<void(crow::websocket::connection&, const std::string&)> close_handler_; std::function<void(crow::websocket::connection&, const std::string&)> close_handler_;
std::function<void(crow::websocket::connection&)> error_handler_; std::function<void(crow::websocket::connection&, const std::string&)> error_handler_;
std::function<bool(const crow::request&)> accept_handler_; std::function<bool(const crow::request&, void**)> accept_handler_;
}; };
} // namespace websocket } // namespace websocket
} // namespace crow } // namespace crow

View File

@ -11,7 +11,8 @@ theme:
font: false font: false
language: 'en' language: 'en'
features: features:
navigation.tabs - navigation.tabs
- content.code.annotate
favicon: 'assets/favicon.svg' favicon: 'assets/favicon.svg'
logo: 'assets/favicon.svg' logo: 'assets/favicon.svg'
icon: icon:
@ -54,6 +55,7 @@ nav:
- MacOS: getting_started/setup/macos.md - MacOS: getting_started/setup/macos.md
- Windows: getting_started/setup/windows.md - Windows: getting_started/setup/windows.md
- Your First Application: getting_started/your_first_application.md - Your First Application: getting_started/your_first_application.md
- A simple Webpage: getting_started/a_simple_webpage.md
- Guides: - Guides:
- Different parts of Crow: - Different parts of Crow:
- App: guides/app.md - App: guides/app.md

View File

@ -6,7 +6,7 @@ pkgdesc="A Fast and Easy to use C++ microframework for the web."
arch=(any) arch=(any)
url="https://crowcpp.org" url="https://crowcpp.org"
license=('custom:BSD-3-Clause') license=('custom:BSD-3-Clause')
depends=('boost>=1.64.0') depends=('asio')
optdepends=('openssl: HTTPS support' 'zlib: HTTP compression support' 'cmake: Choose this if you plan on using CMake for your Crow project') optdepends=('openssl: HTTPS support' 'zlib: HTTP compression support' 'cmake: Choose this if you plan on using CMake for your Crow project')
conflicts=("$pkgname-git") conflicts=("$pkgname-git")
changelog='changelog.md' changelog='changelog.md'

View File

@ -8,8 +8,6 @@
#define LOCALHOST_ADDRESS "127.0.0.1" #define LOCALHOST_ADDRESS "127.0.0.1"
using namespace boost;
// TODO(EDev): SSL test with .pem file // TODO(EDev): SSL test with .pem file
TEST_CASE("SSL") TEST_CASE("SSL")
{ {
@ -76,8 +74,8 @@ TEST_CASE("SSL")
CHECK(std::string("Hello world, I'm keycrt.").substr((z * -1)) == to_test); CHECK(std::string("Hello world, I'm keycrt.").substr((z * -1)) == to_test);
} }
boost::system::error_code ec; asio::error_code ec;
c.lowest_layer().shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, ec); c.lowest_layer().shutdown(asio::socket_base::shutdown_type::shutdown_both, ec);
} }
/* /*

View File

@ -596,7 +596,7 @@ TEST_CASE("undefined_status_code")
asio::ip::tcp::socket socket(is); asio::ip::tcp::socket socket(is);
socket.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), app.port())); socket.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(LOCALHOST_ADDRESS), app.port()));
boost::asio::streambuf request; asio::streambuf request;
std::ostream request_stream(&request); std::ostream request_stream(&request);
request_stream << "GET " << route << " HTTP/1.0\r\n"; request_stream << "GET " << route << " HTTP/1.0\r\n";
request_stream << "Host: " << LOCALHOST_ADDRESS << "\r\n"; request_stream << "Host: " << LOCALHOST_ADDRESS << "\r\n";
@ -604,10 +604,10 @@ TEST_CASE("undefined_status_code")
request_stream << "Connection: close\r\n\r\n"; request_stream << "Connection: close\r\n\r\n";
// Send the request. // Send the request.
boost::asio::write(socket, request); asio::write(socket, request);
boost::asio::streambuf response; asio::streambuf response;
boost::asio::read_until(socket, response, "\r\n"); asio::read_until(socket, response, "\r\n");
std::istream response_stream(&response); std::istream response_stream(&response);
std::string http_version; std::string http_version;
@ -759,7 +759,7 @@ TEST_CASE("json_read_real")
for (auto x : v) for (auto x : v)
{ {
CROW_LOG_DEBUG << x; CROW_LOG_DEBUG << x;
CHECK(json::load(x).d() == boost::lexical_cast<double>(x)); CHECK(json::load(x).d() == utility::lexical_cast<double>(x));
} }
auto ret = json::load( auto ret = json::load(
@ -1702,9 +1702,15 @@ TEST_CASE("middleware_cookieparser_format")
} }
// expires // expires
{ {
auto tp = boost::posix_time::time_from_string("2000-11-01 23:59:59.000"); std::time_t tp;
std::time(&tp);
std::tm* tm = std::gmtime(&tp);
std::istringstream ss("2000-11-01 23:59:59");
ss >> std::get_time(tm, "%Y-%m-%d %H:%M:%S");
std::mktime(tm);
auto c = Cookie("key", "value") auto c = Cookie("key", "value")
.expires(boost::posix_time::to_tm(tp)); .expires(*tm);
auto s = c.dump(); auto s = c.dump();
CHECK(valid(s, 2)); CHECK(valid(s, 2));
CHECK(s.find("Expires=Wed, 01 Nov 2000 23:59:59 GMT") != std::string::npos); CHECK(s.find("Expires=Wed, 01 Nov 2000 23:59:59 GMT") != std::string::npos);
@ -1936,10 +1942,10 @@ TEST_CASE("simple_url_params")
c.receive(asio::buffer(buf, 2048)); c.receive(asio::buffer(buf, 2048));
c.close(); c.close();
CHECK(boost::lexical_cast<int>(last_url_params.get("int")) == 100); CHECK(utility::lexical_cast<int>(last_url_params.get("int")) == 100);
CHECK(boost::lexical_cast<double>(last_url_params.get("double")) == CHECK(utility::lexical_cast<double>(last_url_params.get("double")) ==
123.45); 123.45);
CHECK(boost::lexical_cast<bool>(last_url_params.get("boolean"))); CHECK(utility::lexical_cast<bool>(last_url_params.get("boolean")));
} }
// check single array value // check single array value
sendmsg = "GET /params?tmnt[]=leonardo\r\n\r\n"; sendmsg = "GET /params?tmnt[]=leonardo\r\n\r\n";
@ -2240,7 +2246,7 @@ TEST_CASE("stream_response")
app.validate(); app.validate();
// running the test on a separate thread to allow the client to sleep // running the test on a separate thread to allow the client to sleep
std::thread runTest([&app, &key_response, key_response_size]() { std::thread runTest([&app, &key_response, key_response_size, keyword_]() {
auto _ = app.bindaddr(LOCALHOST_ADDRESS).port(45451).run_async(); auto _ = app.bindaddr(LOCALHOST_ADDRESS).port(45451).run_async();
app.wait_for_server_start(); app.wait_for_server_start();
asio::io_service is; asio::io_service is;
@ -2257,15 +2263,21 @@ TEST_CASE("stream_response")
asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
c.send(asio::buffer(sendmsg)); c.send(asio::buffer(sendmsg));
//consuming the headers, since we don't need those for the test // consuming the headers, since we don't need those for the test
static char buf[2048]; static char buf[2048];
size_t received_headers_bytes = 0; size_t received_headers_bytes = 0;
// magic number is 102 (it's the size of the headers, which is how much this line below needs to read) // Magic number is 102. It's the size of the headers, which is at
const size_t headers_bytes = 102; // least how much we need to read. Since the header size may change
while (received_headers_bytes < headers_bytes) // and break the test, we read twice as much as the header and
received_headers_bytes += c.receive(asio::buffer(buf, 102)); // search in the received data for the first occurrence of keyword_.
received += received_headers_bytes - headers_bytes; //add any extra that might have been received to the proper received count const size_t headers_bytes_and_some = 102 * 2;
while (received_headers_bytes < headers_bytes_and_some)
received_headers_bytes += c.receive(asio::buffer(buf + received_headers_bytes,
sizeof(buf) / sizeof(buf[0]) - received_headers_bytes));
const std::string::size_type header_end_pos = std::string(buf, received_headers_bytes).find(keyword_);
received += received_headers_bytes - header_end_pos; // add any extra that might have been received to the proper received count
while (received < key_response_size) while (received < key_response_size)
{ {
@ -2512,8 +2524,8 @@ TEST_CASE("websocket_max_payload")
} }
} }
boost::system::error_code ec; asio::error_code ec;
c.lowest_layer().shutdown(boost::asio::socket_base::shutdown_type::shutdown_both, ec); c.lowest_layer().shutdown(asio::socket_base::shutdown_type::shutdown_both, ec);
app.stop(); app.stop();
} // websocket_max_payload } // websocket_max_payload
@ -2915,12 +2927,18 @@ TEST_CASE("blueprint")
TEST_CASE("base64") TEST_CASE("base64")
{ {
unsigned char sample_bin[] = {0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e}; unsigned char sample_bin[] = {0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e};
std::string sample_bin_str(reinterpret_cast<char const*>(sample_bin),
sizeof(sample_bin) / sizeof(sample_bin[0]));
std::string sample_bin_enc = "FPucA9l+"; std::string sample_bin_enc = "FPucA9l+";
std::string sample_bin_enc_url = "FPucA9l-"; std::string sample_bin_enc_url = "FPucA9l-";
unsigned char sample_bin1[] = {0x14, 0xfb, 0x9c, 0x03, 0xd9}; unsigned char sample_bin1[] = {0x14, 0xfb, 0x9c, 0x03, 0xd9};
std::string sample_bin1_str(reinterpret_cast<char const*>(sample_bin1),
sizeof(sample_bin1) / sizeof(sample_bin1[0]));
std::string sample_bin1_enc = "FPucA9k="; std::string sample_bin1_enc = "FPucA9k=";
std::string sample_bin1_enc_np = "FPucA9k"; std::string sample_bin1_enc_np = "FPucA9k";
unsigned char sample_bin2[] = {0x14, 0xfb, 0x9c, 0x03}; unsigned char sample_bin2[] = {0x14, 0xfb, 0x9c, 0x03};
std::string sample_bin2_str(reinterpret_cast<char const*>(sample_bin2),
sizeof(sample_bin2) / sizeof(sample_bin2[0]));
std::string sample_bin2_enc = "FPucAw=="; std::string sample_bin2_enc = "FPucAw==";
std::string sample_bin2_enc_np = "FPucAw"; std::string sample_bin2_enc_np = "FPucAw";
std::string sample_text = "Crow Allows users to handle requests that may not come from the network. This is done by calling the handle(req, res) method and providing a request and response objects. Which causes crow to identify and run the appropriate handler, returning the resulting response."; std::string sample_text = "Crow Allows users to handle requests that may not come from the network. This is done by calling the handle(req, res) method and providing a request and response objects. Which causes crow to identify and run the appropriate handler, returning the resulting response.";
@ -2928,20 +2946,19 @@ TEST_CASE("base64")
CHECK(crow::utility::base64encode(sample_text, sample_text.length()) == sample_base64); CHECK(crow::utility::base64encode(sample_text, sample_text.length()) == sample_base64);
CHECK(crow::utility::base64encode((unsigned char*)sample_bin, 6) == sample_bin_enc); CHECK(crow::utility::base64encode(sample_bin, 6) == sample_bin_enc);
CHECK(crow::utility::base64encode_urlsafe((unsigned char*)sample_bin, 6) == sample_bin_enc_url); CHECK(crow::utility::base64encode_urlsafe(sample_bin, 6) == sample_bin_enc_url);
CHECK(crow::utility::base64encode((unsigned char*)sample_bin1, 5) == sample_bin1_enc); CHECK(crow::utility::base64encode(sample_bin1, 5) == sample_bin1_enc);
CHECK(crow::utility::base64encode((unsigned char*)sample_bin2, 4) == sample_bin2_enc); CHECK(crow::utility::base64encode(sample_bin2, 4) == sample_bin2_enc);
CHECK(crow::utility::base64decode(sample_base64) == sample_text); CHECK(crow::utility::base64decode(sample_base64) == sample_text);
CHECK(crow::utility::base64decode(sample_base64, sample_base64.length()) == sample_text); CHECK(crow::utility::base64decode(sample_base64, sample_base64.length()) == sample_text);
CHECK(crow::utility::base64decode(sample_bin_enc, 8) == std::string(reinterpret_cast<char const*>(sample_bin))); CHECK(crow::utility::base64decode(sample_bin_enc, 8) == sample_bin_str);
CHECK(crow::utility::base64decode(sample_bin_enc_url, 8) == std::string(reinterpret_cast<char const*>(sample_bin))); CHECK(crow::utility::base64decode(sample_bin_enc_url, 8) == sample_bin_str);
CHECK(crow::utility::base64decode(sample_bin1_enc, 8) == std::string(reinterpret_cast<char const*>(sample_bin1)).substr(0, 5)); CHECK(crow::utility::base64decode(sample_bin1_enc, 8) == sample_bin1_str);
CHECK(crow::utility::base64decode(sample_bin1_enc_np, 7) == std::string(reinterpret_cast<char const*>(sample_bin1)).substr(0, 5)); CHECK(crow::utility::base64decode(sample_bin1_enc_np, 7) == sample_bin1_str);
CHECK(crow::utility::base64decode(sample_bin2_enc, 8) == std::string(reinterpret_cast<char const*>(sample_bin2)).substr(0, 4)); CHECK(crow::utility::base64decode(sample_bin2_enc, 8) == sample_bin2_str);
CHECK(crow::utility::base64decode(sample_bin2_enc_np, 6) == std::string(reinterpret_cast<char const*>(sample_bin2)).substr(0, 4)); CHECK(crow::utility::base64decode(sample_bin2_enc_np, 6) == sample_bin2_str);
} // base64 } // base64
TEST_CASE("sanitize_filename") TEST_CASE("sanitize_filename")
@ -3012,7 +3029,7 @@ TEST_CASE("timeout")
asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451)); asio::ip::address::from_string(LOCALHOST_ADDRESS), 45451));
auto receive_future = async(launch::async, [&]() { auto receive_future = async(launch::async, [&]() {
boost::system::error_code ec; asio::error_code ec;
c.receive(asio::buffer(buf, 2048), 0, ec); c.receive(asio::buffer(buf, 2048), 0, ec);
return ec; return ec;
}); });
@ -3032,7 +3049,7 @@ TEST_CASE("timeout")
size_t received; size_t received;
auto receive_future = async(launch::async, [&]() { auto receive_future = async(launch::async, [&]() {
boost::system::error_code ec; asio::error_code ec;
received = c.receive(asio::buffer(buf, 2048), 0, ec); received = c.receive(asio::buffer(buf, 2048), 0, ec);
return ec; return ec;
}); });
@ -3058,9 +3075,9 @@ TEST_CASE("timeout")
TEST_CASE("task_timer") TEST_CASE("task_timer")
{ {
using work_guard_type = boost::asio::executor_work_guard<boost::asio::io_service::executor_type>; using work_guard_type = asio::executor_work_guard<asio::io_service::executor_type>;
boost::asio::io_service io_service; asio::io_service io_service;
work_guard_type work_guard(io_service.get_executor()); work_guard_type work_guard(io_service.get_executor());
thread io_thread([&io_service]() { thread io_thread([&io_service]() {
io_service.run(); io_service.run();
@ -3095,3 +3112,39 @@ TEST_CASE("task_timer")
io_service.stop(); io_service.stop();
io_thread.join(); io_thread.join();
} // task_timer } // task_timer
TEST_CASE("trim")
{
CHECK(utility::trim("") == "");
CHECK(utility::trim("0") == "0");
CHECK(utility::trim(" a") == "a");
CHECK(utility::trim("b ") == "b");
CHECK(utility::trim(" c ") == "c");
CHECK(utility::trim(" a b ") == "a b");
CHECK(utility::trim(" ") == "");
}
TEST_CASE("string_equals")
{
CHECK(utility::string_equals("a", "aa") == false);
CHECK(utility::string_equals("a", "b") == false);
CHECK(utility::string_equals("", "") == true);
CHECK(utility::string_equals("abc", "abc") == true);
CHECK(utility::string_equals("ABC", "abc") == true);
CHECK(utility::string_equals("a", "aa", true) == false);
CHECK(utility::string_equals("a", "b", true) == false);
CHECK(utility::string_equals("", "", true) == true);
CHECK(utility::string_equals("abc", "abc", true) == true);
CHECK(utility::string_equals("ABC", "abc", true) == false);
}
TEST_CASE("lexical_cast")
{
CHECK(utility::lexical_cast<int>(4) == 4);
CHECK(utility::lexical_cast<double>(4) == 4.0);
CHECK(utility::lexical_cast<int>("5") == 5);
CHECK(utility::lexical_cast<string>(4) == "4");
CHECK(utility::lexical_cast<float>("10", 2) == 10.0f);
}

View File

@ -3,32 +3,7 @@
"version-string": "master", "version-string": "master",
"dependencies": [ "dependencies": [
{ {
"name": "boost-array", "name": "asio"
"version>=": "1.70.0"
},
{
"name": "boost-algorithm",
"version>=": "1.70.0"
},
{
"name": "boost-asio",
"version>=": "1.70.0"
},
{
"name": "boost-date-time",
"version>=": "1.70.0"
},
{
"name": "boost-functional",
"version>=": "1.70.0"
},
{
"name": "boost-lexical-cast",
"version>=": "1.70.0"
},
{
"name": "boost-optional",
"version>=": "1.70.0"
}, },
{ {
"name": "openssl" "name": "openssl"
@ -37,35 +12,5 @@
"name": "zlib" "name": "zlib"
} }
], ],
"overrides": [
{
"name": "boost-array",
"version": "1.70.0"
},
{
"name": "boost-algorithm",
"version": "1.70.0"
},
{
"name": "boost-asio",
"version-semver": "1.70.0-2"
},
{
"name": "boost-date-time",
"version": "1.70.0"
},
{
"name": "boost-functional",
"version": "1.70.0"
},
{
"name": "boost-lexical-cast",
"version": "1.70.0"
},
{
"name": "boost-optional",
"version": "1.70.0"
}
],
"builtin-baseline": "44d94c2edbd44f0c01d66c2ad95eb6982a9a61bc" "builtin-baseline": "44d94c2edbd44f0c01d66c2ad95eb6982a9a61bc"
} }