acl/lib_acl_cpp/samples/ssl/ssl_server/ssl_server.cpp

446 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// main.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <assert.h>
#include "lib_acl.h"
#include "acl_cpp/lib_acl.hpp"
using namespace acl;
//////////////////////////////////////////////////////////////////////////
class http_servlet : public HttpServlet
{
public:
http_servlet()
{
param1_ = NULL;
param2_ = NULL;
param3_ = NULL;
file1_ = NULL;
file2_ = NULL;
file3_ = NULL;
first_ = true;
}
~http_servlet(void)
{
}
virtual bool doError(HttpServletRequest&, HttpServletResponse&)
{
if (first_)
logger_error("first request error");
return false;
}
// GET 方法
virtual bool doGet(HttpServletRequest& req, HttpServletResponse& res)
{
bool ret = doPost(req, res);
if (ret == false)
logger_error("doPost error!");
else
logger("doPost OK!");
return ret;
}
// POST 方法
virtual bool doPost(HttpServletRequest& req, HttpServletResponse& res)
{
first_ = false;
logger("request one now");
// 创建 HTTP 响应头
res.addCookie("name1", "value1");
res.addCookie("name2", "value2", ".test.com", "/", 3600 * 24);
res.setChunkedTransferEncoding(true);
res.setKeepAlive(true);
// res.setStatus(400); // 可以设置返回的状态码
// 两种方式都可以设置字符集
if (0)
res.setContentType("text/xml; charset=gb2312");
else
{
res.setContentType("text/xml");
res.setCharacterEncoding("gb2312");
}
// 获得 HTTP 请求的数据类型,正常的参数类型,即 name&value 方式
// 还是 MIME 数据类型,还是数据流类型
http_request_t request_type = req.getRequestType();
if (request_type == HTTP_REQUEST_NORMAL)
return doParams(req, res);
else if (request_type == HTTP_REQUEST_MULTIPART_FORM)
return doUpload(req, res);
assert(request_type == HTTP_REQUEST_OCTET_STREAM);
return doOctetStream(req, res);
}
bool doResponse(HttpServletRequest& req, HttpServletResponse& res)
{
// 获得浏览器传来的 cookie 值
const char* cookie1 = req.getCookieValue("name1");
const char* cookie2 = req.getCookieValue("name2");
// 创建 xml 格式的数据体
xml1 body;
body.get_root().add_child("root", true)
.add_child("content_type", true)
.add_attr("type", (int) req.getRequestType())
.get_parent()
.add_child("cookies", true)
.add_child("cookie", true)
.add_attr("name1", cookie1 ? cookie1 : "null")
.get_parent()
.add_child("cookie", true)
.add_attr("name2", cookie2 ? cookie2 : "null")
.get_parent()
.get_parent()
.add_child("params", true)
.add_child("param", true)
.add_attr("name1", param1_ ? param1_ : "null")
.get_parent()
.add_child("param", true)
.add_attr("name2", param2_ ? param2_ : "null")
.get_parent()
.add_child("param", true)
.add_attr("name3", param3_ ? param3_ : "null")
.get_parent()
.get_parent()
.add_child("files", true)
.add_child("file", true)
.add_attr("filename", file1_ ? file1_ : "null")
.get_parent()
.add_child("file", true)
.add_attr("filename", file2_ ? file2_ : "null")
.get_parent()
.add_child("file", true)
.add_attr("filename", file3_ ? file3_ : "null");
string buf("<?xml version=\"1.0\"?>");
body.build_xml(buf);
//printf(">>>response: %s\r\n", buf.c_str());
//res.setContentLength(buf.length());
// 不必显示工调用下面过程来发送 http 响应头
//if (res.sendHeader() == false)
// return false;
// 发送 http 响应体,当使用 chunk 传输时,必须最后调用一次发送空数据
if (res.write(buf) == false || res.write(NULL, 0) == false)
return false;
return true;
}
// GET 方式或 POST 方式且满足:
// Content-Type: application/x-www-form-urlencoded
bool doParams(HttpServletRequest& req, HttpServletResponse& res)
{
param1_ = req.getParameter("name1");
param2_ = req.getParameter("name2");
param3_ = req.getParameter("name2");
return doResponse(req, res);
}
// POST 方式且满足:
// Content-Type: multipart/form-data; boundary=xxx
bool doUpload(HttpServletRequest& req, HttpServletResponse& res)
{
// 先获得 Content-Type 对应的 http_ctype 对象
http_mime* mime = req.getHttpMime();
if (mime == NULL)
{
logger_error("http_mime null");
return false;
}
// 获得数据体的长度
long long int len = req.getContentLength();
if (len <= 0)
{
logger_error("body empty");
return false;
}
// 获得输入流
istream& in = req.getInputStream();
char buf[8192];
int ret;
bool finish = false;
const char* filepath = "./var/mime_file";
ofstream out;
out.open_write(filepath);
// 设置原始文件存入路径
mime->set_saved_path(filepath);
size_t k;
// 读取 HTTP 客户端请求数据
while (len > 0)
{
k = (size_t) len > sizeof(buf) ? sizeof(buf) : (size_t) len;
ret = in.read(buf, k, false);
if (ret == -1)
{
logger_error("read POST data error");
return false;
}
out.write(buf, ret);
len -= ret;
// 将读得到的数据输入至解析器进行解析
if (!finish && mime->update(buf, ret) == true)
finish = true;
}
out.close();
if (len != 0 || finish == false)
logger_warn("not read all data from client");
param1_ = req.getParameter("name1");
param2_ = req.getParameter("name2");
param3_ = req.getParameter("name3");
string path;
// 遍历所有的 MIME 结点,找出其中为文件结点的部分进行转储
const std::list<http_mime_node*>& nodes = mime->get_nodes();
std::list<http_mime_node*>::const_iterator cit = nodes.begin();
for (; cit != nodes.end(); ++cit)
{
const char* name = (*cit)->get_name();
if (name == NULL)
continue;
http_mime_t mime_type = (*cit)->get_mime_type();
if (mime_type == HTTP_MIME_FILE)
{
const char* filename = (*cit)->get_filename();
if (filename == NULL)
{
logger("filename null");
continue;
}
if (strcmp(name, "file1") == 0)
file1_ = filename;
else if (strcmp(name, "file2") == 0)
file2_ = filename;
else if (strcmp(name, "file3") == 0)
file3_ = filename;
// 有的浏览器如IE上传文件时会带着文件路径所以
// 需要先将路径去掉
filename = acl_safe_basename(filename);
#ifdef WIN32
path.format("var\\%s", filename);
#else
path.format("./var/%s", filename);
#endif
(void) (*cit)->save(path.c_str());
}
}
// 查找上载的某个文件并转储
const http_mime_node* node = mime->get_node("file1");
if (node && node->get_mime_type() == HTTP_MIME_FILE)
{
const char* ptr = node->get_filename();
if (ptr)
{
// 有的浏览器如IE上传文件时会带着文件路径所以
// 需要先将路径去掉
ptr = acl_safe_basename(ptr);
#ifdef WIN32
path.format(".\\var\\1_%s", ptr);
#else
path.format("./var/1_%s", ptr);
#endif
(void) node->save(path.c_str());
}
}
return doResponse(req, res);
}
// POST 方式且满足:
// Content-Type: application/octet-stream
bool doOctetStream(HttpServletRequest&, HttpServletResponse&)
{
logger_error("not support now!");
return false;
}
protected:
private:
const char* param1_;
const char* param2_;
const char* param3_;
const char* file1_;
const char* file2_;
const char* file3_;
bool first_;
};
//////////////////////////////////////////////////////////////////////////
static void do_run(socket_stream* stream)
{
memcache_session session("127.0.0.1:11211");
http_servlet servlet;
servlet.setLocalCharset("gb2312");
servlet.doRun(session, stream);
}
// 服务器方式运行时的服务类
class master_service : public master_proc
{
public:
master_service(const char* crt_file, const char* key_file,
acl::polarssl_verify_t verify_mode)
{
if (crt_file && *crt_file && key_file && *key_file)
{
crt_file_ = crt_file;
key_file_ = key_file;
conf_ = new polarssl_conf;
}
else
conf_ = NULL;
verify_mode_ = verify_mode;
}
~master_service()
{
if (conf_)
delete conf_;
}
protected:
virtual void on_accept(socket_stream* stream)
{
stream->set_rw_timeout(60);
if (conf_)
{
// 对于使用 SSL 方式的流对象,需要将 SSL IO 流对象注册至网络
// 连接流对象中,即用 ssl io 替换 stream 中默认的底层 IO 过程
logger("begin setup ssl hook...");
polarssl_io* ssl = new polarssl_io(*conf_, true);
if (stream->setup_hook(ssl) == ssl)
{
logger_error("setup_hook error!");
ssl->destroy();
}
else
logger("setup ssl hook ok");
}
do_run(stream);
}
virtual void proc_on_init()
{
if (conf_ == NULL)
return;
// 允许服务端的 SSL 会话缓存功能
conf_->enable_cache(true);
// 添加本地服务的证书
if (conf_->add_cert(crt_file_.c_str()) == false)
{
logger_error("add cert failed, crt: %s, key: %s",
crt_file_.c_str(), key_file_.c_str());
delete conf_;
conf_ = NULL;
return;
}
logger("load cert ok, crt: %s, key: %s",
crt_file_.c_str(), key_file_.c_str());
// 添加本地服务密钥
if (conf_->set_key(key_file_.c_str()) == false)
{
logger_error("set private key error");
delete conf_;
conf_ = NULL;
}
conf_->set_authmode(verify_mode_);
}
private:
polarssl_conf* conf_;
string crt_file_;
string key_file_;
acl::polarssl_verify_t verify_mode_;
};
// WEB 服务模式
static void do_alone(const char* crt_file, const char* key_file,
acl::polarssl_verify_t verify_mode)
{
master_service service(crt_file, key_file, verify_mode);
acl::log::stdout_open(true);
const char* addr = "0.0.0.0:2443";
printf("listen: %s ...\r\n", addr);
service.run_alone(addr, NULL, 0); // 单独运行方式
}
// WEB CGI 模式
static void do_cgi(void)
{
do_run(NULL);
}
//////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
#ifdef WIN32
acl::acl_cpp_init();
#endif
acl::log::stdout_open(true);
// 开始运行
if (argc >= 2 && strcmp(argv[1], "alone") == 0)
{
acl::polarssl_verify_t verify_mode;
const char* crt_file, *key_file;
if (argc >= 3)
crt_file = argv[2];
else
crt_file = NULL;
if (argc >= 4)
key_file = argv[3];
else
key_file = NULL;
if (argc >= 5)
{
if (strcasecmp(argv[4], "req") == 0)
verify_mode = acl::POLARSSL_VERIFY_REQ;
else if (strcasecmp(argv[4], "opt") == 0)
verify_mode = acl::POLARSSL_VERIFY_OPT;
else
verify_mode = acl::POLARSSL_VERIFY_NONE;
}
else
verify_mode = acl::POLARSSL_VERIFY_NONE;
do_alone(crt_file, key_file, verify_mode);
}
else if (argc >= 2)
printf("usage: %s alone cert_file key_file verify[none|req|opt]\r\n", argv[0]);
else
do_cgi();
return 0;
}