此处说明一下:
1.ssl的初始化整个进程只需执行一次,请勿多次初始化。谨防析构函数多次析构ctx
2.此处因为封装为类,故将回调函数设置为静态。如有需要回调函数返回值,可用类成员变量存储,调用回调函数时传入this指针,以此将返回值带出,需将获取返回值的函数阻塞。防止获取返回值时,回调函数还未返回
3.密钥等需自行生成
https_server.h
4.此时openssl库用的较低版本libssl.so.1,libevent库用的大约2.0或2.1版本
#ifndef HTTPS_SERVER_H
#define HTTPS_SERVER_H
#include <iostream>
#include <openssl/ssl.h>
#include <event2/event.h>
#include <event2/bufferevent_ssl.h>
#include <event2/http.h>
class HttpsServer {
public:
HttpsServer();
~HttpsServer();
int StartServerHttps ();
int ServerSetCerts ();
int Init();
private:
event_base* m_base;
evhttp* m_http;
SSL_CTX* m_ctx;
EC_KEY* m_ecdh;
};
#endif
https_server.cpp
#include "https_server.h"
#include <event2/buffer.h>
#include "json/json.h"
#include <unistd.h>
#define MYHTTPD_SIGNATURE "MoCarHttpd v0.1"
#define HTTPS_PORT 10999
#define HTTP_CERT_PEM "cert.pem"
#define HTTP_KEY_PEM "key.pem"
static void *MyZeroMalloc (size_t howmuch)
{
return calloc (1, howmuch);
}
HttpsServer::HttpsServer():m_base(NULL),m_http(NULL),m_ctx(NULL),m_ecdh(NULL)
{
}
HttpsServer::~HttpsServer()
{
if (m_base != NULL)
{
event_base_free(m_base);
}
if (m_http != NULL)
{
evhttp_free(m_http);
}
if (m_ctx != NULL)
{
SSL_CTX_free(m_ctx);
}
if (m_ecdh != NULL)
{
EC_KEY_free(m_ecdh);
}
}
static void Login (struct evhttp_request *req, void *arg)
{
HttpsServer* self = static_cast<HttpsServer*>(arg);
struct evbuffer *evb = NULL;
const char *uri = evhttp_request_get_uri (req);
struct evhttp_uri *decoded = NULL;
/* 判断 req 是否是GET 请求 */
if (evhttp_request_get_command (req) != EVHTTP_REQ_POST)
{
evhttp_send_reply(req, 405, "Method Not Allowed", nullptr);
return;
}
//判断此URI是否合法
decoded = evhttp_uri_parse (uri);
if (! decoded)
{
printf("It's not a good URI. Sending BADREQUEST\n");
evhttp_send_error (req, HTTP_BADREQUEST, 0);
return;
}
/* Decode the payload */
struct evbuffer *buf = evhttp_request_get_input_buffer (req);
evbuffer_add (buf, "", 1); /* NUL-terminate the buffer */
char *payload = (char *) evbuffer_pullup (buf, -1);
int post_data_len = evbuffer_get_length(buf);
char request_data_buf[4096] = {0};
memcpy(request_data_buf, payload, post_data_len);
printf("https server received body : \n%s ",payload);
evhttp_add_header(evhttp_request_get_output_headers(req), "Server", MYHTTPD_SIGNATURE);
evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/plain; charset=UTF-8");
evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close");
Json::Value jsonData;
jsonData["code"] = 0;
jsonData["msg"] = "post login success !";
std::string jsonString = jsonData.toStyledString();
evb = evbuffer_new ();
evbuffer_add_printf(evb, "%s", jsonString.c_str());
//将封装好的evbuffer 发送给客户端
evhttp_send_reply(req, HTTP_OK, "OK", evb);
if (decoded)
evhttp_uri_free (decoded);
if (evb)
evbuffer_free (evb);
}
int HttpsServer::Init()
{
CRYPTO_set_mem_functions (MyZeroMalloc, realloc, free);
SSL_library_init ();
SSL_load_error_strings ();
OpenSSL_add_all_algorithms ();
m_base = event_base_new ();
if (!m_base)
{
printf("Couldn't create an event_base: exiting\n");
return -1;
}
m_http = evhttp_new (m_base);
if (!m_http)
{
printf("couldn't create evhttp. Exiting.\n");
return -2;
}
m_ctx = SSL_CTX_new (SSLv23_server_method ());
SSL_CTX_set_options (m_ctx,
SSL_OP_SINGLE_DH_USE |
SSL_OP_SINGLE_ECDH_USE |
SSL_OP_NO_SSLv2);
m_ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
if (!m_ecdh)
{
printf("EC_KEY_new_by_curve_name fail");
return -3;
}
if (1 != SSL_CTX_set_tmp_ecdh (m_ctx, m_ecdh))
{
printf("SSL_CTX_set_tmp_ecdh fail");
return -4;
}
int ret = ServerSetCerts();
if (ret < 0 )
{
return ret;
}
return 0;
}
static struct bufferevent* bevcb (struct event_base *base, void *arg)
{
struct bufferevent* r;
SSL_CTX *ctx = (SSL_CTX *) arg;
r = bufferevent_openssl_socket_new (base,
-1,
SSL_new (ctx),
BUFFEREVENT_SSL_ACCEPTING,
BEV_OPT_CLOSE_ON_FREE);
return r;
}
int HttpsServer::ServerSetCerts ()
{
if (1 != SSL_CTX_use_certificate_chain_file (m_ctx, HTTP_CERT_PEM))
{
printf("SSL_CTX_use_certificate_chain_file fail");
return -1;
}
if (1 != SSL_CTX_use_PrivateKey_file (m_ctx, HTTP_KEY_PEM, SSL_FILETYPE_PEM))
{
printf("SSL_CTX_use_PrivateKey_file fail");
return -2;
}
if (1 != SSL_CTX_check_private_key (m_ctx))
{
printf("SSL_CTX_check_private_key fail");
return -3;
}
return 0;
}
int HttpsServer::StartServerHttps ()
{
evhttp_set_bevcb (m_http, bevcb, m_ctx);
evhttp_set_cb(m_http, "/login", Login, this);
evhttp_bound_socket* handle = evhttp_bind_socket_with_handle (m_http, "0.0.0.0", HTTPS_PORT);
if (!handle)
{
fprintf (stderr, "couldn't bind to port %d. Exiting.\n",(int) HTTPS_PORT);
return 1;
}
event_base_dispatch (m_base);
return 0;
}
main.cpp
#include "https_server.h"
int main()
{
HttpsServer* server = new HttpsServer;
server->Init();
server->StartServerHttps();
return 0;
}
make.sh
g++ *.cpp -L/usr/local/lib64 -lssl -lcrypto -levent_openssl -levent -ljsoncpp -o server -ggdb -std=c++11 -Wno-deprecated-declarations