add httpd_proxy sample.

This commit is contained in:
zsx 2019-03-14 15:24:11 +08:00
parent c0437bb1de
commit 2f3038c1a4
13 changed files with 775 additions and 0 deletions

View File

@ -0,0 +1,2 @@
include ./Makefile.in
PROG = httpd_proxy

View File

@ -0,0 +1,148 @@
CC = g++
CFLAGS = -c -g -W \
-Wall \
-Wcast-qual \
-Wcast-align \
-Wno-long-long \
-Wpointer-arith \
-Werror \
-Wshadow \
-O3 \
-D_REENTRANT \
-D_POSIX_PTHREAD_SEMANTICS \
-D_USE_FAST_MACRO
###########################################################
#Check system:
# Linux, SunOS, Solaris, BSD variants, AIX, HP-UX
SYSLIB = -lpthread -lz -ldl
CHECKSYSRES = @echo "Unknow system type!";exit 1
UNIXNAME = $(shell uname -s)
OSTYPE = $(shell uname -m)
RPATH = linux64
ifeq ($(CC),)
CC = gcc
endif
# For FreeBSD
ifeq ($(findstring FreeBSD, $(UNIXNAME)), FreeBSD)
ifeq ($(findstring gcc, $(CC)), gcc)
CFLAGS += -Wstrict-prototypes
endif
CFLAGS += -DFREEBSD -D_REENTRANT
SYSLIB = -lcrypt -lpthread -lz
RPATH = freebsd
endif
# For Darwin
ifeq ($(findstring Darwin, $(UNIXNAME)), Darwin)
CFLAGS += -DMACOSX -Wno-invalid-source-encoding \
-Wno-extended-offsetof
UNIXTYPE = MACOSX
SYSLIB += -liconv -rdynamic
RPATH = macos
endif
#Path for Linux
ifeq ($(findstring Linux, $(UNIXNAME)), Linux)
ifeq ($CC, "gcc")
CFLAGS += -Wstrict-prototypes
endif
ifeq ($(findstring i686, $(OSTYPE)), i686)
RPATH = linux32
endif
ifeq ($(findstring x86_64, $(OSTYPE)), x86_64)
RPATH = linux64
endif
CFLAGS += -DLINUX2 -D_REENTRANT
SYSLIB += -lcrypt
endif
# For MINGW
ifeq ($(findstring MINGW, $(UNIXNAME)), MINGW)
SYSLIB = -lpthread-2 -liconv
CFLAGS += -DLINUX2 -DMINGW
UNIXTYPE = LINUX
endif
# For MSYS
ifeq ($(findstring MSYS, $(UNIXNAME)), MSYS)
SYSLIB = -lpthread-2 -liconv
CFLAGS += -DLINUX2 -DMINGW
UNIXTYPE = LINUX
endif
#Path for SunOS
ifeq ($(findstring SunOS, $(UNIXNAME)), SunOS)
ifeq ($(findstring 86, $(UNIXNAME)), 86)
SYSLIB += -lsocket -lnsl -lrt
endif
ifeq ($(findstring sun4u, $(UNIXNAME)), sun4u)
SYSLIB += -lsocket -lnsl -lrt
endif
ifeq ($CC, "gcc")
CFLAGS += -Wstrict-prototypes
endif
CFLAGS += -DSUNOS5 -D_REENTRANT
RPATH = sunos_x86
endif
#Path for HP-UX
ifeq ($(findstring HP-UX, $(UNIXNAME)), HP-UX)
ifeq ($CC, "gcc")
CFLAGS += -Wstrict-prototypes
endif
CFLAGS += -DHP_UX -DHPUX11
PLAT_NAME=hp-ux
endif
#Find system type.
ifneq ($(SYSPATH),)
CHECKSYSRES = @echo "System is $(shell uname -sm)"
endif
###########################################################
ACL_PATH = ../../..
CFLAGS += -I$(ACL_PATH)/lib_acl/include \
-I$(ACL_PATH)/lib_protocol/include \
-I$(ACL_PATH)/lib_acl_cpp/include \
-I$(ACL_PATH)/lib_fiber/c/include \
-I$(ACL_PATH)/lib_fiber/cpp/include
EXTLIBS =
LDFLAGS = -L$(ACL_PATH)/lib_fiber/lib -lfiber_cpp \
-L$(ACL_PATH)/lib_acl_cpp/lib -lacl_cpp \
-L$(ACL_PATH)/lib_protocol/lib -lprotocol \
-L$(ACL_PATH)/lib_acl/lib -lacl -lfiber \
$(EXTLIBS) $(SYSLIB)
COMPILE = $(CC) $(CFLAGS)
LINK = $(CC) $(OBJ) $(LDFLAGS)
###########################################################
OBJ_PATH = .
#Project's objs
SRC = $(wildcard *.cpp)
OBJ = $(patsubst %.cpp, $(OBJ_PATH)/%.o, $(notdir $(SRC)))
$(shell mkdir -p ../../../dist/master/libexec/$(RPATH)/)
$(OBJ_PATH)/%.o: %.cpp
$(COMPILE) $< -o $@
.PHONY = all clean
all: RM $(OBJ)
$(LINK) -o $(PROG)
@echo ""
@echo "All ok! Output:$(PROG)"
@echo ""
RM:
rm -f $(PROG)
clean:
rm -f $(PROG)
rm -f $(OBJ)
install:
cp $(PROG) ../../../dist/master/libexec/$(RPATH)/
cp $(PROG).cf ../../../dist/master/conf/service/
###########################################################

View File

@ -0,0 +1,42 @@
#include "stdafx.h"
#include "fiber_transfer.h"
fiber_transfer::fiber_transfer(acl::socket_stream& in, acl::socket_stream& out)
: in_(in), out_(out), peer_(NULL)
{
}
void fiber_transfer::set_peer(fiber_transfer& peer)
{
peer_ = &peer;
}
fiber_transfer::~fiber_transfer(void)
{
}
void fiber_transfer::wait(void)
{
(void) box_.pop();
}
void fiber_transfer::run(void)
{
char buf[8192];
while (true) {
int ret = in_.read(buf, sizeof(buf) - 1, false);
if (ret == -1) {
break;
}
if (out_.write(buf, ret) == -1) {
break;
}
}
if (peer_) {
peer_->kill();
}
box_.push(NULL);
}

View File

@ -0,0 +1,32 @@
#pragma once
class fiber_transfer : public acl::fiber
{
public:
fiber_transfer(acl::socket_stream& in, acl::socket_stream& out);
~fiber_transfer(void);
void set_peer(fiber_transfer& peer);
void wait(void);
acl::socket_stream& get_input(void) const
{
return in_;
}
acl::socket_stream& get_output(void) const
{
return out_;
}
protected:
// @override
void run(void);
private:
acl::fiber_tbox<int> box_;
acl::socket_stream& in_;
acl::socket_stream& out_;
fiber_transfer* peer_;
};

View File

@ -0,0 +1,163 @@
#include "stdafx.h"
#include "fiber_transfer.h"
#include "http_servlet.h"
http_servlet::http_servlet(acl::socket_stream* stream, acl::session* session)
: acl::HttpServlet(stream, session)
{
handlers_["/hello"] = &http_servlet::on_hello;
}
http_servlet::~http_servlet(void)
{
}
bool http_servlet::doError(request_t&, response_t& res)
{
res.setStatus(400);
res.setContentType("text/xml; charset=utf-8");
// 发送 http 响应体
acl::string buf;
buf.format("<root error='some error happened!' />\r\n");
res.write(buf);
res.write(NULL, 0);
return false;
}
bool http_servlet::doOther(request_t&, response_t& res, const char* method)
{
res.setStatus(400);
res.setContentType("text/xml; charset=utf-8");
// 发送 http 响应体
acl::string buf;
buf.format("<root error='unkown request method %s' />\r\n", method);
res.write(buf);
res.write(NULL, 0);
return false;
}
bool http_servlet::doGet(request_t& req, response_t& res)
{
return doPost(req, res);
}
bool http_servlet::doPost(request_t& req, response_t& res)
{
// 如果需要 http session 控制,请打开下面注释,且需要保证
// 在 master_service.cpp 的函数 thread_on_read 中设置的
// memcached 服务正常工作
/*
const char* sid = req.getSession().getAttribute("sid");
if (*sid == 0)
req.getSession().setAttribute("sid", "xxxxxx");
sid = req.getSession().getAttribute("sid");
*/
// 如果需要取得浏览器 cookie 请打开下面注释
/*
*/
const char* path = req.getPathInfo();
handler_t handler = path && *path ? handlers_[path] : NULL;
return handler ? (this->*handler)(req, res) : on_default(req, res);
}
bool http_servlet::on_default(request_t& req, response_t& res)
{
return on_hello(req, res);
}
bool http_servlet::on_hello(request_t& req, response_t& res)
{
res.setContentType("text/html; charset=utf-8") // 设置响应字符集
.setKeepAlive(req.isKeepAlive()) // 设置是否保持长连接
.setContentEncoding(true) // 自动支持压缩传输
.setChunkedTransferEncoding(true); // 采用 chunk 传输方式
acl::string buf;
buf.format("<html><body>xxxxxxx<br>\r\n");
if (res.write(buf) == false) {
printf("write error\r\n");
return false;
}
acl::json* json = req.getJson();
if (json == NULL) {
printf("json null\r\n");
} else {
printf("json is [%s]\r\n", json->to_string().c_str());
}
for (size_t i = 0; i < 1; i++)
{
buf.format("hello world=%d<br>\r\n", (int) i);
if (res.write(buf) == false) {
printf("write error\r\n");
return false;
}
if (i % 10000 == 0)
{
sleep(1);
printf("i=%d\n", (int) i);
}
}
buf = "</body></html><br>\r\n";
printf("write ok\n");
return res.write(buf) && res.write(NULL, 0);
}
bool http_servlet::doConnect(request_t& req, response_t& res)
{
// CONNECT 127.0.0.1:22 HTTP/1.0
// HTTP/1.1 200 Connection Established
const char* host = req.getRemoteHost();
if (host == NULL || *host == 0) {
printf("getRemoteHost null\r\n");
return false;
}
printf("remote host=%s\r\n", host);
acl::socket_stream peer;
if (peer.open(host, 0, 0) == false) {
printf("connect %s error %s\r\n", host, acl::last_serror());
return false;
}
//const char* ok = "HTTP/1.1 200 Connection Established\r\n";
//acl::ostream& out = res.getOutputStream();
const char* ok = "";
res.setContentLength(0);
if (res.write(ok) == false) {
return false;
}
acl::socket_stream& local = req.getSocketStream();
doProxy(local, peer);
return false;
}
bool http_servlet::doProxy(acl::socket_stream& local, acl::socket_stream& peer)
{
fiber_transfer fiber_local(local, peer);
fiber_transfer fiber_peer(peer, local);
fiber_local.set_peer(fiber_peer);
fiber_peer.set_peer(fiber_local);
fiber_local.start();
fiber_peer.start();
fiber_local.wait();
fiber_peer.wait();
printf("doProxy finished, local fd=%d, peer fd=%d\r\n",
fiber_local.get_input().sock_handle(),
fiber_local.get_output().sock_handle());
return true;
}

View File

@ -0,0 +1,32 @@
#pragma once
class http_servlet : public acl::HttpServlet
{
public:
http_servlet(acl::socket_stream*, acl::session*);
~http_servlet();
protected:
// @override
bool doGet(request_t&, response_t&);
// @override
bool doPost(request_t&, response_t&);
// @override
bool doError(request_t&, response_t&);
// @override
bool doOther(request_t&, response_t&, const char* method);
bool doConnect(request_t&, response_t&);
private:
typedef bool (http_servlet::*handler_t)(request_t&,response_t&);
std::map<std::string, handler_t> handlers_;
bool on_default(request_t&, response_t&);
bool on_hello(request_t&, response_t&);
bool doProxy(acl::socket_stream& local, acl::socket_stream& peer);
};

View File

@ -0,0 +1,130 @@
service httpd_proxy
{
# 进程是否禁止运行
master_disable = no
# 服务地址及端口号
# for master_type = inet
# master_service = 127.0.0.1:5001
# for master_type = unix
# master_service = echo.sock
# for master_type = sock
master_service = 8288
# 服务监听为域套接口
# master_service = aio_echo.sock
# 服务类型
# master_type = inet
# master_type = unix
master_type = sock
# 停止子进程时是否采用强行停止的方式(即给子进程发送 SIGTERM 信号)
master_stop_kill = false
# master_stop_kill true 该配置决定是否要等待子进程退出
master_stop_wait = false
# 当系统支持 SO_REUSEPORT 是否启用该功能
master_reuseport = yes
# 是否针对监听套接口设定为非阻塞方式
master_nonblock = yes
# 当系统支持 TCP_FASTOPEN 是否启用该功能
master_fastopen = no
# 当子进程异常退出时如果该值非空则将子进程异常退出的消息通知该服务
# master_notify_addr = 127.0.0.1:5801
# 邮件通知接收者
# master_notify_recipients = zhengshuxin@hotmail.com
# 是否允许延迟接受客户端连接如果为0则表示关闭该功能如果大于0则表示打开此功能
# 并且此值代表延迟接受连接的超时值超过此值时如果客户端依然没有发来数据则操作
# 系统会在系统层直接关闭该连接
# master_defer_accept = 0
# 是否只允许私有访问, 如果为 y, 则域套接口创建在 {install_path}/var/log/private/ 目录下,
# 如果为 n, 则域套接口创建在 {install_path}/var/log/public/ 目录下,
master_private = n
master_unpriv = n
# 是否需要 chroot: n -- no, y -- yes
master_chroot = n
# 每隔多长时间触发一次单位为秒(仅对 trigger 模式有效)
master_wakeup = -
# 最大进程数
master_maxproc = 1
# 预启动进程数该值不得大于 master_maxproc
master_prefork = 1
# 进程程序名
master_command = httpd_proxy
# 进程日志记录文件
master_log = {install_path}/var/log/httpd_proxy
# 调试日志方式格式tag:level; tag:level; tab:level, all:1; 101:2
# master_debug =
# 进程启动参数只能为: -u [是否允许以某普通用户的身份运行]
# master_args =
# 传递给服务子进程的环境变量, 可以通过 getenv("SERVICE_ENV") 获得此值
# master_env = mempool_limit:512000000
# 当启动多个子进程实例时该开关控制多个子进程在接收连接时是否向 acl_master 发送消息报告自己的状态
# master_status_notify = 1
# 是否允许产生 core 文件
# fiber_enable_core = 1
# core 文件大小限制-1 表示不限制 core 文件大小0 表示禁止产生 core> 0 表示 core 文件最大大小
# fiber_core_limit = -1
# 进程退出时是否禁止产生 core 文件
# fiber_disable_core_onexit = 1
# 每个进程实例处理连接数的最大次数超过此值后进程实例主动退出
fiber_use_limit = 0
# 每个进程实例的空闲超时时间超过此值后进程实例主动退出
fiber_idle_limit = 0
# 每个进程启动的线程数
fiber_threads = 1
# 进程运行时所在的路径
fiber_queue_dir = {install_path}/var
# 读写超时时间, 单位为秒
fiber_rw_timeout = 120
# 读缓冲区的缓冲区大小
fiber_buf_size = 8192
# 进程运行时的用户身份
fiber_owner = root
# 当启用 master_dispatch 连接分开服务后该配置指定 master_dispatch 所监听的
# 域套接口的全路径这样本子进程就可以从 master_dispatch 获得客户端连接
# fiber_dispatch_addr = {install_path}/var/private/dispatch.sock
# fiber_dispatch_addr 开启后下面参数控制本服务进程发给前端 master_dispatch 的服务标识信息
# fiber_dispatch_type = default
# 线程的堆栈空间大小单位为字节
fiber_stack_size = 64000
# 允许访问 udserver 的客户端IP地址范围
# fiber_access_allow = 127.0.0.1:255.255.255.255, 127.0.0.1:127.0.0.1
fiber_access_allow = all
# acl_master 退出时如果该值置1则该程序不等所有连接处理完毕便立即退出
fiber_quick_abort = 1
# fiber_quick_abort 0 且本配置项大于 0 该配置项才有效指定了
# 本进程在所有连接退出前的最大等待时间()
fiber_wait_limit = 0
############################################################################
# 应用自己的配置选项
# mysql 服务地址
# mysql_dbaddr = /tmp/mysql.sock
# mysql_dbaddr = 10.0.250.199:3306
# 连接 mysql 数据库的连接池的最大值
# mysql_dbmax = 200
# ping mysql 连接的间隔时间, 以秒为单位
# mysql_dbping = 10
# mysql 连接空闲的时间间隔, 以秒为单位
# mysql_dbtimeout = 30
# 数据库名称
# mysql_dbname = fiber_db
# 数据库访问用户
# mysql_dbuser = fiber_user
# 数据库用户访问密码
# mysql_dbpass = 111111
# 是否输出当前内存的状态信息
# debug_mem = 1
# 是否在一个线程中连接读
# loop_read = 1
}

View File

@ -0,0 +1,31 @@
#include "stdafx.h"
#include "master_service.h"
int main(int argc, char *argv[])
{
acl::acl_cpp_init();
master_service& ms = acl::singleton2<master_service>::get_instance();
// 设置配置参数表
ms.set_cfg_int(var_conf_int_tab);
ms.set_cfg_int64(var_conf_int64_tab);
ms.set_cfg_str(var_conf_str_tab);
ms.set_cfg_bool(var_conf_bool_tab);
if (argc >= 2 && strcasecmp(argv[1], "alone") == 0)
{
const char* addr = ":8288";
acl::log::stdout_open(true);
if (argc >= 4)
addr = argv[3];
printf("listen: %s\r\n", addr);
ms.run_alone(addr, argc >= 3 ? argv[2] : NULL);
}
else
ms.run_daemon(argc, argv);
return 0;
}

View File

@ -0,0 +1,85 @@
#include "stdafx.h"
#include "http_servlet.h"
#include "master_service.h"
static char *var_cfg_str;
acl::master_str_tbl var_conf_str_tab[] = {
{ "str", "test_msg", &var_cfg_str },
{ 0, 0, 0 }
};
static int var_cfg_debug_enable;
acl::master_bool_tbl var_conf_bool_tab[] = {
{ "debug_enable", 1, &var_cfg_debug_enable },
{ 0, 0, 0 }
};
static int var_cfg_io_timeout;
acl::master_int_tbl var_conf_int_tab[] = {
{ "io_timeout", 120, &var_cfg_io_timeout, 0, 0 },
{ 0, 0 , 0 , 0, 0 }
};
acl::master_int64_tbl var_conf_int64_tab[] = {
{ 0, 0 , 0 , 0, 0 }
};
//////////////////////////////////////////////////////////////////////////
master_service::master_service(void)
{
}
master_service::~master_service(void)
{
}
void master_service::on_accept(acl::socket_stream& conn)
{
logger("connect from %s, fd %d", conn.get_peer(), conn.sock_handle());
conn.set_rw_timeout(120);
acl::memcache_session session("127.0.0.1:11211");
http_servlet servlet(&conn, &session);
// charset: big5, gb2312, gb18030, gbk, utf-8
servlet.setLocalCharset("utf-8");
while(servlet.doRun()) {}
logger("disconnect from %s", conn.get_peer());
}
void master_service::proc_pre_jail(void)
{
logger(">>>proc_pre_jail<<<");
}
void master_service::proc_on_listen(acl::server_socket& ss)
{
logger(">>>listen %s ok<<<", ss.get_addr());
}
void master_service::proc_on_init(void)
{
logger(">>>proc_on_init<<<");
}
void master_service::proc_on_exit(void)
{
logger(">>>proc_on_exit<<<");
}
bool master_service::proc_on_sighup(acl::string&)
{
logger(">>>proc_on_sighup<<<");
return true;
}

View File

@ -0,0 +1,36 @@
#pragma once
extern acl::master_str_tbl var_conf_str_tab[];
extern acl::master_bool_tbl var_conf_bool_tab[];
extern acl::master_int_tbl var_conf_int_tab[];
extern acl::master_int64_tbl var_conf_int64_tab[];
//////////////////////////////////////////////////////////////////////////
class master_service : public acl::master_fiber
{
public:
master_service(void);
~master_service(void);
protected:
// @override
void on_accept(acl::socket_stream& conn);
// @override
void proc_pre_jail(void);
// @override
void proc_on_listen(acl::server_socket& ss);
// @override
void proc_on_init(void);
// @override
void proc_on_exit(void);
// @override
bool proc_on_sighup(acl::string&);
private:
};

View File

@ -0,0 +1,8 @@
// stdafx.cpp : 只包括标准包含文件的源文件
// master_threads.pch 将成为预编译头
// stdafx.obj 将包含预编译类型信息
#include "stdafx.h"
// TODO: 在 STDAFX.H 中
//引用任何所需的附加头文件,而不是在此文件中引用

View File

@ -0,0 +1,63 @@
// stdafx.h : 标准系统包含文件的包含文件,
// 或是常用但不常更改的项目特定的包含文件
//
#pragma once
//#include <iostream>
//#include <tchar.h>
// TODO: 在此处引用程序要求的附加头文件
#include "lib_acl.h"
#include "lib_protocol.h"
#include "acl_cpp/lib_acl.hpp"
#include "fiber/lib_fiber.h"
#include "fiber/lib_fiber.hpp"
#ifdef WIN32
#define snprintf _snprintf
#endif
typedef acl::HttpServletRequest request_t;
typedef acl::HttpServletResponse response_t;
#undef logger
#undef logger_warn
#undef logger_error
#undef logger_fatal
#undef logger_debug
#if defined(_WIN32) || defined(_WIN64)
# if _MSC_VER >= 1500
# define logger(fmt, ...) \
acl::log::msg4(__FILE__, __LINE__, __FUNCTION__, fmt, __VA_ARGS__)
# define logger_warn(fmt, ...) \
acl::log::warn4(__FILE__, __LINE__, __FUNCTION__, fmt, __VA_ARGS__)
# define logger_error(fmt, ...) \
acl::log::error4(__FILE__, __LINE__, __FUNCTION__, fmt, __VA_ARGS__)
# define logger_fatal(fmt, ...) \
acl::log::fatal4(__FILE__, __LINE__, __FUNCTION__, fmt, __VA_ARGS__)
# define logger_debug(section, level, fmt, ...) \
acl::log::msg6(section, level, __FILE__, __LINE__, __FUNCTION__, fmt, __VA_ARGS__)
# else
# define logger acl::log::msg1
# define logger_warn acl::log::warn1
# define logger_error acl::log::error1
# define logger_fatal acl::log::fatal1
# define logger_debug acl::log::msg3
# endif
#else
# define logger(fmt, args...) \
acl::log::msg4(__FILE__, __LINE__, __FUNCTION__, fmt, ##args)
# define logger_warn(fmt, args...) \
acl::log::warn4(__FILE__, __LINE__, __FUNCTION__, fmt, ##args)
# define logger_error(fmt, args...) \
acl::log::error4(__FILE__, __LINE__, __FUNCTION__, fmt, ##args)
# define logger_fatal(fmt, args...) \
acl::log::fatal4(__FILE__, __LINE__, __FUNCTION__, fmt, ##args)
# define logger_debug(section, level, fmt, args...) \
acl::log::msg6(section, level, __FILE__, __LINE__, __FUNCTION__, fmt, ##args)
#endif // !_WIN32 && !_WIN64

View File

@ -0,0 +1,3 @@
#!/bin/sh
valgrind --tool=memcheck --leak-check=yes -v ./httpd_proxy alone httpd_proxy.cf