Android:libevent实现https服务器

libevent实现https服务器
 参考老外服务器代码:
https://github.com/ppelleti/https-example
确保libevent在2.1.2之上版本。
确保系统安装openssl,确保libevent_openssl.so存在
搭建支持http是的libevent
 1下载libevent 和 openssl
 libevent
http://libevent.org/
2 安装openssl
 wget http://www.openssl.org/source/openssl-1.0.1t.tar.gz
 tar -zxvf openssl-1.0.1c.tar.gz
 ./config --prefix=/usr/local --openssldir=/usr/local/openssl
make depend
 make
 sudo make install
//若要生成libssl.so动态库文件 需要如下make
 make clean
 ./config shared --prefix=/usr/local --openssldir=/usr/local/openssl
 make depend
 make
 sudo make install
 *这里要注意,在执行./config的时候一定要加上 --prefix=/usr/local 和–openssldir=/usr/local/ssl/
否则libevent是找不到openssl库,那就不会编译生成带有openssl的libevent库了。*
3安装libevent
 tar -zxvf libevent-release-2.1.6-beta.tar.gz
 ./autogen.sh
 问题(1)如果出现
./autogen.sh: 11: ./autogen.sh: aclocal: not found
 需要安装automake工具。
sudo apt-get install automake
 问题(2) 如果出现
configure.ac:137: error: possibly undefined macro: AC_PROG_LIBTOOL
 需要安装libtool工具。
sudo apt-get install libtool
 sudo apt-get install libsysfs-dev
 如果成功,执行
./configure
 make
 sudo make install
 登陆服务实现
 结合我们之前用libevent实现的登陆服务器代码和老外的参考代码,
现在提供登陆的功能的https服务器代码.
此时服务器路径文件包括以下:
$ls
 cJSON.c https-common.h server-private-key.pem
 cJSON.h https-server.c server-certificate-chain.pem
 https-common.c Makefile
 其中 server-certificate-chain.pem 为 当前https服务器的私人证书,此证书并没有CA认证,如果想要合法证书请购买。(改证书里携带 此服务器 认证公钥)
其中 server-private-key.pem 是此服务器 认证私钥
其中 https-common.h https-server.c https-common.c 为https服务器源码
https-common.h
/* © Oblong Industries */
#ifndef COMMON_MAN
 #define COMMON_MAN
/
- Rather than using the standard https port of 443, use this one.
 */
 #define COMMON_HTTPS_PORT 8421
/
- This is the string the client tells the server in the POST request.
 */
 #define COMMON_PASSCODE “R23”
/
- If an OpenSSL function returns a return value indicating failure
- (for non-pointery functions, generally 1 means success, and non-1
- means failure), then usually it ought to have left one or more
- entries in the OpenSSL “error stack”, which is a bit of thread-local
- state containing error information.
- This function is a simple way to handle OpenSSL errors (something
- better may be needed in a real application), which prints the
- name of the function which failed (which you must supply as an
- argument), prints the OpenSSL error stack (which hopefully says
- something meaningful) and exits.
 */
 void die_most_horribly_from_openssl_error (const char *func);
void error_exit (const char *fmt, …);
#define error_report printf
 #define info_report printf
/
- Calls some OpenSSL setup functions, which both the client and
- server need to do.
 */
 void common_setup (void);
#endif /* COMMON_MAN */
 https-common.c
/* © Oblong Industries */
#include <signal.h>
#include “https-common.h”
#include <openssl/ssl.h>
 #include <openssl/err.h>
#include <event2/event.h>
/* 这个是调用openSSL提供的打印log接口 */
 void die_most_horribly_from_openssl_error (const char *func)
 {
 fprintf (stderr, “%s failed:\\n”, func);
/* This is the OpenSSL function that prints the contents of the* error stack to the specified file handle. */
ERR_print_errors_fp (stderr);exit (EXIT_FAILURE);
}
void error_exit (const char *fmt, …)
 { va_list ap;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);exit (EXIT_FAILURE);
}
/* OpenSSL has a habit of using uninitialized memory. (They turn up their
- nose at tools like valgrind.) To avoid spurious valgrind errors (as well
- as to allay any concerns that the uninitialized memory is actually
- affecting behavior), let’s install a custom malloc function which is
- actually calloc.
 */
 static void *my_zeroing_malloc (size_t howmuch)
 {
 return calloc (1, howmuch);
 }
void common_setup (void)
 {
 signal (SIGPIPE, SIG_IGN);
CRYPTO_set_mem_functions (my_zeroing_malloc, realloc, free);
SSL_library_init ();
SSL_load_error_strings ();
OpenSSL_add_all_algorithms ();printf ("Using OpenSSL version \\"%s\\"\\nand libevent version \\"%s\\"\\n",SSLeay_version (SSLEAY_VERSION),event_get_version ());
}
 https-server.c
/
- @file https-server.c
- @brief
- @author liu_danbing
- @version 1.0
- @date 2016-10-26
 */
#include “https-common.h”
#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <limits.h>
#include <sys/types.h>
 #include <sys/stat.h>
#include <sys/stat.h>
 #include <sys/socket.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <dirent.h>
#include <openssl/ssl.h>
 #include <openssl/err.h>
#include <event2/bufferevent.h>
 #include <event2/bufferevent_ssl.h>
 #include <event2/event.h>
 #include <event2/http.h>
 #include <event2/buffer.h>
 #include <event2/util.h>
 #include <event2/keyvalq_struct.h>
#include <cJSON.h>
#define MYHTTPD_SIGNATURE “MoCarHttpd v0.1”
#ifdef EVENT__HAVE_NETINET_IN_H
 #include <netinet/in.h>
ifdef _XOPEN_SOURCE_EXTENDED
include <arpa/inet.h>
endif
#endif
unsigned short serverPort = COMMON_HTTPS_PORT;
/* Instead of casting between these types, create a union with all of them,
- to avoid -Wstrict-aliasing warnings. */
 typedef union
 {
 struct sockaddr_storage ss;
 struct sockaddr sa;
 struct sockaddr_in in;
 struct sockaddr_in6 i6;
 } sock_hop;
/* This callback gets invoked when we get any http request that doesn’t match
- 
any other callback. Like any evhttp server callback, it has a simple job: 
- 
it must eventually call evhttp_send_error() or evhttp_send_reply(). 
 */
 static void
 login_cb (struct evhttp_request *req, void *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_GET)
 {
 struct evbuffer *buf = evbuffer_new();
 if (buf == NULL) return;
 evbuffer_add_printf(buf, “Requested: %s\\n”, uri);
 evhttp_send_reply(req, HTTP_OK, “OK”, buf);
 return;
 }/* 这里只处理Post请求, Get请求,就直接return 200 OK */ 
 if (evhttp_request_get_command (req) != EVHTTP_REQ_POST)
 {
 evhttp_send_reply (req, 200, “OK”, NULL);
 return;
 }printf (“Got a POST request for <%s>\\n”, uri); //判断此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(“[post_data][%d]=\\n %s\\n”, post_data_len, payload);/* 
 具体的:可以根据Post的参数执行相应操作,然后将结果输出
 …
 /
 //unpack json
 cJSON root = cJSON_Parse(request_data_buf);
 cJSON* username = cJSON_GetObjectItem(root, “username”);
 cJSON* password = cJSON_GetObjectItem(root, “password”);printf(“username = %s\\n”, username->valuestring); 
 printf(“password = %s\\n”, password->valuestring);cJSON_Delete(root); //packet json 
 root = cJSON_CreateObject();cJSON_AddStringToObject(root, “result”, “ok”); 
 cJSON_AddStringToObject(root, “sessionid”, “xxxxxxxx”);char *response_data = cJSON_Print(root); 
 cJSON_Delete(root);/* This holds the content we’re sending. */ //HTTP header 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”);evb = evbuffer_new (); 
 evbuffer_add_printf(evb, “%s”, response_data);
 //将封装好的evbuffer 发送给客户端
 evhttp_send_reply(req, HTTP_OK, “OK”, evb);if (decoded) 
 evhttp_uri_free (decoded);
 if (evb)
 evbuffer_free (evb);printf(“[response]:\\n”); 
 printf(“%s\\n”, response_data);free(response_data); 
 }
/
- 
This callback is responsible for creating a new SSL connection 
- 
and wrapping it in an OpenSSL bufferevent. This is the way 
- 
we implement an https server instead of a plain old http server. 
 /
 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;
 }
static void server_setup_certs (SSL_CTX *ctx,
 const char *certificate_chain,
 const char *private_key)
 {
 info_report (“Loading certificate chain from ‘%s’\\n”
 “and private key from ‘%s’\\n”,
 certificate_chain, private_key);
if (1 != SSL_CTX_use_certificate_chain_file (ctx, certificate_chain))die_most_horribly_from_openssl_error ("SSL_CTX_use_certificate_chain_file");if (1 != SSL_CTX_use_PrivateKey_file (ctx, private_key, SSL_FILETYPE_PEM))die_most_horribly_from_openssl_error ("SSL_CTX_use_PrivateKey_file");if (1 != SSL_CTX_check_private_key (ctx))die_most_horribly_from_openssl_error ("SSL_CTX_check_private_key");
}
static int serve_some_http (void)
 {
 struct event_base *base;
 struct evhttp *http;
 struct evhttp_bound_socket *handle;
base = event_base_new ();
if (! base)
{ fprintf (stderr, "Couldn't create an event_base: exiting\\n");return 1;
}/* 创建一个 evhttp 句柄,去处理用户端的requests请求 */
http = evhttp_new (base);
if (! http)
{ fprintf (stderr, "couldn't create evhttp. Exiting.\\n");return 1;
}/* 创建SSL上下文环境 ,可以理解为 SSL句柄 */
SSL_CTX *ctx = SSL_CTX_new (SSLv23_server_method ());
SSL_CTX_set_options (ctx,SSL_OP_SINGLE_DH_USE |SSL_OP_SINGLE_ECDH_USE |SSL_OP_NO_SSLv2);/* Cheesily pick an elliptic curve to use with elliptic curve ciphersuites.* We just hardcode a single curve which is reasonably decent.* See http://www.mail-archive.com/openssl-dev@openssl.org/msg30957.html */
EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
if (! ecdh)die_most_horribly_from_openssl_error ("EC_KEY_new_by_curve_name");
if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh))die_most_horribly_from_openssl_error ("SSL_CTX_set_tmp_ecdh");/* 选择服务器证书 和 服务器私钥. */
const char *certificate_chain = "server-certificate-chain.pem";
const char *private_key = "server-private-key.pem";
/* 设置服务器证书 和 服务器私钥 到 OPENSSL ctx上下文句柄中 */
server_setup_certs (ctx, certificate_chain, private_key);/* 使我们创建好的evhttp句柄 支持 SSL加密实际上,加密的动作和解密的动作都已经帮我们自动完成,我们拿到的数据就已经解密之后的
*/
evhttp_set_bevcb (http, bevcb, ctx);/* 设置http回调函数 */
//默认回调
//evhttp_set_gencb (http, send_document_cb, NULL);
//专属uri路径回调
evhttp_set_cb(http, "/login", login_cb, NULL);/* 设置监听IP和端口 */
handle = evhttp_bind_socket_with_handle (http, "0.0.0.0", serverPort);
if (! handle)
{ fprintf (stderr, "couldn't bind to port %d. Exiting.\\n",(int) serverPort);return 1;
}{ /* 打印收到的客户端链接信息  */sock_hop ss;evutil_socket_t fd;ev_socklen_t socklen = sizeof (ss);char addrbuf[128];void *inaddr;const char *addr;int got_port = -1;fd = evhttp_bound_socket_get_fd (handle);memset (&ss, 0, sizeof(ss));if (getsockname (fd, &ss.sa, &socklen)){ perror ("getsockname() failed");return 1;}if (ss.ss.ss_family == AF_INET){ got_port = ntohs (ss.in.sin_port);inaddr = &ss.in.sin_addr;}else if (ss.ss.ss_family == AF_INET6){ got_port = ntohs (ss.i6.sin6_port);inaddr = &ss.i6.sin6_addr;}else{ fprintf (stderr, "Weird address family %d\\n", ss.ss.ss_family);return 1;}addr = evutil_inet_ntop (ss.ss.ss_family, inaddr, addrbuf,sizeof (addrbuf));if (addr)printf ("Listening on %s:%d\\n", addr, got_port);else{ fprintf (stderr, "evutil_inet_ntop failed\\n");return 1;}
}/* 开始阻塞监听 (永久执行) */
event_base_dispatch (base);return 0;
}
int main (int argc, char argv)
 {
 /*OpenSSL 初始化 */
 common_setup ();
if (argc > 1) {char *end_ptr;long lp = strtol(argv[1], &end_ptr, 0);if (*end_ptr) {fprintf(stderr, "Invalid integer\\n");return -1;}if (lp <= 0) {fprintf(stderr, "Port must be positive\\n");return -1;}if (lp >= USHRT_MAX) {fprintf(stderr, "Port must fit 16-bit range\\n");return -1;}serverPort = (unsigned short)lp;
}/* now run http server (never returns) */
return serve_some_http ();
}
 Makefile
CC=gcc
 CPPFLAGS= -I. -I./include
 CFLAGS=-Wall
 LIBS=-lpthread -levent -lm -lssl -lcrypto -levent_openssl
#找到当前目录下所有的.c文件
 src = $(wildcard *.c)
#将当前目录下所有的.c 转换成.o给obj
 obj = $(patsubst %.c, %.o, $(src))
server=server
 target=(server)ALL:(server) ALL:(server)ALL:(target)
#生成所有的.o文件
 $(obj):%.o:%.c
 $(CC) -c $< -o $@ $(CPPFLAGS) $(CFLAGS)
#test_main程序
 $(server): https-server.o https-common.o cJSON.o
 $(CC) $^ -o $@ $(LIBS)
#clean指令
clean:
 -rm -rf $(obj) $(target)
distclean:
 -rm -rf $(obj) $(target)
#将clean目标 改成一个虚拟符号
 .PHONY: clean ALL distclean


