mirror of
https://github.com/yhirose/cpp-httplib
synced 2026-06-08 18:30:49 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66f698fab6 | |||
| b9a9df4d73 | |||
| 25aa3ca982 | |||
| 2d67211183 |
@@ -14,13 +14,12 @@ using namespace std;
|
||||
|
||||
int main(void) {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
httplib::url::Options options;
|
||||
options.ca_cert_file_path = CA_CERT_FILE;
|
||||
// options.server_certificate_verification = true;
|
||||
|
||||
auto res = httplib::url::Get("https://localhost:8080/hi", options);
|
||||
auto res = httplib::Client2("https://localhost:8080")
|
||||
.set_ca_cert_path(CA_CERT_FILE)
|
||||
// .enable_server_certificate_verification(true)
|
||||
.Get("/hi");
|
||||
#else
|
||||
auto res = httplib::url::Get("http://localhost:8080/hi");
|
||||
auto res = httplib::Client2("http://localhost:8080").Get("/hi");
|
||||
#endif
|
||||
|
||||
if (res) {
|
||||
|
||||
+8
-4
@@ -80,15 +80,19 @@ int main(void) {
|
||||
svr.Get("/event1", [&](const Request & /*req*/, Response &res) {
|
||||
cout << "connected to event1..." << endl;
|
||||
res.set_header("Content-Type", "text/event-stream");
|
||||
res.set_chunked_content_provider(
|
||||
[&](uint64_t /*offset*/, DataSink &sink) { ed.wait_event(&sink); });
|
||||
res.set_chunked_content_provider([&](size_t /*offset*/, DataSink &sink) {
|
||||
ed.wait_event(&sink);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
svr.Get("/event2", [&](const Request & /*req*/, Response &res) {
|
||||
cout << "connected to event2..." << endl;
|
||||
res.set_header("Content-Type", "text/event-stream");
|
||||
res.set_chunked_content_provider(
|
||||
[&](uint64_t /*offset*/, DataSink &sink) { ed.wait_event(&sink); });
|
||||
res.set_chunked_content_provider([&](size_t /*offset*/, DataSink &sink) {
|
||||
ed.wait_event(&sink);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
thread t([&] {
|
||||
|
||||
@@ -215,7 +215,8 @@ using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
|
||||
|
||||
class DataSink {
|
||||
public:
|
||||
DataSink() = default;
|
||||
DataSink() : os(&sb_), sb_(*this) {}
|
||||
|
||||
DataSink(const DataSink &) = delete;
|
||||
DataSink &operator=(const DataSink &) = delete;
|
||||
DataSink(DataSink &&) = delete;
|
||||
@@ -224,6 +225,24 @@ public:
|
||||
std::function<void(const char *data, size_t data_len)> write;
|
||||
std::function<void()> done;
|
||||
std::function<bool()> is_writable;
|
||||
std::ostream os;
|
||||
|
||||
private:
|
||||
class data_sink_streambuf : public std::streambuf {
|
||||
public:
|
||||
data_sink_streambuf(DataSink &sink) : sink_(sink) {}
|
||||
|
||||
protected:
|
||||
std::streamsize xsputn(const char *s, std::streamsize n) {
|
||||
sink_.write(s, static_cast<size_t>(n));
|
||||
return n;
|
||||
}
|
||||
|
||||
private:
|
||||
DataSink &sink_;
|
||||
};
|
||||
|
||||
data_sink_streambuf sb_;
|
||||
};
|
||||
|
||||
using ContentProvider =
|
||||
@@ -2079,27 +2098,37 @@ inline ssize_t write_headers(Stream &strm, const T &info,
|
||||
return write_len;
|
||||
}
|
||||
|
||||
inline bool write_data(Stream &strm, const char *d, size_t l) {
|
||||
size_t offset = 0;
|
||||
while (offset < l) {
|
||||
auto length = strm.write(d + offset, l - offset);
|
||||
if (length < 0) { return false; }
|
||||
offset += static_cast<size_t>(length);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
|
||||
size_t offset, size_t length) {
|
||||
size_t begin_offset = offset;
|
||||
size_t end_offset = offset + length;
|
||||
|
||||
ssize_t written_length = 0;
|
||||
auto ok = true;
|
||||
|
||||
DataSink data_sink;
|
||||
data_sink.write = [&](const char *d, size_t l) {
|
||||
offset += l;
|
||||
written_length = strm.write(d, l);
|
||||
};
|
||||
data_sink.is_writable = [&](void) {
|
||||
return strm.is_writable() && written_length >= 0;
|
||||
if (ok) {
|
||||
offset += l;
|
||||
if (!write_data(strm, d, l)) { ok = false; }
|
||||
}
|
||||
};
|
||||
data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
|
||||
|
||||
while (offset < end_offset) {
|
||||
while (ok && offset < end_offset) {
|
||||
if (!content_provider(offset, end_offset - offset, data_sink)) {
|
||||
return -1;
|
||||
}
|
||||
if (written_length < 0) { return written_length; }
|
||||
if (!ok) { return -1; }
|
||||
}
|
||||
|
||||
return static_cast<ssize_t>(offset - begin_offset);
|
||||
@@ -2113,29 +2142,41 @@ inline ssize_t write_content_chunked(Stream &strm,
|
||||
auto data_available = true;
|
||||
ssize_t total_written_length = 0;
|
||||
|
||||
ssize_t written_length = 0;
|
||||
auto ok = true;
|
||||
|
||||
DataSink data_sink;
|
||||
data_sink.write = [&](const char *d, size_t l) {
|
||||
data_available = l > 0;
|
||||
offset += l;
|
||||
if (ok) {
|
||||
data_available = l > 0;
|
||||
offset += l;
|
||||
|
||||
// Emit chunked response header and footer for each chunk
|
||||
auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n";
|
||||
written_length = strm.write(chunk);
|
||||
// Emit chunked response header and footer for each chunk
|
||||
auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n";
|
||||
if (write_data(strm, chunk.data(), chunk.size())) {
|
||||
total_written_length += chunk.size();
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
data_sink.done = [&](void) {
|
||||
data_available = false;
|
||||
written_length = strm.write("0\r\n\r\n");
|
||||
if (ok) {
|
||||
static const std::string done_marker("0\r\n\r\n");
|
||||
if (write_data(strm, done_marker.data(), done_marker.size())) {
|
||||
total_written_length += done_marker.size();
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
data_sink.is_writable = [&](void) {
|
||||
return strm.is_writable() && written_length >= 0;
|
||||
return ok && strm.is_writable();
|
||||
};
|
||||
|
||||
while (data_available && !is_shutting_down()) {
|
||||
if (!content_provider(offset, 0, data_sink)) { return -1; }
|
||||
if (written_length < 0) { return written_length; }
|
||||
total_written_length += written_length;
|
||||
if (!ok) { return -1; }
|
||||
}
|
||||
|
||||
return total_written_length;
|
||||
@@ -4115,7 +4156,9 @@ inline bool Client::write_request(Stream &strm, const Request &req,
|
||||
|
||||
// Flush buffer
|
||||
auto &data = bstrm.get_buffer();
|
||||
strm.write(data.data(), data.size());
|
||||
if (!detail::write_data(strm, data.data(), data.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Body
|
||||
if (req.body.empty()) {
|
||||
@@ -4123,26 +4166,31 @@ inline bool Client::write_request(Stream &strm, const Request &req,
|
||||
size_t offset = 0;
|
||||
size_t end_offset = req.content_length;
|
||||
|
||||
ssize_t written_length = 0;
|
||||
bool ok = true;
|
||||
|
||||
DataSink data_sink;
|
||||
data_sink.write = [&](const char *d, size_t l) {
|
||||
written_length = strm.write(d, l);
|
||||
offset += static_cast<size_t>(written_length);
|
||||
if (ok) {
|
||||
if (detail::write_data(strm, d, l)) {
|
||||
offset += l;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
data_sink.is_writable = [&](void) {
|
||||
return strm.is_writable() && written_length >= 0;
|
||||
return ok && strm.is_writable();
|
||||
};
|
||||
|
||||
while (offset < end_offset) {
|
||||
if (!req.content_provider(offset, end_offset - offset, data_sink)) {
|
||||
return false;
|
||||
}
|
||||
if (written_length < 0) { return false; }
|
||||
if (!ok) { return false; }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strm.write(req.body);
|
||||
return detail::write_data(strm, req.body.data(), req.body.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
+58
-41
@@ -651,19 +651,6 @@ TEST(YahooRedirectTest, Redirect) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(YahooRedirectTest2, Redirect) {
|
||||
httplib::Client2 cli("http://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);
|
||||
@@ -673,16 +660,6 @@ TEST(HttpsToHttpRedirectTest, Redirect) {
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(HttpsToHttpRedirectTest2, Redirect) {
|
||||
auto res =
|
||||
httplib::Client2("https://httpbin.org")
|
||||
.set_follow_location(true)
|
||||
.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
|
||||
TEST(RedirectToDifferentPort, Redirect) {
|
||||
Server svr8080;
|
||||
Server svr8081;
|
||||
@@ -898,9 +875,9 @@ protected:
|
||||
res.set_chunked_content_provider(
|
||||
[](size_t /*offset*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.write("123", 3);
|
||||
sink.write("456", 3);
|
||||
sink.write("789", 3);
|
||||
sink.os << "123";
|
||||
sink.os << "456";
|
||||
sink.os << "789";
|
||||
sink.done();
|
||||
return true;
|
||||
});
|
||||
@@ -912,9 +889,9 @@ protected:
|
||||
[i](size_t /*offset*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
switch (*i) {
|
||||
case 0: sink.write("123", 3); break;
|
||||
case 1: sink.write("456", 3); break;
|
||||
case 2: sink.write("789", 3); break;
|
||||
case 0: sink.os << "123"; break;
|
||||
case 1: sink.os << "456"; break;
|
||||
case 2: sink.os << "789"; break;
|
||||
case 3: sink.done(); break;
|
||||
}
|
||||
(*i)++;
|
||||
@@ -926,7 +903,7 @@ protected:
|
||||
[&](const Request & /*req*/, Response &res) {
|
||||
res.set_content_provider(
|
||||
6, [](size_t offset, size_t /*length*/, DataSink &sink) {
|
||||
sink.write(offset < 3 ? "a" : "b", 1);
|
||||
sink.os << (offset < 3 ? "a" : "b");
|
||||
return true;
|
||||
});
|
||||
})
|
||||
@@ -952,8 +929,7 @@ protected:
|
||||
size_t(-1),
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
std::string data = "data_chunk";
|
||||
sink.write(data.data(), data.size());
|
||||
sink.os << "data_chunk";
|
||||
return true;
|
||||
});
|
||||
})
|
||||
@@ -1921,7 +1897,7 @@ TEST_F(ServerTest, PutWithContentProvider) {
|
||||
"/put", 3,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.write("PUT", 3);
|
||||
sink.os << "PUT";
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
@@ -1949,7 +1925,7 @@ TEST_F(ServerTest, PutWithContentProviderWithGzip) {
|
||||
"/put", 3,
|
||||
[](size_t /*offset*/, size_t /*length*/, DataSink &sink) {
|
||||
EXPECT_TRUE(sink.is_writable());
|
||||
sink.write("PUT", 3);
|
||||
sink.os << "PUT";
|
||||
return true;
|
||||
},
|
||||
"text/plain");
|
||||
@@ -2887,13 +2863,6 @@ TEST(SSLClientServerTest, TrustDirOptional) {
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
/* Cannot test this case as there is no external access to SSL object to check
|
||||
SSL_get_peer_certificate() == NULL TEST(SSLClientServerTest,
|
||||
ClientCAPathRequired) { SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE,
|
||||
nullptr, CLIENT_CA_CERT_DIR);
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -2902,3 +2871,51 @@ TEST(CleanupTest, WSACleanup) {
|
||||
ASSERT_EQ(0, ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
TEST(InvalidScheme, SimpleInterface) {
|
||||
httplib::Client2 cli("scheme://yahoo.com");
|
||||
ASSERT_FALSE(cli.is_valid());
|
||||
}
|
||||
|
||||
TEST(NoScheme, SimpleInterface) {
|
||||
httplib::Client2 cli("yahoo.com");
|
||||
ASSERT_FALSE(cli.is_valid());
|
||||
}
|
||||
|
||||
TEST(YahooRedirectTest2, SimpleInterface) {
|
||||
httplib::Client2 cli("http://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(YahooRedirectTest3, SimpleInterface) {
|
||||
httplib::Client2 cli("https://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(HttpsToHttpRedirectTest2, SimpleInterface) {
|
||||
auto res =
|
||||
httplib::Client2("https://httpbin.org")
|
||||
.set_follow_location(true)
|
||||
.Get("/redirect-to?url=http%3A%2F%2Fwww.google.com&status_code=302");
|
||||
|
||||
ASSERT_TRUE(res != nullptr);
|
||||
EXPECT_EQ(200, res->status);
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user