mirror of
https://github.com/yhirose/cpp-httplib
synced 2024-11-21 06:26:02 -07:00
Added OpenSSL support. #5
This commit is contained in:
parent
2c276ed31a
commit
22f124f871
8 changed files with 373 additions and 100 deletions
15
README.md
15
README.md
|
@ -52,4 +52,17 @@ int main(void)
|
|||
}
|
||||
```
|
||||
|
||||
Copyright (c) 2014 Yuji Hirose. All rights reserved.
|
||||
OpenSSL Support
|
||||
---------------
|
||||
|
||||
SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked.
|
||||
|
||||
```c++
|
||||
#define CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
|
||||
SSLServer svr("./key.pem", "./cert.pem");
|
||||
|
||||
SSLClient cli("localhost", 8080);
|
||||
```
|
||||
|
||||
Copyright (c) 2017 Yuji Hirose. All rights reserved.
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
|
||||
USE_CLANG = 1
|
||||
|
||||
ifdef USE_CLANG
|
||||
CC = clang++
|
||||
CFLAGS = -std=c++1y -stdlib=libc++ -g
|
||||
else
|
||||
CC = g++-4.9
|
||||
CFLAGS = -std=c++1y -g
|
||||
endif
|
||||
CFLAGS = -std=c++14 -I..
|
||||
#OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lssl -lcrypto
|
||||
|
||||
all: server client hello simplesvr
|
||||
|
||||
server : server.cc ../httplib.h
|
||||
$(CC) -o server $(CFLAGS) -I.. server.cc
|
||||
$(CC) -o server $(CFLAGS) server.cc $(OPENSSL_SUPPORT)
|
||||
|
||||
client : client.cc ../httplib.h
|
||||
$(CC) -o client $(CFLAGS) -I.. client.cc
|
||||
$(CC) -o client $(CFLAGS) client.cc $(OPENSSL_SUPPORT)
|
||||
|
||||
hello : hello.cc ../httplib.h
|
||||
$(CC) -o hello $(CFLAGS) -I.. hello.cc
|
||||
$(CC) -o hello $(CFLAGS) hello.cc $(OPENSSL_SUPPORT)
|
||||
|
||||
simplesvr : simplesvr.cc ../httplib.h
|
||||
$(CC) -o simplesvr $(CFLAGS) -I.. simplesvr.cc
|
||||
$(CC) -o simplesvr $(CFLAGS) simplesvr.cc $(OPENSSL_SUPPORT)
|
||||
|
||||
pem:
|
||||
openssl genrsa 2048 > key.pem
|
||||
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||
|
||||
clean:
|
||||
rm server client hello simplesvr *.pem
|
||||
|
|
|
@ -12,7 +12,11 @@ using namespace std;
|
|||
|
||||
int main(void)
|
||||
{
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
httplib::SSLClient cli("localhost", 8080);
|
||||
#else
|
||||
httplib::Client cli("localhost", 8080);
|
||||
#endif
|
||||
|
||||
auto res = cli.get("/hi");
|
||||
if (res) {
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
#include <httplib.h>
|
||||
#include <cstdio>
|
||||
|
||||
#define SERVER_CERT_FILE "./cert.pem"
|
||||
#define SERVER_PRIVATE_KEY_FILE "./key.pem"
|
||||
|
||||
using namespace httplib;
|
||||
|
||||
std::string dump_headers(const MultiMap& headers)
|
||||
|
@ -51,7 +54,7 @@ std::string log(const Request& req, const Response& res)
|
|||
snprintf(buf, sizeof(buf), "%d\n", res.status);
|
||||
s += buf;
|
||||
s += dump_headers(res.headers);
|
||||
|
||||
|
||||
if (!res.body.empty()) {
|
||||
s += res.body;
|
||||
}
|
||||
|
@ -63,7 +66,11 @@ std::string log(const Request& req, const Response& res)
|
|||
|
||||
int main(void)
|
||||
{
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
|
||||
#else
|
||||
Server svr;
|
||||
#endif
|
||||
|
||||
svr.get("/", [=](const auto& req, auto& res) {
|
||||
res.set_redirect("/hi");
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#define SERVER_CERT_FILE "./cert.pem"
|
||||
#define SERVER_PRIVATE_KEY_FILE "./key.pem"
|
||||
|
||||
using namespace httplib;
|
||||
using namespace std;
|
||||
|
||||
|
@ -52,7 +55,7 @@ string log(const Request& req, const Response& res)
|
|||
snprintf(buf, sizeof(buf), "%d\n", res.status);
|
||||
s += buf;
|
||||
s += dump_headers(res.headers);
|
||||
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
@ -63,7 +66,11 @@ int main(int argc, const char** argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
|
||||
#else
|
||||
Server svr;
|
||||
#endif
|
||||
|
||||
svr.set_error_handler([](const auto& req, auto& res) {
|
||||
const char* fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
|
||||
|
|
374
httplib.h
374
httplib.h
|
@ -1,12 +1,12 @@
|
|||
//
|
||||
// httplib.h
|
||||
//
|
||||
// Copyright (c) 2012 Yuji Hirose. All rights reserved.
|
||||
// Copyright (c) 2017 Yuji Hirose. All rights reserved.
|
||||
// The Boost Software License 1.0
|
||||
//
|
||||
|
||||
#ifndef _CPPHTTPLIB_HTTPSLIB_H_
|
||||
#define _CPPHTTPLIB_HTTPSLIB_H_
|
||||
#ifndef _CPPHTTPLIB_HTTPLIB_H_
|
||||
#define _CPPHTTPLIB_HTTPLIB_H_
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
@ -55,6 +55,10 @@ typedef int socket_t;
|
|||
#include <sys/stat.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
namespace httplib
|
||||
{
|
||||
|
||||
|
@ -93,12 +97,34 @@ struct Response {
|
|||
Response() : status(-1) {}
|
||||
};
|
||||
|
||||
class Stream {
|
||||
public:
|
||||
virtual ~Stream() {}
|
||||
virtual int read(char* ptr, size_t size) = 0;
|
||||
virtual int write(const char* ptr, size_t size1) = 0;
|
||||
virtual int write(const char* ptr) = 0;
|
||||
};
|
||||
|
||||
class SocketStream : public Stream {
|
||||
public:
|
||||
SocketStream(socket_t sock);
|
||||
virtual ~SocketStream();
|
||||
|
||||
virtual int read(char* ptr, size_t size);
|
||||
virtual int write(const char* ptr, size_t size);
|
||||
virtual int write(const char* ptr);
|
||||
|
||||
private:
|
||||
socket_t sock_;
|
||||
};
|
||||
|
||||
class Server {
|
||||
public:
|
||||
typedef std::function<void (const Request&, Response&)> Handler;
|
||||
typedef std::function<void (const Request&, const Response&)> Logger;
|
||||
|
||||
Server();
|
||||
virtual ~Server();
|
||||
|
||||
void get(const char* pattern, Handler handler);
|
||||
void post(const char* pattern, Handler handler);
|
||||
|
@ -111,15 +137,20 @@ public:
|
|||
bool listen(const char* host, int port);
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
void process_request(Stream& strm);
|
||||
|
||||
private:
|
||||
typedef std::vector<std::pair<std::regex, Handler>> Handlers;
|
||||
|
||||
void process_request(socket_t sock);
|
||||
bool read_request_line(socket_t sock, Request& req);
|
||||
bool routing(Request& req, Response& res);
|
||||
bool handle_file_request(Request& req, Response& res);
|
||||
bool dispatch_request(Request& req, Response& res, Handlers& handlers);
|
||||
|
||||
bool read_request_line(Stream& strm, Request& req);
|
||||
|
||||
virtual bool read_and_close_socket(socket_t sock);
|
||||
|
||||
socket_t svr_sock_;
|
||||
std::string base_dir_;
|
||||
Handlers get_handlers_;
|
||||
|
@ -131,6 +162,7 @@ private:
|
|||
class Client {
|
||||
public:
|
||||
Client(const char* host, int port);
|
||||
virtual ~Client();
|
||||
|
||||
std::shared_ptr<Response> get(const char* url);
|
||||
std::shared_ptr<Response> head(const char* url);
|
||||
|
@ -139,14 +171,58 @@ public:
|
|||
|
||||
bool send(const Request& req, Response& res);
|
||||
|
||||
protected:
|
||||
bool process_request(Stream& strm, const Request& req, Response& res);
|
||||
|
||||
private:
|
||||
bool read_response_line(socket_t sock, Response& res);
|
||||
bool read_response_line(Stream& strm, Response& res);
|
||||
|
||||
virtual bool read_and_close_socket(socket_t sock, const Request& req, Response& res);
|
||||
|
||||
const std::string host_;
|
||||
const int port_;
|
||||
};
|
||||
|
||||
// Implementation
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
class SSLSocketStream : public Stream {
|
||||
public:
|
||||
SSLSocketStream(SSL* ssl);
|
||||
virtual ~SSLSocketStream();
|
||||
|
||||
virtual int read(char* ptr, size_t size);
|
||||
virtual int write(const char* ptr, size_t size);
|
||||
virtual int write(const char* ptr);
|
||||
|
||||
private:
|
||||
SSL* ssl_;
|
||||
};
|
||||
|
||||
class SSLServer : public Server {
|
||||
public:
|
||||
SSLServer(const char* cert_path, const char* private_key_path);
|
||||
virtual ~SSLServer();
|
||||
|
||||
private:
|
||||
virtual bool read_and_close_socket(socket_t sock);
|
||||
|
||||
SSL_CTX* ctx_;
|
||||
};
|
||||
|
||||
class SSLClient : public Client {
|
||||
public:
|
||||
SSLClient(const char* host, int port);
|
||||
virtual ~SSLClient();
|
||||
|
||||
private:
|
||||
virtual bool read_and_close_socket(socket_t sock, const Request& req, Response& res);
|
||||
|
||||
SSL_CTX* ctx_;
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Implementation
|
||||
*/
|
||||
namespace detail {
|
||||
|
||||
template <class Fn>
|
||||
|
@ -168,30 +244,14 @@ void split(const char* b, const char* e, char d, Fn fn)
|
|||
}
|
||||
}
|
||||
|
||||
inline int socket_read(socket_t sock, char* ptr, size_t size)
|
||||
{
|
||||
return recv(sock, ptr, size, 0);
|
||||
}
|
||||
|
||||
inline int socket_write(socket_t sock, const char* ptr, size_t size)
|
||||
{
|
||||
return send(sock, ptr, size, 0);
|
||||
}
|
||||
|
||||
inline int socket_write(socket_t sock, const char* ptr)
|
||||
{
|
||||
size_t size = strlen(ptr);
|
||||
return socket_write(sock, ptr, size);
|
||||
}
|
||||
|
||||
inline bool socket_gets(socket_t sock, char* buf, int bufsiz)
|
||||
inline bool socket_gets(Stream& strm, char* buf, int bufsiz)
|
||||
{
|
||||
// TODO: buffering for better performance
|
||||
size_t i = 0;
|
||||
|
||||
for (;;) {
|
||||
char byte;
|
||||
auto n = socket_read(sock, &byte, 1);
|
||||
auto n = strm.read(&byte, 1);
|
||||
|
||||
if (n < 1) {
|
||||
if (i == 0) {
|
||||
|
@ -213,7 +273,7 @@ inline bool socket_gets(socket_t sock, char* buf, int bufsiz)
|
|||
}
|
||||
|
||||
template <typename ...Args>
|
||||
inline void socket_printf(socket_t sock, const char* fmt, const Args& ...args)
|
||||
inline void socket_printf(Stream& strm, const char* fmt, const Args& ...args)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
auto n = snprintf(buf, BUFSIZ, fmt, args...);
|
||||
|
@ -221,7 +281,7 @@ inline void socket_printf(socket_t sock, const char* fmt, const Args& ...args)
|
|||
if (n >= BUFSIZ) {
|
||||
// TODO: buffer size is not large enough...
|
||||
} else {
|
||||
socket_write(sock, buf, n);
|
||||
strm.write(buf, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -238,7 +298,8 @@ inline int close_socket(socket_t sock)
|
|||
template <typename T>
|
||||
inline bool read_and_close_socket(socket_t sock, T callback)
|
||||
{
|
||||
auto ret = callback(sock);
|
||||
SocketStream strm(sock);
|
||||
auto ret = callback(strm);
|
||||
close_socket(sock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -394,7 +455,7 @@ inline int get_header_value_int(const MultiMap& map, const char* key, int def)
|
|||
return def;
|
||||
}
|
||||
|
||||
inline bool read_headers(socket_t sock, MultiMap& headers)
|
||||
inline bool read_headers(Stream& strm, MultiMap& headers)
|
||||
{
|
||||
static std::regex re("(.+?): (.+?)\r\n");
|
||||
|
||||
|
@ -402,7 +463,7 @@ inline bool read_headers(socket_t sock, MultiMap& headers)
|
|||
char buf[BUFSIZ_HEADER];
|
||||
|
||||
for (;;) {
|
||||
if (!socket_gets(sock, buf, BUFSIZ_HEADER)) {
|
||||
if (!socket_gets(strm, buf, BUFSIZ_HEADER)) {
|
||||
return false;
|
||||
}
|
||||
if (!strcmp(buf, "\r\n")) {
|
||||
|
@ -420,12 +481,12 @@ inline bool read_headers(socket_t sock, MultiMap& headers)
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
bool read_content(socket_t sock, T& x)
|
||||
bool read_content(Stream& strm, T& x)
|
||||
{
|
||||
auto len = get_header_value_int(x.headers, "Content-Length", 0);
|
||||
if (len) {
|
||||
x.body.assign(len, 0);
|
||||
if (!socket_read(sock, &x.body[0], x.body.size())) {
|
||||
if (!strm.read(&x.body[0], x.body.size())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -433,30 +494,30 @@ bool read_content(socket_t sock, T& x)
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
inline void write_headers(socket_t sock, const T& res)
|
||||
inline void write_headers(Stream& strm, const T& res)
|
||||
{
|
||||
socket_write(sock, "Connection: close\r\n");
|
||||
strm.write("Connection: close\r\n");
|
||||
|
||||
for (const auto& x: res.headers) {
|
||||
if (x.first != "Content-Type" && x.first != "Content-Length") {
|
||||
socket_printf(sock, "%s: %s\r\n", x.first.c_str(), x.second.c_str());
|
||||
socket_printf(strm, "%s: %s\r\n", x.first.c_str(), x.second.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
auto t = get_header_value(res.headers, "Content-Type", "text/plain");
|
||||
socket_printf(sock, "Content-Type: %s\r\n", t);
|
||||
socket_printf(sock, "Content-Length: %ld\r\n", res.body.size());
|
||||
socket_write(sock, "\r\n");
|
||||
socket_printf(strm, "Content-Type: %s\r\n", t);
|
||||
socket_printf(strm, "Content-Length: %ld\r\n", res.body.size());
|
||||
strm.write("\r\n");
|
||||
}
|
||||
|
||||
inline void write_response(socket_t sock, const Request& req, const Response& res)
|
||||
inline void write_response(Stream& strm, const Request& req, const Response& res)
|
||||
{
|
||||
socket_printf(sock, "HTTP/1.0 %d %s\r\n", res.status, status_message(res.status));
|
||||
socket_printf(strm, "HTTP/1.0 %d %s\r\n", res.status, status_message(res.status));
|
||||
|
||||
write_headers(sock, res);
|
||||
write_headers(strm, res);
|
||||
|
||||
if (!res.body.empty() && req.method != "HEAD") {
|
||||
socket_write(sock, res.body.c_str(), res.body.size());
|
||||
strm.write(res.body.c_str(), res.body.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -591,19 +652,19 @@ inline std::string decode_url(const std::string& s)
|
|||
return result;
|
||||
}
|
||||
|
||||
inline void write_request(socket_t sock, const Request& req)
|
||||
inline void write_request(Stream& strm, const Request& req)
|
||||
{
|
||||
auto url = encode_url(req.url);
|
||||
socket_printf(sock, "%s %s HTTP/1.0\r\n", req.method.c_str(), url.c_str());
|
||||
socket_printf(strm, "%s %s HTTP/1.0\r\n", req.method.c_str(), url.c_str());
|
||||
|
||||
write_headers(sock, req);
|
||||
write_headers(strm, req);
|
||||
|
||||
if (!req.body.empty()) {
|
||||
if (req.has_header("application/x-www-form-urlencoded")) {
|
||||
auto str = encode_url(req.body);
|
||||
socket_write(sock, str.c_str(), str.size());
|
||||
strm.write(str.c_str(), str.size());
|
||||
} else {
|
||||
socket_write(sock, req.body.c_str(), req.body.size());
|
||||
strm.write(req.body.c_str(), req.body.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -697,12 +758,40 @@ inline void Response::set_content(const std::string& s, const char* content_type
|
|||
set_header("Content-Type", content_type);
|
||||
}
|
||||
|
||||
// Socket stream implementation
|
||||
inline SocketStream::SocketStream(socket_t sock): sock_(sock)
|
||||
{
|
||||
}
|
||||
|
||||
inline SocketStream::~SocketStream()
|
||||
{
|
||||
}
|
||||
|
||||
inline int SocketStream::read(char* ptr, size_t size)
|
||||
{
|
||||
return recv(sock_, ptr, size, 0);
|
||||
}
|
||||
|
||||
inline int SocketStream::write(const char* ptr, size_t size)
|
||||
{
|
||||
return send(sock_, ptr, size, 0);
|
||||
}
|
||||
|
||||
inline int SocketStream::write(const char* ptr)
|
||||
{
|
||||
return write(ptr, strlen(ptr));
|
||||
}
|
||||
|
||||
// HTTP server implementation
|
||||
inline Server::Server()
|
||||
: svr_sock_(-1)
|
||||
{
|
||||
}
|
||||
|
||||
inline Server::~Server()
|
||||
{
|
||||
}
|
||||
|
||||
inline void Server::get(const char* pattern, Handler handler)
|
||||
{
|
||||
get_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
|
||||
|
@ -755,10 +844,7 @@ inline bool Server::listen(const char* host, int port)
|
|||
}
|
||||
|
||||
// TODO: should be async
|
||||
detail::read_and_close_socket(sock, [this](socket_t sock) {
|
||||
process_request(sock);
|
||||
return true;
|
||||
});
|
||||
read_and_close_socket(sock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -771,11 +857,11 @@ inline void Server::stop()
|
|||
svr_sock_ = -1;
|
||||
}
|
||||
|
||||
inline bool Server::read_request_line(socket_t sock, Request& req)
|
||||
inline bool Server::read_request_line(Stream& strm, Request& req)
|
||||
{
|
||||
const auto BUFSIZ_REQUESTLINE = 2048;
|
||||
char buf[BUFSIZ_REQUESTLINE];
|
||||
if (!detail::socket_gets(sock, buf, BUFSIZ_REQUESTLINE)) {
|
||||
if (!detail::socket_gets(strm, buf, BUFSIZ_REQUESTLINE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -809,7 +895,7 @@ inline bool Server::handle_file_request(Request& req, Response& res)
|
|||
|
||||
if (detail::is_file(path)) {
|
||||
detail::read_file(path, res.body);
|
||||
res.set_header("Content-Type",
|
||||
res.set_header("Content-Type",
|
||||
detail::get_content_type_from_file_extention(
|
||||
detail::get_file_extention(path)));
|
||||
res.status = 200;
|
||||
|
@ -848,18 +934,20 @@ inline bool Server::dispatch_request(Request& req, Response& res, Handlers& hand
|
|||
return false;
|
||||
}
|
||||
|
||||
inline void Server::process_request(socket_t sock)
|
||||
inline void Server::process_request(Stream& strm)
|
||||
{
|
||||
Request req;
|
||||
Response res;
|
||||
|
||||
if (!read_request_line(sock, req) ||
|
||||
!detail::read_headers(sock, req.headers)) {
|
||||
if (!read_request_line(strm, req) ||
|
||||
!detail::read_headers(strm, req.headers)) {
|
||||
// TODO:
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.method == "POST") {
|
||||
if (!detail::read_content(sock, req)) {
|
||||
if (!detail::read_content(strm, req)) {
|
||||
// TODO:
|
||||
return;
|
||||
}
|
||||
static std::string type = "application/x-www-form-urlencoded";
|
||||
|
@ -881,13 +969,21 @@ inline void Server::process_request(socket_t sock)
|
|||
error_handler_(req, res);
|
||||
}
|
||||
|
||||
detail::write_response(sock, req, res);
|
||||
detail::write_response(strm, req, res);
|
||||
|
||||
if (logger_) {
|
||||
logger_(req, res);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool Server::read_and_close_socket(socket_t sock)
|
||||
{
|
||||
return detail::read_and_close_socket(sock, [this](Stream& strm) {
|
||||
process_request(strm);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// HTTP client implementation
|
||||
inline Client::Client(const char* host, int port)
|
||||
: host_(host)
|
||||
|
@ -895,11 +991,15 @@ inline Client::Client(const char* host, int port)
|
|||
{
|
||||
}
|
||||
|
||||
inline bool Client::read_response_line(socket_t sock, Response& res)
|
||||
inline Client::~Client()
|
||||
{
|
||||
}
|
||||
|
||||
inline bool Client::read_response_line(Stream& strm, Response& res)
|
||||
{
|
||||
const auto BUFSIZ_RESPONSELINE = 2048;
|
||||
char buf[BUFSIZ_RESPONSELINE];
|
||||
if (!detail::socket_gets(sock, buf, BUFSIZ_RESPONSELINE)) {
|
||||
if (!detail::socket_gets(strm, buf, BUFSIZ_RESPONSELINE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -920,22 +1020,32 @@ inline bool Client::send(const Request& req, Response& res)
|
|||
return false;
|
||||
}
|
||||
|
||||
return detail::read_and_close_socket(sock, [&](socket_t sock) {
|
||||
// Send request
|
||||
detail::write_request(sock, req);
|
||||
return read_and_close_socket(sock, req, res);
|
||||
}
|
||||
|
||||
// Receive response
|
||||
if (!read_response_line(sock, res) ||
|
||||
!detail::read_headers(sock, res.headers)) {
|
||||
inline bool Client::process_request(Stream& strm, const Request& req, Response& res)
|
||||
{
|
||||
// Send request
|
||||
detail::write_request(strm, req);
|
||||
|
||||
// Receive response
|
||||
if (!read_response_line(strm, res) ||
|
||||
!detail::read_headers(strm, res.headers)) {
|
||||
return false;
|
||||
}
|
||||
if (req.method != "HEAD") {
|
||||
if (!detail::read_content(strm, res)) {
|
||||
return false;
|
||||
}
|
||||
if (req.method != "HEAD") {
|
||||
if (!detail::read_content(sock, res)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Client::read_and_close_socket(socket_t sock, const Request& req, Response& res)
|
||||
{
|
||||
return detail::read_and_close_socket(sock, [&](Stream& strm) {
|
||||
return process_request(strm, req, res);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -991,6 +1101,124 @@ inline std::shared_ptr<Response> Client::post(
|
|||
return post(url, query, "application/x-www-form-urlencoded");
|
||||
}
|
||||
|
||||
/*
|
||||
* SSL Implementation
|
||||
*/
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
namespace detail {
|
||||
|
||||
template <typename U, typename T>
|
||||
inline bool read_and_close_socket_ssl(socket_t sock, SSL_CTX* ctx, U SSL_connect_or_accept, T callback)
|
||||
{
|
||||
auto ssl = SSL_new(ctx);
|
||||
SSL_set_fd(ssl, sock);
|
||||
SSL_connect_or_accept(ssl);
|
||||
|
||||
SSLSocketStream strm(ssl);
|
||||
auto ret = callback(strm);
|
||||
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
close_socket(sock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
class SSLInit {
|
||||
public:
|
||||
SSLInit() {
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
}
|
||||
};
|
||||
|
||||
static SSLInit sslinit_;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// SSL socket stream implementation
|
||||
inline SSLSocketStream::SSLSocketStream(SSL* ssl): ssl_(ssl)
|
||||
{
|
||||
}
|
||||
|
||||
inline SSLSocketStream::~SSLSocketStream()
|
||||
{
|
||||
}
|
||||
|
||||
inline int SSLSocketStream::read(char* ptr, size_t size)
|
||||
{
|
||||
return SSL_read(ssl_, ptr, size);
|
||||
}
|
||||
|
||||
inline int SSLSocketStream::write(const char* ptr, size_t size)
|
||||
{
|
||||
return SSL_write(ssl_, ptr, size);
|
||||
}
|
||||
|
||||
inline int SSLSocketStream::write(const char* ptr)
|
||||
{
|
||||
return write(ptr, strlen(ptr));
|
||||
}
|
||||
|
||||
// SSL HTTP server implementation
|
||||
inline SSLServer::SSLServer(const char* cert_path, const char* private_key_path)
|
||||
{
|
||||
ctx_ = SSL_CTX_new(SSLv23_server_method());
|
||||
|
||||
if (ctx_) {
|
||||
SSL_CTX_set_options(ctx_,
|
||||
SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
||||
SSL_OP_NO_COMPRESSION |
|
||||
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
|
||||
|
||||
// auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
// SSL_CTX_set_tmp_ecdh(ctx_, ecdh);
|
||||
// EC_KEY_free(ecdh);
|
||||
|
||||
if (SSL_CTX_use_certificate_file(ctx_, cert_path, SSL_FILETYPE_PEM) != 1 ||
|
||||
SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1) {
|
||||
SSL_CTX_free(ctx_);
|
||||
ctx_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline SSLServer::~SSLServer()
|
||||
{
|
||||
if (ctx_) {
|
||||
SSL_CTX_free(ctx_);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool SSLServer::read_and_close_socket(socket_t sock)
|
||||
{
|
||||
return detail::read_and_close_socket_ssl(sock, ctx_, SSL_accept, [this](Stream& strm) {
|
||||
process_request(strm);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// SSL HTTP client implementation
|
||||
inline SSLClient::SSLClient(const char* host, int port)
|
||||
: Client(host, port)
|
||||
{
|
||||
ctx_ = SSL_CTX_new(SSLv23_client_method());
|
||||
}
|
||||
|
||||
inline SSLClient::~SSLClient()
|
||||
{
|
||||
if (ctx_) {
|
||||
SSL_CTX_free(ctx_);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool SSLClient::read_and_close_socket(socket_t sock, const Request& req, Response& res)
|
||||
{
|
||||
return detail::read_and_close_socket_ssl(sock, ctx_, SSL_connect, [&](Stream& strm) {
|
||||
return process_request(strm, req, res);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace httplib
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
|
||||
USE_CLANG = 1
|
||||
|
||||
ifdef USE_CLANG
|
||||
CC = clang++
|
||||
CCFLAGS = -std=c++1y -stdlib=libc++ -g -DGTEST_USE_OWN_TR1_TUPLE
|
||||
else
|
||||
CC = g++-4.9
|
||||
CCFLAGS = -std=c++1y -g
|
||||
endif
|
||||
CFLAGS = -std=c++14 -DGTEST_USE_OWN_TR1_TUPLE -I.. -I.
|
||||
#OPENSSL_SUPPORT = -DCPPHTTPLIB_OPENSSL_SUPPORT -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -lssl -lcrypto
|
||||
|
||||
all : test
|
||||
./test
|
||||
|
||||
test : test.cc ../httplib.h
|
||||
$(CC) -o test $(CCFLAGS) -I.. -I. test.cc gtest/gtest-all.cc gtest/gtest_main.cc
|
||||
$(CC) -o test $(CFLAGS) test.cc gtest/gtest-all.cc gtest/gtest_main.cc $(OPENSSL_SUPPORT)
|
||||
|
||||
pem:
|
||||
openssl genrsa 2048 > key.pem
|
||||
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
|
||||
|
||||
clean:
|
||||
rm test *.pem
|
||||
|
|
18
test/test.cc
18
test/test.cc
|
@ -4,6 +4,9 @@
|
|||
#include <future>
|
||||
#include <iostream>
|
||||
|
||||
#define SERVER_CERT_FILE "./cert.pem"
|
||||
#define SERVER_PRIVATE_KEY_FILE "./key.pem"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <process.h>
|
||||
#define msleep(n) ::Sleep(n)
|
||||
|
@ -99,8 +102,12 @@ TEST(GetHeaderValueTest, RegularValueInt)
|
|||
|
||||
class ServerTest : public ::testing::Test {
|
||||
protected:
|
||||
ServerTest() : cli_(HOST, PORT), up_(false) {
|
||||
}
|
||||
ServerTest()
|
||||
: cli_(HOST, PORT)
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
, svr_(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE)
|
||||
#endif
|
||||
, up_(false) {}
|
||||
|
||||
virtual void SetUp() {
|
||||
svr_.set_base_dir("./www");
|
||||
|
@ -155,8 +162,13 @@ protected:
|
|||
}
|
||||
|
||||
map<string, string> persons_;
|
||||
Server svr_;
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
SSLClient cli_;
|
||||
SSLServer svr_;
|
||||
#else
|
||||
Client cli_;
|
||||
Server svr_;
|
||||
#endif
|
||||
future<void> f_;
|
||||
bool up_;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue