mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-04-13 20:28:30 +00:00
Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a444b612af | ||
|
|
ed6d949f42 | ||
|
|
d28cd3f937 | ||
|
|
8cc3e6c434 | ||
|
|
26fbc1b7c0 | ||
|
|
0dc653f45a | ||
|
|
7a58c0a430 | ||
|
|
dabaa51a7d | ||
|
|
a1cfc0f377 | ||
|
|
eb4fcb5003 | ||
|
|
ae43c96984 | ||
|
|
9c81693801 | ||
|
|
80202c9f62 | ||
|
|
094a6a614a | ||
|
|
39c7bba7b9 | ||
|
|
f2476f21fc | ||
|
|
c776454c84 | ||
|
|
82a5ac735f | ||
|
|
08bf806e92 | ||
|
|
9a41b16cbb | ||
|
|
10759f0a38 | ||
|
|
58b2814fda | ||
|
|
260422b7d7 | ||
|
|
d2c7b447d5 | ||
|
|
72b20c08da | ||
|
|
afd6d5f9dc | ||
|
|
e5827ad16f | ||
|
|
5324b3d661 | ||
|
|
151ccba57e | ||
|
|
69a28d50f6 | ||
|
|
048f31109f | ||
|
|
d064fb7ff2 | ||
|
|
3c2736bb2a | ||
|
|
fd4e1b4112 | ||
|
|
f6a2365ca5 | ||
|
|
df1ff7510b | ||
|
|
379905bd34 | ||
|
|
66719ae3d4 | ||
|
|
bc9251ea49 | ||
|
|
a9e942d755 | ||
|
|
e1785d6723 | ||
|
|
b9539b8921 | ||
|
|
4c93b973ff | ||
|
|
033bc35723 | ||
|
|
d910bfc303 | ||
|
|
b69c0a1dcb | ||
|
|
5e37e38398 | ||
|
|
295e4d58aa | ||
|
|
448de6a9c6 | ||
|
|
6f58dc728f | ||
|
|
905f2d84f4 | ||
|
|
880f7fa62b | ||
|
|
8f3dbf7f21 | ||
|
|
924a557fa3 | ||
|
|
d8da740597 | ||
|
|
d45676b064 | ||
|
|
94d13e88a5 | ||
|
|
4f9d04cb8e | ||
|
|
9fb11986a5 | ||
|
|
55d04ee354 | ||
|
|
a62a48a7b5 | ||
|
|
c652919954 | ||
|
|
58753ba33c | ||
|
|
5706828d2c | ||
|
|
e743b8cd57 | ||
|
|
9d57899352 | ||
|
|
d03937e144 | ||
|
|
8fb37a449d | ||
|
|
f0b1b5dbfd | ||
|
|
5f32c424c2 | ||
|
|
f0683f2301 | ||
|
|
0d527e2b83 | ||
|
|
bea3ebd7af | ||
|
|
380f725713 | ||
|
|
a106bd314c | ||
|
|
e4fd9f19ca | ||
|
|
dfc01338eb | ||
|
|
c4ebc31345 | ||
|
|
d1abf96581 | ||
|
|
001b8a5529 | ||
|
|
7a3abd2768 | ||
|
|
4a52524f47 | ||
|
|
89e1e9b8fe | ||
|
|
98d16eb836 | ||
|
|
bcf0c32245 | ||
|
|
dcdb0d047b | ||
|
|
1f86e41d97 | ||
|
|
6d8302313c | ||
|
|
89440ec322 | ||
|
|
5edf455d72 | ||
|
|
5f49c13f95 | ||
|
|
760bccc3ad | ||
|
|
a99e02aeb3 | ||
|
|
4aae1dcc42 | ||
|
|
f23f9a06a9 | ||
|
|
46466b1e28 | ||
|
|
224119a60a | ||
|
|
c02849e269 | ||
|
|
71979b1e88 | ||
|
|
a62d1f79f4 | ||
|
|
b14b7b0f8f | ||
|
|
9dbe0d855c | ||
|
|
2cef8df6ae | ||
|
|
94fc229c44 | ||
|
|
a7052cba22 | ||
|
|
c47c6b3910 | ||
|
|
c946eb7699 | ||
|
|
1f99ad5d6e | ||
|
|
96e372bad2 | ||
|
|
fc56f39d17 | ||
|
|
5844432a7b | ||
|
|
b97420f363 | ||
|
|
81610ee080 | ||
|
|
c7f8561472 | ||
|
|
47bc7456d2 | ||
|
|
4ab9270660 | ||
|
|
d599a36c2a | ||
|
|
6f8f51496d | ||
|
|
0c293887d0 | ||
|
|
7e92ffec48 | ||
|
|
531708816a | ||
|
|
bfec81998b | ||
|
|
c9238434e1 |
5
.clang-format
Normal file
5
.clang-format
Normal file
@@ -0,0 +1,5 @@
|
||||
BasedOnStyle: LLVM
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
Cpp11BracedListStyle: true
|
||||
17
.github/workflows/test.yaml
vendored
Normal file
17
.github/workflows/test.yaml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macOS-latest, ubuntu-latest]
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: cd test && make
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@ example/hello
|
||||
example/simplesvr
|
||||
example/benchmark
|
||||
example/redirect
|
||||
example/upload
|
||||
example/*.pem
|
||||
test/test
|
||||
test/test.xcodeproj/xcuser*
|
||||
|
||||
138
README.md
138
README.md
@@ -1,6 +1,7 @@
|
||||
cpp-httplib
|
||||
===========
|
||||
|
||||
[](https://github.com/yhirose/cpp-httplib/actions)
|
||||
[](https://travis-ci.org/yhirose/cpp-httplib)
|
||||
[](https://ci.appveyor.com/project/yhirose/cpp-httplib)
|
||||
|
||||
@@ -49,7 +50,16 @@ svr.listen_after_bind();
|
||||
### Static File Server
|
||||
|
||||
```cpp
|
||||
svr.set_base_dir("./www");
|
||||
svr.set_base_dir("./www"); // This is same as `svr.set_base_dir("./www", "/")`;
|
||||
```
|
||||
|
||||
```cpp
|
||||
svr.set_base_dir("./www", "/public");
|
||||
```
|
||||
|
||||
```cpp
|
||||
svr.set_base_dir("./www1", "/public"); // 1st order
|
||||
svr.set_base_dir("./www2", "/public"); // 2nd order
|
||||
```
|
||||
|
||||
### Logging
|
||||
@@ -64,7 +74,7 @@ svr.set_logger([](const auto& req, const auto& res) {
|
||||
|
||||
```cpp
|
||||
svr.set_error_handler([](const auto& req, auto& res) {
|
||||
const char* fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
|
||||
auto fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
|
||||
char buf[BUFSIZ];
|
||||
snprintf(buf, sizeof(buf), fmt, res.status);
|
||||
res.set_content(buf, "text/html");
|
||||
@@ -76,15 +86,16 @@ svr.set_error_handler([](const auto& req, auto& res) {
|
||||
```cpp
|
||||
svr.Post("/multipart", [&](const auto& req, auto& res) {
|
||||
auto size = req.files.size();
|
||||
auto ret = req.has_file("name1"));
|
||||
auto ret = req.has_file("name1");
|
||||
const auto& file = req.get_file_value("name1");
|
||||
// file.filename;
|
||||
// file.content_type;
|
||||
auto body = req.body.substr(file.offset, file.length));
|
||||
})
|
||||
// file.content;
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
### Stream content with Content provider
|
||||
### Send content with Content provider
|
||||
|
||||
```cpp
|
||||
const uint64_t DATA_CHUNK_SIZE = 4;
|
||||
@@ -94,30 +105,57 @@ svr.Get("/stream", [&](const Request &req, Response &res) {
|
||||
|
||||
res.set_content_provider(
|
||||
data->size(), // Content length
|
||||
[data](uint64_t offset, uint64_t length, Out out) {
|
||||
[data](uint64_t offset, uint64_t length, DataSink sink) {
|
||||
const auto &d = *data;
|
||||
out(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
||||
sink(&d[offset], std::min(length, DATA_CHUNK_SIZE));
|
||||
},
|
||||
[data] { delete data; });
|
||||
});
|
||||
```
|
||||
|
||||
### Receive content with Content receiver
|
||||
|
||||
```cpp
|
||||
svr.Post("/content_receiver",
|
||||
[&](const Request &req, Response &res, const ContentReader &content_reader) {
|
||||
if (req.is_multipart_form_data()) {
|
||||
MultipartFormDataItems files;
|
||||
content_reader(
|
||||
[&](const MultipartFormData &file) {
|
||||
files.push_back(file);
|
||||
return true;
|
||||
},
|
||||
[&](const char *data, size_t data_length) {
|
||||
files.back().content.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
std::string body;
|
||||
content_reader([&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
res.set_content(body, "text/plain");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Chunked transfer encoding
|
||||
|
||||
```cpp
|
||||
svr.Get("/chunked", [&](const Request& req, Response& res) {
|
||||
res.set_chunked_content_provider(
|
||||
[](uint64_t offset, Out out, Done done) {
|
||||
out("123", 3);
|
||||
out("345", 3);
|
||||
out("789", 3);
|
||||
[](uint64_t offset, DataSink sink, Done done) {
|
||||
sink("123", 3);
|
||||
sink("345", 3);
|
||||
sink("789", 3);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### Default thread pool supporet
|
||||
### Default thread pool support
|
||||
|
||||
Set thread count to 8:
|
||||
|
||||
@@ -192,8 +230,9 @@ int main(void)
|
||||
std::string body;
|
||||
|
||||
auto res = cli.Get("/large-data",
|
||||
[&](const char *data, uint64_t data_length, uint64_t offset, uint64_t content_length) {
|
||||
[&](const char *data, uint64_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
|
||||
assert(res->body.empty());
|
||||
@@ -262,7 +301,7 @@ res = cli.Options("/resource/foo");
|
||||
### Connection Timeout
|
||||
|
||||
```c++
|
||||
httplib::Client cli("localhost", 8080, 5); // timeouts in 5 seconds
|
||||
cli.set_timeout_sec(5); // timeouts in 5 seconds
|
||||
```
|
||||
### With Progress Callback
|
||||
|
||||
@@ -284,18 +323,32 @@ std::shared_ptr<httplib::Response> res =
|
||||
|
||||
This feature was contributed by [underscorediscovery](https://github.com/yhirose/cpp-httplib/pull/23).
|
||||
|
||||
### Basic Authentication
|
||||
### Authentication
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("httplib.org");
|
||||
// Basic Authentication
|
||||
cli.set_basic_auth("user", "pass");
|
||||
|
||||
auto res = cli.Get("/basic-auth/hello/world", {
|
||||
httplib::make_basic_authentication_header("hello", "world")
|
||||
});
|
||||
// res->status should be 200
|
||||
// res->body should be "{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n".
|
||||
// Digest Authentication
|
||||
cli.set_digest_auth("user", "pass");
|
||||
```
|
||||
|
||||
NOTE: OpenSSL is required for Digest Authentication.
|
||||
|
||||
### Proxy server support
|
||||
|
||||
```cpp
|
||||
cli.set_proxy("host", port);
|
||||
|
||||
// Basic Authentication
|
||||
cli.set_proxy_basic_auth("user", "pass");
|
||||
|
||||
// Digest Authentication
|
||||
cli.set_proxy_digest_auth("user", "pass");
|
||||
```
|
||||
|
||||
NOTE: OpenSSL is required for Digest Authentication.
|
||||
|
||||
### Range
|
||||
|
||||
```cpp
|
||||
@@ -333,11 +386,34 @@ if (cli.send(requests, responses)) {
|
||||
}
|
||||
```
|
||||
|
||||
### Redirect
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("yahoo.com");
|
||||
|
||||
auto res = cli.Get("/");
|
||||
res->status; // 301
|
||||
|
||||
cli.set_follow_location(true);
|
||||
res = cli.Get("/");
|
||||
res->status; // 200
|
||||
```
|
||||
|
||||
### Use a specitic network interface
|
||||
|
||||
NOTE: This feature is not available on Windows, yet.
|
||||
|
||||
```cpp
|
||||
cli.set_interface("eth0"); // Interface name, IP address or host name
|
||||
```
|
||||
|
||||
OpenSSL Support
|
||||
---------------
|
||||
|
||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||
|
||||
NOTE: cpp-httplib supports 1.1.1 (until 2023-09-11) and 1.0.2 (2019-12-31).
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
|
||||
@@ -362,10 +438,26 @@ The server applies gzip compression to the following MIME type contents:
|
||||
* application/xml
|
||||
* application/xhtml+xml
|
||||
|
||||
### Compress content on client
|
||||
|
||||
```c++
|
||||
cli.set_compress(true);
|
||||
res = cli.Post("/resource/foo", "...", "text/plain");
|
||||
```
|
||||
|
||||
Split httplib.h into .h and .cc
|
||||
-------------------------------
|
||||
|
||||
```bash
|
||||
> python3 split.py
|
||||
> ls out
|
||||
httplib.h httplib.cc
|
||||
```
|
||||
|
||||
NOTE
|
||||
----
|
||||
|
||||
g++ 4.8 cannot build this library since `<regex>` in g++4.8 is [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions).
|
||||
g++ 4.8 and below cannot build this library since `<regex>` in the versions are [broken](https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions).
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -std=c++14 -I.. -Wall -Wextra -pthread
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lssl -lcrypto
|
||||
OPENSSL_DIR = /usr/local/opt/openssl
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
all: server client hello simplesvr redirect benchmark
|
||||
all: server client hello simplesvr upload redirect benchmark
|
||||
|
||||
server : server.cc ../httplib.h Makefile
|
||||
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
@@ -18,6 +19,9 @@ hello : hello.cc ../httplib.h Makefile
|
||||
simplesvr : simplesvr.cc ../httplib.h Makefile
|
||||
$(CXX) -o simplesvr $(CXXFLAGS) simplesvr.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
upload : upload.cc ../httplib.h Makefile
|
||||
$(CXX) -o upload $(CXXFLAGS) upload.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
redirect : redirect.cc ../httplib.h Makefile
|
||||
$(CXX) -o redirect $(CXXFLAGS) redirect.cc $(OPENSSL_SUPPORT) $(ZLIB_SUPPORT)
|
||||
|
||||
@@ -29,4 +33,4 @@ pem:
|
||||
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||
|
||||
clean:
|
||||
rm server client hello simplesvr redirect *.pem
|
||||
rm server client hello simplesvr upload redirect benchmark *.pem
|
||||
|
||||
@@ -44,14 +44,10 @@ int main(void) {
|
||||
#endif
|
||||
|
||||
// Run servers
|
||||
auto httpThread = std::thread([&]() {
|
||||
http.listen("localhost", 8080);
|
||||
});
|
||||
auto httpThread = std::thread([&]() { http.listen("localhost", 8080); });
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto httpsThread = std::thread([&]() {
|
||||
https.listen("localhost", 8081);
|
||||
});
|
||||
auto httpsThread = std::thread([&]() { https.listen("localhost", 8081); });
|
||||
#endif
|
||||
|
||||
httpThread.join();
|
||||
|
||||
@@ -27,7 +27,7 @@ string dump_headers(const Headers &headers) {
|
||||
return s;
|
||||
}
|
||||
|
||||
string dump_multipart_files(const MultipartFiles &files) {
|
||||
string dump_multipart_files(const MultipartFormDataMap &files) {
|
||||
string s;
|
||||
char buf[BUFSIZ];
|
||||
|
||||
@@ -46,10 +46,7 @@ string dump_multipart_files(const MultipartFiles &files) {
|
||||
snprintf(buf, sizeof(buf), "content type: %s\n", file.content_type.c_str());
|
||||
s += buf;
|
||||
|
||||
snprintf(buf, sizeof(buf), "text offset: %lu\n", file.offset);
|
||||
s += buf;
|
||||
|
||||
snprintf(buf, sizeof(buf), "text length: %lu\n", file.length);
|
||||
snprintf(buf, sizeof(buf), "text length: %lu\n", file.content.size());
|
||||
s += buf;
|
||||
|
||||
s += "----------------\n";
|
||||
|
||||
50
example/upload.cc
Normal file
50
example/upload.cc
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// upload.cc
|
||||
//
|
||||
// Copyright (c) 2019 Yuji Hirose. All rights reserved.
|
||||
// MIT License
|
||||
//
|
||||
|
||||
#include <fstream>
|
||||
#include <httplib.h>
|
||||
#include <iostream>
|
||||
using namespace httplib;
|
||||
using namespace std;
|
||||
|
||||
const char *html = R"(
|
||||
<form id="formElem">
|
||||
<input type="file" name="file" accept="image/*">
|
||||
<input type="submit">
|
||||
</form>
|
||||
<script>
|
||||
formElem.onsubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
let res = await fetch('/post', {
|
||||
method: 'POST',
|
||||
body: new FormData(formElem)
|
||||
});
|
||||
console.log(await res.text());
|
||||
};
|
||||
</script>
|
||||
)";
|
||||
|
||||
int main(void) {
|
||||
Server svr;
|
||||
|
||||
svr.Get("/", [](const Request & /*req*/, Response &res) {
|
||||
res.set_content(html, "text/html");
|
||||
});
|
||||
|
||||
svr.Post("/post", [](const Request &req, Response &res) {
|
||||
auto file = req.get_file_value("file");
|
||||
cout << "file length: " << file.content.length() << ":" << file.filename
|
||||
<< endl;
|
||||
|
||||
ofstream ofs(file.filename, ios::binary);
|
||||
ofs << file.content;
|
||||
|
||||
res.set_content("done", "text/plain");
|
||||
});
|
||||
|
||||
svr.listen("localhost", 1234);
|
||||
}
|
||||
24
split.py
Normal file
24
split.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import os
|
||||
|
||||
border = '// ----------------------------------------------------------------------------'
|
||||
|
||||
with open('httplib.h') as f:
|
||||
lines = f.readlines()
|
||||
inImplementation = False
|
||||
os.makedirs('out', exist_ok=True)
|
||||
with open('out/httplib.h', 'w') as fh:
|
||||
with open('out/httplib.cc', 'w') as fc:
|
||||
fc.write('#include "httplib.h"\n')
|
||||
fc.write('namespace httplib {\n')
|
||||
for line in lines:
|
||||
isBorderLine = border in line
|
||||
if isBorderLine:
|
||||
inImplementation = not inImplementation
|
||||
else:
|
||||
if inImplementation:
|
||||
fc.write(line.replace('inline ', ''))
|
||||
pass
|
||||
else:
|
||||
fh.write(line)
|
||||
pass
|
||||
fc.write('} // namespace httplib\n')
|
||||
@@ -1,7 +1,8 @@
|
||||
|
||||
#CXX = clang++
|
||||
CXXFLAGS = -ggdb -O0 -std=c++11 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I. -Wall -Wextra -Wtype-limits
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lssl -lcrypto
|
||||
OPENSSL_DIR = /usr/local/opt/openssl
|
||||
OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I$(OPENSSL_DIR)/include -L$(OPENSSL_DIR)/lib -lssl -lcrypto
|
||||
ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
|
||||
|
||||
all : test
|
||||
|
||||
710
test/test.cc
710
test/test.cc
@@ -28,6 +28,17 @@ const string LONG_QUERY_URL = "/long-query-value?key=" + LONG_QUERY_VALUE;
|
||||
|
||||
const std::string JSON_DATA = "{\"hello\":\"world\"}";
|
||||
|
||||
const string LARGE_DATA = string(1024 * 1024 * 100, '@'); // 100MB
|
||||
|
||||
MultipartFormData &get_file_value(MultipartFormDataItems &files,
|
||||
const char *key) {
|
||||
auto it = std::find_if(
|
||||
files.begin(), files.end(),
|
||||
[&](const MultipartFormData &file) { return file.name == key; });
|
||||
if (it != files.end()) { return *it; }
|
||||
throw std::runtime_error("invalid mulitpart form data name error");
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
TEST(StartupTest, WSAStartup) {
|
||||
WSADATA wsaData;
|
||||
@@ -193,15 +204,15 @@ TEST(ParseHeaderValueTest, Range) {
|
||||
|
||||
TEST(ChunkedEncodingTest, FromHTTPWatch) {
|
||||
auto host = "www.httpwatch.com";
|
||||
auto sec = 2;
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto port = 443;
|
||||
httplib::SSLClient cli(host, port, sec);
|
||||
httplib::SSLClient cli(host, port);
|
||||
#else
|
||||
auto port = 80;
|
||||
httplib::Client cli(host, port, sec);
|
||||
httplib::Client cli(host, port);
|
||||
#endif
|
||||
cli.set_timeout_sec(2);
|
||||
|
||||
auto res =
|
||||
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137");
|
||||
@@ -216,20 +227,20 @@ TEST(ChunkedEncodingTest, FromHTTPWatch) {
|
||||
|
||||
TEST(ChunkedEncodingTest, WithContentReceiver) {
|
||||
auto host = "www.httpwatch.com";
|
||||
auto sec = 2;
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto port = 443;
|
||||
httplib::SSLClient cli(host, port, sec);
|
||||
httplib::SSLClient cli(host, port);
|
||||
#else
|
||||
auto port = 80;
|
||||
httplib::Client cli(host, port, sec);
|
||||
httplib::Client cli(host, port);
|
||||
#endif
|
||||
cli.set_timeout_sec(2);
|
||||
|
||||
std::string body;
|
||||
auto res =
|
||||
cli.Get("/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137",
|
||||
[&](const char *data, size_t data_length, uint64_t, uint64_t) {
|
||||
[&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
@@ -242,17 +253,49 @@ TEST(ChunkedEncodingTest, WithContentReceiver) {
|
||||
EXPECT_EQ(out, body);
|
||||
}
|
||||
|
||||
TEST(RangeTest, FromHTTPBin) {
|
||||
auto host = "httpbin.org";
|
||||
auto sec = 5;
|
||||
TEST(ChunkedEncodingTest, WithResponseHandlerAndContentReceiver) {
|
||||
auto host = "www.httpwatch.com";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto port = 443;
|
||||
httplib::SSLClient cli(host, port, sec);
|
||||
httplib::SSLClient cli(host, port);
|
||||
#else
|
||||
auto port = 80;
|
||||
httplib::Client cli(host, port, sec);
|
||||
httplib::Client cli(host, port);
|
||||
#endif
|
||||
cli.set_timeout_sec(2);
|
||||
|
||||
std::string body;
|
||||
auto res = cli.Get(
|
||||
"/httpgallery/chunked/chunkedimage.aspx?0.4153841143030137", Headers(),
|
||||
[&](const Response &response) {
|
||||
EXPECT_EQ(200, response.status);
|
||||
return true;
|
||||
},
|
||||
[&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
|
||||
std::string out;
|
||||
httplib::detail::read_file("./image.jpg", out);
|
||||
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(out, body);
|
||||
}
|
||||
|
||||
TEST(RangeTest, FromHTTPBin) {
|
||||
auto host = "httpbin.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto port = 443;
|
||||
httplib::SSLClient cli(host, port);
|
||||
#else
|
||||
auto port = 80;
|
||||
httplib::Client cli(host, port);
|
||||
#endif
|
||||
cli.set_timeout_sec(5);
|
||||
|
||||
{
|
||||
httplib::Headers headers;
|
||||
@@ -303,16 +346,16 @@ TEST(RangeTest, FromHTTPBin) {
|
||||
}
|
||||
|
||||
TEST(ConnectionErrorTest, InvalidHost) {
|
||||
auto host = "abcde.com";
|
||||
auto sec = 2;
|
||||
auto host = "-abcde.com";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto port = 443;
|
||||
httplib::SSLClient cli(host, port, sec);
|
||||
httplib::SSLClient cli(host, port);
|
||||
#else
|
||||
auto port = 80;
|
||||
httplib::Client cli(host, port, sec);
|
||||
httplib::Client cli(host, port);
|
||||
#endif
|
||||
cli.set_timeout_sec(2);
|
||||
|
||||
auto res = cli.Get("/");
|
||||
ASSERT_TRUE(res == nullptr);
|
||||
@@ -320,15 +363,15 @@ TEST(ConnectionErrorTest, InvalidHost) {
|
||||
|
||||
TEST(ConnectionErrorTest, InvalidPort) {
|
||||
auto host = "localhost";
|
||||
auto sec = 2;
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto port = 44380;
|
||||
httplib::SSLClient cli(host, port, sec);
|
||||
httplib::SSLClient cli(host, port);
|
||||
#else
|
||||
auto port = 8080;
|
||||
httplib::Client cli(host, port, sec);
|
||||
httplib::Client cli(host, port);
|
||||
#endif
|
||||
cli.set_timeout_sec(2);
|
||||
|
||||
auto res = cli.Get("/");
|
||||
ASSERT_TRUE(res == nullptr);
|
||||
@@ -336,15 +379,15 @@ TEST(ConnectionErrorTest, InvalidPort) {
|
||||
|
||||
TEST(ConnectionErrorTest, Timeout) {
|
||||
auto host = "google.com";
|
||||
auto sec = 2;
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto port = 44380;
|
||||
httplib::SSLClient cli(host, port, sec);
|
||||
httplib::SSLClient cli(host, port);
|
||||
#else
|
||||
auto port = 8080;
|
||||
httplib::Client cli(host, port, sec);
|
||||
httplib::Client cli(host, port);
|
||||
#endif
|
||||
cli.set_timeout_sec(2);
|
||||
|
||||
auto res = cli.Get("/");
|
||||
ASSERT_TRUE(res == nullptr);
|
||||
@@ -352,15 +395,15 @@ TEST(ConnectionErrorTest, Timeout) {
|
||||
|
||||
TEST(CancelTest, NoCancel) {
|
||||
auto host = "httpbin.org";
|
||||
auto sec = 5;
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto port = 443;
|
||||
httplib::SSLClient cli(host, port, sec);
|
||||
httplib::SSLClient cli(host, port);
|
||||
#else
|
||||
auto port = 80;
|
||||
httplib::Client cli(host, port, sec);
|
||||
httplib::Client cli(host, port);
|
||||
#endif
|
||||
cli.set_timeout_sec(5);
|
||||
|
||||
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return true; });
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
@@ -370,31 +413,31 @@ TEST(CancelTest, NoCancel) {
|
||||
|
||||
TEST(CancelTest, WithCancelSmallPayload) {
|
||||
auto host = "httpbin.org";
|
||||
auto sec = 5;
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto port = 443;
|
||||
httplib::SSLClient cli(host, port, sec);
|
||||
httplib::SSLClient cli(host, port);
|
||||
#else
|
||||
auto port = 80;
|
||||
httplib::Client cli(host, port, sec);
|
||||
httplib::Client cli(host, port);
|
||||
#endif
|
||||
|
||||
auto res = cli.Get("/range/32", [](uint64_t, uint64_t) { return false; });
|
||||
cli.set_timeout_sec(5);
|
||||
ASSERT_TRUE(res == nullptr);
|
||||
}
|
||||
|
||||
TEST(CancelTest, WithCancelLargePayload) {
|
||||
auto host = "httpbin.org";
|
||||
auto sec = 5;
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
auto port = 443;
|
||||
httplib::SSLClient cli(host, port, sec);
|
||||
httplib::SSLClient cli(host, port);
|
||||
#else
|
||||
auto port = 80;
|
||||
httplib::Client cli(host, port, sec);
|
||||
httplib::Client cli(host, port);
|
||||
#endif
|
||||
cli.set_timeout_sec(5);
|
||||
|
||||
uint32_t count = 0;
|
||||
httplib::Headers headers;
|
||||
@@ -429,8 +472,160 @@ TEST(BaseAuthTest, FromHTTPWatch) {
|
||||
"{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n");
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
cli.set_basic_auth("hello", "world");
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(res->body,
|
||||
"{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n");
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
cli.set_basic_auth("hello", "bad");
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
cli.set_basic_auth("bad", "world");
|
||||
auto res = cli.Get("/basic-auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(DigestAuthTest, FromHTTPWatch) {
|
||||
auto host = "httpbin.org";
|
||||
auto port = 443;
|
||||
httplib::SSLClient cli(host, port);
|
||||
|
||||
{
|
||||
auto res = cli.Get("/digest-auth/auth/hello/world");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(401, res->status);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> paths = {
|
||||
"/digest-auth/auth/hello/world/MD5",
|
||||
"/digest-auth/auth/hello/world/SHA-256",
|
||||
"/digest-auth/auth/hello/world/SHA-512",
|
||||
"/digest-auth/auth-init/hello/world/MD5",
|
||||
"/digest-auth/auth-int/hello/world/MD5",
|
||||
};
|
||||
|
||||
cli.set_digest_auth("hello", "world");
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(res->body,
|
||||
"{\n \"authenticated\": true, \n \"user\": \"hello\"\n}\n");
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
cli.set_digest_auth("hello", "bad");
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(400, res->status);
|
||||
}
|
||||
|
||||
cli.set_digest_auth("bad", "world");
|
||||
for (auto path : paths) {
|
||||
auto res = cli.Get(path.c_str());
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(400, res->status);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(AbsoluteRedirectTest, Redirect) {
|
||||
auto host = "httpbin.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
httplib::SSLClient cli(host);
|
||||
#else
|
||||
httplib::Client cli(host);
|
||||
#endif
|
||||
|
||||
cli.set_follow_location(true);
|
||||
auto res = cli.Get("/absolute-redirect/3");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(RedirectTest, Redirect) {
|
||||
auto host = "httpbin.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
httplib::SSLClient cli(host);
|
||||
#else
|
||||
httplib::Client cli(host);
|
||||
#endif
|
||||
|
||||
cli.set_follow_location(true);
|
||||
auto res = cli.Get("/redirect/3");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(RelativeRedirectTest, Redirect) {
|
||||
auto host = "httpbin.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
httplib::SSLClient cli(host);
|
||||
#else
|
||||
httplib::Client cli(host);
|
||||
#endif
|
||||
|
||||
cli.set_follow_location(true);
|
||||
auto res = cli.Get("/relative-redirect/3");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(TooManyRedirectTest, Redirect) {
|
||||
auto host = "httpbin.org";
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
httplib::SSLClient cli(host);
|
||||
#else
|
||||
httplib::Client cli(host);
|
||||
#endif
|
||||
|
||||
cli.set_follow_location(true);
|
||||
auto res = cli.Get("/redirect/21");
|
||||
ASSERT_TRUE(res == nullptr);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(YahooRedirectTest, Redirect) {
|
||||
httplib::Client cli("yahoo.com");
|
||||
|
||||
auto res = cli.Get("/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(301, res->status);
|
||||
|
||||
cli.set_follow_location(true);
|
||||
res = cli.Get("/");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(HttpsToHttpRedirectTest, Redirect) {
|
||||
httplib::SSLClient cli("httpbin.org");
|
||||
cli.set_follow_location(true);
|
||||
auto res =
|
||||
cli.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(Server, BindAndListenSeparately) {
|
||||
Server svr;
|
||||
int port = svr.bind_to_any_port("localhost");
|
||||
@@ -451,6 +646,7 @@ protected:
|
||||
|
||||
virtual void SetUp() {
|
||||
svr_.set_base_dir("./www");
|
||||
svr_.set_base_dir("./www2", "/mount");
|
||||
|
||||
svr_.Get("/hi",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
@@ -523,8 +719,9 @@ protected:
|
||||
[data](uint64_t offset, uint64_t length, DataSink sink) {
|
||||
size_t DATA_CHUNK_SIZE = 4;
|
||||
const auto &d = *data;
|
||||
auto out_len = std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
|
||||
sink(&d[offset], out_len);
|
||||
auto out_len =
|
||||
std::min(static_cast<size_t>(length), DATA_CHUNK_SIZE);
|
||||
sink(&d[static_cast<size_t>(offset)], out_len);
|
||||
},
|
||||
[data] { delete data; });
|
||||
})
|
||||
@@ -558,29 +755,27 @@ protected:
|
||||
{
|
||||
const auto &file = req.get_file_value("text1");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_EQ("text default",
|
||||
req.body.substr(file.offset, file.length));
|
||||
EXPECT_EQ("text default", file.content);
|
||||
}
|
||||
|
||||
{
|
||||
const auto &file = req.get_file_value("text2");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_EQ("aωb", req.body.substr(file.offset, file.length));
|
||||
EXPECT_EQ("aωb", file.content);
|
||||
}
|
||||
|
||||
{
|
||||
const auto &file = req.get_file_value("file1");
|
||||
EXPECT_EQ("hello.txt", file.filename);
|
||||
EXPECT_EQ("text/plain", file.content_type);
|
||||
EXPECT_EQ("h\ne\n\nl\nl\no\n",
|
||||
req.body.substr(file.offset, file.length));
|
||||
EXPECT_EQ("h\ne\n\nl\nl\no\n", file.content);
|
||||
}
|
||||
|
||||
{
|
||||
const auto &file = req.get_file_value("file3");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_EQ("application/octet-stream", file.content_type);
|
||||
EXPECT_EQ(0u, file.length);
|
||||
EXPECT_EQ(0u, file.content.size());
|
||||
}
|
||||
})
|
||||
.Post("/empty",
|
||||
@@ -593,6 +788,11 @@ protected:
|
||||
EXPECT_EQ(req.body, "PUT");
|
||||
res.set_content(req.body, "text/plain");
|
||||
})
|
||||
.Put("/put-large",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(req.body, LARGE_DATA);
|
||||
res.set_content(req.body, "text/plain");
|
||||
})
|
||||
.Patch("/patch",
|
||||
[&](const Request &req, Response &res) {
|
||||
EXPECT_EQ(req.body, "PATCH");
|
||||
@@ -629,6 +829,81 @@ protected:
|
||||
EXPECT_EQ(1u, req.get_header_value_count("Content-Length"));
|
||||
EXPECT_EQ("5", req.get_header_value("Content-Length"));
|
||||
})
|
||||
.Post("/content_receiver",
|
||||
[&](const Request &req, Response &res,
|
||||
const ContentReader &content_reader) {
|
||||
if (req.is_multipart_form_data()) {
|
||||
MultipartFormDataItems files;
|
||||
content_reader(
|
||||
[&](const MultipartFormData &file) {
|
||||
files.push_back(file);
|
||||
return true;
|
||||
},
|
||||
[&](const char *data, size_t data_length) {
|
||||
files.back().content.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
|
||||
EXPECT_EQ(5u, files.size());
|
||||
|
||||
{
|
||||
const auto &file = get_file_value(files, "text1");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_EQ("text default", file.content);
|
||||
}
|
||||
|
||||
{
|
||||
const auto &file = get_file_value(files, "text2");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_EQ("aωb", file.content);
|
||||
}
|
||||
|
||||
{
|
||||
const auto &file = get_file_value(files, "file1");
|
||||
EXPECT_EQ("hello.txt", file.filename);
|
||||
EXPECT_EQ("text/plain", file.content_type);
|
||||
EXPECT_EQ("h\ne\n\nl\nl\no\n", file.content);
|
||||
}
|
||||
|
||||
{
|
||||
const auto &file = get_file_value(files, "file3");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_EQ("application/octet-stream", file.content_type);
|
||||
EXPECT_EQ(0u, file.content.size());
|
||||
}
|
||||
} else {
|
||||
std::string body;
|
||||
content_reader([&](const char *data, size_t data_length) {
|
||||
EXPECT_EQ(data_length, 7);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
EXPECT_EQ(body, "content");
|
||||
res.set_content(body, "text/plain");
|
||||
}
|
||||
})
|
||||
.Put("/content_receiver",
|
||||
[&](const Request & /*req*/, Response &res,
|
||||
const ContentReader &content_reader) {
|
||||
std::string body;
|
||||
content_reader([&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
EXPECT_EQ(body, "content");
|
||||
res.set_content(body, "text/plain");
|
||||
})
|
||||
.Patch("/content_receiver",
|
||||
[&](const Request & /*req*/, Response &res,
|
||||
const ContentReader &content_reader) {
|
||||
std::string body;
|
||||
content_reader([&](const char *data, size_t data_length) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
EXPECT_EQ(body, "content");
|
||||
res.set_content(body, "text/plain");
|
||||
})
|
||||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||
.Get("/gzip",
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
@@ -652,14 +927,13 @@ protected:
|
||||
{
|
||||
const auto &file = req.get_file_value("key1");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_EQ("test", req.body.substr(file.offset, file.length));
|
||||
EXPECT_EQ("test", file.content);
|
||||
}
|
||||
|
||||
{
|
||||
const auto &file = req.get_file_value("key2");
|
||||
EXPECT_EQ("", file.filename);
|
||||
EXPECT_EQ("--abcdefg123",
|
||||
req.body.substr(file.offset, file.length));
|
||||
EXPECT_EQ("--abcdefg123", file.content);
|
||||
}
|
||||
})
|
||||
#endif
|
||||
@@ -847,9 +1121,42 @@ TEST_F(ServerTest, GetMethodOutOfBaseDir2) {
|
||||
EXPECT_EQ(404, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, InvalidBaseDir) {
|
||||
EXPECT_EQ(false, svr_.set_base_dir("invalid_dir"));
|
||||
EXPECT_EQ(true, svr_.set_base_dir("."));
|
||||
TEST_F(ServerTest, GetMethodDirMountTest) {
|
||||
auto res = cli_.Get("/mount/dir/test.html");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("test.html", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethodDirMountTestWithDoubleDots) {
|
||||
auto res = cli_.Get("/mount/dir/../dir/test.html");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("text/html", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("test.html", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethodInvalidMountPath) {
|
||||
auto res = cli_.Get("/mount/dir/../test.html");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(404, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethodOutOfBaseDirMount) {
|
||||
auto res = cli_.Get("/mount/../www2/dir/test.html");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(404, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetMethodOutOfBaseDirMount2) {
|
||||
auto res = cli_.Get("/mount/dir/../../www2/dir/test.html");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(404, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, InvalidBaseDirMount) {
|
||||
EXPECT_EQ(false, svr_.set_base_dir("./www3", "invalid_mount_point"));
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, EmptyRequest) {
|
||||
@@ -1123,10 +1430,15 @@ TEST_F(ServerTest, GetStreamedWithRangeMultipart) {
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GetStreamedEndless) {
|
||||
uint64_t offset = 0;
|
||||
auto res = cli_.Get("/streamed-cancel",
|
||||
[](const char * /*data*/, uint64_t /*data_length*/,
|
||||
uint64_t offset,
|
||||
uint64_t /*content_length*/) { return offset < 100; });
|
||||
[&](const char * /*data*/, uint64_t data_length) {
|
||||
if (offset < 100) {
|
||||
offset += data_length;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
ASSERT_TRUE(res == nullptr);
|
||||
}
|
||||
|
||||
@@ -1238,6 +1550,44 @@ TEST_F(ServerTest, Put) {
|
||||
EXPECT_EQ("PUT", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutWithContentProvider) {
|
||||
auto res = cli_.Put(
|
||||
"/put", 3,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink sink) {
|
||||
sink("PUT", 3);
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("PUT", res->body);
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
||||
TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Put(
|
||||
"/put", 3,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink sink) {
|
||||
sink("PUT", 3);
|
||||
},
|
||||
"text/plain");
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ("PUT", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutLargeFileWithGzip) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Put("/put-large", LARGE_DATA, "text/plain");
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
EXPECT_EQ(LARGE_DATA, res->body);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(ServerTest, Patch) {
|
||||
auto res = cli_.Patch("/patch", "PATCH", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
@@ -1280,6 +1630,62 @@ TEST_F(ServerTest, NoMultipleHeaders) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostContentReceiver) {
|
||||
auto res = cli_.Post("/content_receiver", "content", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ("content", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostMulitpartFilsContentReceiver) {
|
||||
MultipartFormDataItems items = {
|
||||
{"text1", "text default", "", ""},
|
||||
{"text2", "aωb", "", ""},
|
||||
{"file1", "h\ne\n\nl\nl\no\n", "hello.txt", "text/plain"},
|
||||
{"file2", "{\n \"world\", true\n}\n", "world.json", "application/json"},
|
||||
{"file3", "", "", "application/octet-stream"},
|
||||
};
|
||||
|
||||
auto res = cli_.Post("/content_receiver", items);
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PostContentReceiverGzip) {
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Post("/content_receiver", "content", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ("content", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PutContentReceiver) {
|
||||
auto res = cli_.Put("/content_receiver", "content", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ("content", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, PatchContentReceiver) {
|
||||
auto res = cli_.Patch("/content_receiver", "content", "text/plain");
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
ASSERT_EQ(200, res->status);
|
||||
ASSERT_EQ("content", res->body);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, HTTP2Magic) {
|
||||
Request req;
|
||||
req.method = "PRI";
|
||||
req.path = "*";
|
||||
req.body = "SM";
|
||||
|
||||
auto res = std::make_shared<Response>();
|
||||
auto ret = cli_.send(req, *res);
|
||||
|
||||
ASSERT_TRUE(ret);
|
||||
EXPECT_EQ(400, res->status);
|
||||
}
|
||||
TEST_F(ServerTest, KeepAlive) {
|
||||
cli_.set_keep_alive_max_count(4);
|
||||
|
||||
@@ -1297,19 +1703,19 @@ TEST_F(ServerTest, KeepAlive) {
|
||||
ASSERT_TRUE(requests.size() == responses.size());
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
auto& res = responses[i];
|
||||
auto &res = responses[i];
|
||||
EXPECT_EQ(200, res.status);
|
||||
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("Hello World!", res.body);
|
||||
}
|
||||
|
||||
{
|
||||
auto& res = responses[3];
|
||||
auto &res = responses[3];
|
||||
EXPECT_EQ(404, res.status);
|
||||
}
|
||||
|
||||
{
|
||||
auto& res = responses[4];
|
||||
auto &res = responses[4];
|
||||
EXPECT_EQ(200, res.status);
|
||||
EXPECT_EQ("text/plain", res.get_header_value("Content-Type"));
|
||||
EXPECT_EQ("empty", res.body);
|
||||
@@ -1332,16 +1738,30 @@ TEST_F(ServerTest, Gzip) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GzipWithoutAcceptEncoding) {
|
||||
Headers headers;
|
||||
auto res = cli_.Get("/gzip", headers);
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ("", res->get_header_value("Content-Encoding"));
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("100", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
|
||||
"7890123456789012345678901234567890",
|
||||
res->body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GzipWithContentReceiver) {
|
||||
Headers headers;
|
||||
headers.emplace("Accept-Encoding", "gzip, deflate");
|
||||
std::string body;
|
||||
auto res = cli_.Get("/gzip", headers,
|
||||
[&](const char *data, uint64_t data_length,
|
||||
uint64_t /*offset*/, uint64_t /*content_length*/) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
auto res =
|
||||
cli_.Get("/gzip", headers, [&](const char *data, uint64_t data_length) {
|
||||
EXPECT_EQ(data_length, 100);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ("gzip", res->get_header_value("Content-Encoding"));
|
||||
@@ -1353,6 +1773,26 @@ TEST_F(ServerTest, GzipWithContentReceiver) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, GzipWithContentReceiverWithoutAcceptEncoding) {
|
||||
Headers headers;
|
||||
std::string body;
|
||||
auto res =
|
||||
cli_.Get("/gzip", headers, [&](const char *data, uint64_t data_length) {
|
||||
EXPECT_EQ(data_length, 100);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ("", res->get_header_value("Content-Encoding"));
|
||||
EXPECT_EQ("text/plain", res->get_header_value("Content-Type"));
|
||||
EXPECT_EQ("100", res->get_header_value("Content-Length"));
|
||||
EXPECT_EQ("123456789012345678901234567890123456789012345678901234567890123456"
|
||||
"7890123456789012345678901234567890",
|
||||
body);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, NoGzip) {
|
||||
Headers headers;
|
||||
headers.emplace("Accept-Encoding", "gzip, deflate");
|
||||
@@ -1372,12 +1812,12 @@ TEST_F(ServerTest, NoGzipWithContentReceiver) {
|
||||
Headers headers;
|
||||
headers.emplace("Accept-Encoding", "gzip, deflate");
|
||||
std::string body;
|
||||
auto res = cli_.Get("/nogzip", headers,
|
||||
[&](const char *data, uint64_t data_length,
|
||||
uint64_t /*offset*/, uint64_t /*content_length*/) {
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
auto res =
|
||||
cli_.Get("/nogzip", headers, [&](const char *data, uint64_t data_length) {
|
||||
EXPECT_EQ(data_length, 100);
|
||||
body.append(data, data_length);
|
||||
return true;
|
||||
});
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(false, res->has_header("Content-Encoding"));
|
||||
@@ -1390,60 +1830,99 @@ TEST_F(ServerTest, NoGzipWithContentReceiver) {
|
||||
}
|
||||
|
||||
TEST_F(ServerTest, MultipartFormDataGzip) {
|
||||
Request req;
|
||||
req.method = "POST";
|
||||
req.path = "/gzipmultipart";
|
||||
MultipartFormDataItems items = {
|
||||
{"key1", "test", "", ""},
|
||||
{"key2", "--abcdefg123", "", ""},
|
||||
};
|
||||
|
||||
std::string host_and_port;
|
||||
host_and_port += HOST;
|
||||
host_and_port += ":";
|
||||
host_and_port += std::to_string(PORT);
|
||||
cli_.set_compress(true);
|
||||
auto res = cli_.Post("/gzipmultipart", items);
|
||||
|
||||
req.headers.emplace("Host", host_and_port.c_str());
|
||||
req.headers.emplace("Accept", "*/*");
|
||||
req.headers.emplace("User-Agent", "cpp-httplib/0.1");
|
||||
req.headers.emplace(
|
||||
"Content-Type",
|
||||
"multipart/form-data; boundary=------------------------fcba8368a9f48c0f");
|
||||
req.headers.emplace("Content-Encoding", "gzip");
|
||||
|
||||
// compressed_body generated by creating input.txt to this file:
|
||||
/*
|
||||
--------------------------fcba8368a9f48c0f
|
||||
Content-Disposition: form-data; name="key1"
|
||||
|
||||
test
|
||||
--------------------------fcba8368a9f48c0f
|
||||
Content-Disposition: form-data; name="key2"
|
||||
|
||||
--abcdefg123
|
||||
--------------------------fcba8368a9f48c0f--
|
||||
*/
|
||||
// then running unix2dos input.txt; gzip -9 -c input.txt | xxd -i.
|
||||
uint8_t compressed_body[] = {
|
||||
0x1f, 0x8b, 0x08, 0x08, 0x48, 0xf1, 0xd4, 0x5a, 0x02, 0x03, 0x69, 0x6e,
|
||||
0x70, 0x75, 0x74, 0x2e, 0x74, 0x78, 0x74, 0x00, 0xd3, 0xd5, 0xc5, 0x05,
|
||||
0xd2, 0x92, 0x93, 0x12, 0x2d, 0x8c, 0xcd, 0x2c, 0x12, 0x2d, 0xd3, 0x4c,
|
||||
0x2c, 0x92, 0x0d, 0xd2, 0x78, 0xb9, 0x9c, 0xf3, 0xf3, 0x4a, 0x52, 0xf3,
|
||||
0x4a, 0x74, 0x5d, 0x32, 0x8b, 0x0b, 0xf2, 0x8b, 0x33, 0x4b, 0x32, 0xf3,
|
||||
0xf3, 0xac, 0x14, 0xd2, 0xf2, 0x8b, 0x72, 0x75, 0x53, 0x12, 0x4b, 0x12,
|
||||
0xad, 0x15, 0xf2, 0x12, 0x73, 0x53, 0x6d, 0x95, 0xb2, 0x53, 0x2b, 0x0d,
|
||||
0x95, 0x78, 0xb9, 0x78, 0xb9, 0x4a, 0x52, 0x8b, 0x4b, 0x78, 0xb9, 0x74,
|
||||
0x69, 0x61, 0x81, 0x11, 0xd8, 0x02, 0x5d, 0xdd, 0xc4, 0xa4, 0xe4, 0x94,
|
||||
0xd4, 0xb4, 0x74, 0x43, 0x23, 0x63, 0x52, 0x2c, 0xd2, 0xd5, 0xe5, 0xe5,
|
||||
0x02, 0x00, 0xff, 0x0e, 0x72, 0xdf, 0xf8, 0x00, 0x00, 0x00};
|
||||
|
||||
req.body = std::string((char *)compressed_body,
|
||||
sizeof(compressed_body) / sizeof(compressed_body[0]));
|
||||
|
||||
auto res = std::make_shared<Response>();
|
||||
auto ret = cli_.send(req, *res);
|
||||
|
||||
ASSERT_TRUE(ret);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Sends a raw request to a server listening at HOST:PORT.
|
||||
static bool send_request(time_t read_timeout_sec, const std::string &req) {
|
||||
auto client_sock = detail::create_client_socket(HOST, PORT, /*timeout_sec=*/5,
|
||||
std::string());
|
||||
|
||||
if (client_sock == INVALID_SOCKET) { return false; }
|
||||
|
||||
return detail::process_and_close_socket(
|
||||
true, client_sock, 1, read_timeout_sec, 0,
|
||||
[&](Stream &strm, bool /*last_connection*/, bool &
|
||||
/*connection_close*/) -> bool {
|
||||
if (req.size() !=
|
||||
static_cast<size_t>(strm.write(req.data(), req.size()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[512];
|
||||
|
||||
detail::stream_line_reader line_reader(strm, buf, sizeof(buf));
|
||||
while (line_reader.getline()) {}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, TrimWhitespaceFromHeaderValues) {
|
||||
Server svr;
|
||||
std::string header_value;
|
||||
svr.Get("/validate-ws-in-headers", [&](const Request &req, Response &res) {
|
||||
header_value = req.get_header_value("foo");
|
||||
res.set_content("ok", "text/plain");
|
||||
});
|
||||
|
||||
thread t = thread([&] { svr.listen(HOST, PORT); });
|
||||
while (!svr.is_running()) {
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
// Only space and horizontal tab are whitespace. Make sure other whitespace-
|
||||
// like characters are not treated the same - use vertical tab and escape.
|
||||
const std::string req = "GET /validate-ws-in-headers HTTP/1.1\r\n"
|
||||
"foo: \t \v bar \e\t \r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n";
|
||||
|
||||
ASSERT_TRUE(send_request(5, req));
|
||||
svr.stop();
|
||||
t.join();
|
||||
EXPECT_EQ(header_value, "\v bar \e");
|
||||
}
|
||||
|
||||
TEST(ServerRequestParsingTest, ReadHeadersRegexComplexity) {
|
||||
Server svr;
|
||||
svr.Get("/hi", [&](const Request & /*req*/, Response &res) {
|
||||
res.set_content("ok", "text/plain");
|
||||
});
|
||||
|
||||
// Server read timeout must be longer than the client read timeout for the
|
||||
// bug to reproduce, probably to force the server to process a request
|
||||
// without a trailing blank line.
|
||||
const time_t client_read_timeout_sec = 1;
|
||||
svr.set_read_timeout(client_read_timeout_sec + 1, 0);
|
||||
bool listen_thread_ok = false;
|
||||
thread t = thread([&] { listen_thread_ok = svr.listen(HOST, PORT); });
|
||||
while (!svr.is_running()) {
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
// A certain header line causes an exception if the header property is parsed
|
||||
// naively with a single regex. This occurs with libc++ but not libstdc++.
|
||||
const std::string req =
|
||||
"GET /hi HTTP/1.1\r\n"
|
||||
" : "
|
||||
" ";
|
||||
|
||||
ASSERT_TRUE(send_request(client_read_timeout_sec, req));
|
||||
svr.stop();
|
||||
t.join();
|
||||
EXPECT_TRUE(listen_thread_ok);
|
||||
}
|
||||
|
||||
class ServerTestWithAI_PASSIVE : public ::testing::Test {
|
||||
protected:
|
||||
ServerTestWithAI_PASSIVE()
|
||||
@@ -1639,9 +2118,10 @@ TEST(SSLClientServerTest, ClientCertPresent) {
|
||||
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
|
||||
msleep(1);
|
||||
|
||||
httplib::SSLClient cli(HOST, PORT, 30, CLIENT_CERT_FILE,
|
||||
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE,
|
||||
CLIENT_PRIVATE_KEY_FILE);
|
||||
auto res = cli.Get("/test");
|
||||
cli.set_timeout_sec(30);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
ASSERT_EQ(200, res->status);
|
||||
|
||||
@@ -1658,8 +2138,9 @@ TEST(SSLClientServerTest, ClientCertMissing) {
|
||||
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
|
||||
msleep(1);
|
||||
|
||||
httplib::SSLClient cli(HOST, PORT, 30);
|
||||
httplib::SSLClient cli(HOST, PORT);
|
||||
auto res = cli.Get("/test");
|
||||
cli.set_timeout_sec(30);
|
||||
ASSERT_TRUE(res == nullptr);
|
||||
|
||||
svr.stop();
|
||||
@@ -1679,9 +2160,10 @@ TEST(SSLClientServerTest, TrustDirOptional) {
|
||||
thread t = thread([&]() { ASSERT_TRUE(svr.listen(HOST, PORT)); });
|
||||
msleep(1);
|
||||
|
||||
httplib::SSLClient cli(HOST, PORT, 30, CLIENT_CERT_FILE,
|
||||
httplib::SSLClient cli(HOST, PORT, CLIENT_CERT_FILE,
|
||||
CLIENT_PRIVATE_KEY_FILE);
|
||||
auto res = cli.Get("/test");
|
||||
cli.set_timeout_sec(30);
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
ASSERT_EQ(200, res->status);
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
@@ -47,7 +47,7 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
@@ -69,15 +69,23 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>C:\Program Files\OpenSSL-Win64\lib\VC;C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>C:\Program Files\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>C:\Program Files\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>C:\Program Files\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
@@ -87,6 +95,8 @@
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -102,11 +112,13 @@
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
@@ -119,6 +131,8 @@
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
@@ -138,13 +152,15 @@
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>./;../</AdditionalIncludeDirectories>
|
||||
<AdditionalUsingDirectories>
|
||||
</AdditionalUsingDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Ws2_32.lib;libssl.lib;libcrypto.lib;libssl.lib;libcrypto.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
8
test/www2/dir/index.html
Normal file
8
test/www2/dir/index.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/dir/test.html">Test</a>
|
||||
<a href="/hi">hi</a>
|
||||
</body>
|
||||
</html>
|
||||
1
test/www2/dir/test.html
Normal file
1
test/www2/dir/test.html
Normal file
@@ -0,0 +1 @@
|
||||
test.html
|
||||
8
test/www3/dir/index.html
Normal file
8
test/www3/dir/index.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/dir/test.html">Test</a>
|
||||
<a href="/hi">hi</a>
|
||||
</body>
|
||||
</html>
|
||||
1
test/www3/dir/test.html
Normal file
1
test/www3/dir/test.html
Normal file
@@ -0,0 +1 @@
|
||||
test.html
|
||||
Reference in New Issue
Block a user