惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
F
Fortinet All Blogs
博客园 - 聂微东
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
V
Visual Studio Blog
小众软件
小众软件
有赞技术团队
有赞技术团队
雷峰网
雷峰网
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
AWS News Blog
AWS News Blog
C
Cisco Blogs
美团技术团队
T
Threat Research - Cisco Blogs
C
CERT Recently Published Vulnerability Notes
人人都是产品经理
人人都是产品经理
宝玉的分享
宝玉的分享
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
W
WeLiveSecurity
D
DataBreaches.Net
博客园 - 司徒正美
Blog — PlanetScale
Blog — PlanetScale
IT之家
IT之家
云风的 BLOG
云风的 BLOG
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Simon Willison's Weblog
Simon Willison's Weblog
Google DeepMind News
Google DeepMind News
T
The Blog of Author Tim Ferriss
Know Your Adversary
Know Your Adversary
NISL@THU
NISL@THU
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
Vercel News
Vercel News
月光博客
月光博客
T
Tailwind CSS Blog
H
Help Net Security
aimingoo的专栏
aimingoo的专栏
P
Proofpoint News Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Spread Privacy
Spread Privacy
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
Microsoft Security Blog
Microsoft Security Blog
V
V2EX
WordPress大学
WordPress大学
Cyberwarzone
Cyberwarzone
Recent Announcements
Recent Announcements

博客园 - osbreak

ros2::tf2 QML::qml与c++数据交互 k8s:: Service 管理 deployment postgresql 索引 postgresql 基本类型 postgresql 基础运维 PLSQL 触发器 PLSQL 程序包 PLSQL 执行块 PLSQL 基础数据类型 PLSQL oracle安装部署 mysql事务与隔离 mysql慢查询分析 mysql建增删改查 mysql常用总结 (9)libevent 常用设置 (7)libevent filter(过滤器) (6)libevent定时器 (5)libevent evbuffer
(8)libevent 构建libevent http服务,支持文件下载
osbreak · 2023-11-12 · via 博客园 - osbreak

一、构建libevent http服务,支持文件下载


#include <event2/event.h>
#include <event2/listener.h>
#include <event2/http.h>
#include <event2/keyvalq_struct.h>
#include <event2/buffer.h>
#include <string.h>
#ifndef _WIN32
#include <signal.h>
#endif
#include <iostream>
#include <string>
using namespace std;
#define WEBROOT "." 
#define DEFAULTINDEX "index.html"

void http_cb(struct evhttp_request *request, void *arg)
{
	cout << "http_cb" << endl;
	//1 获取请求信息
	//uri 
	const char *uri = evhttp_request_get_uri(request);
	cout << "uri:" << uri << endl;

	//请求类型 GET POST
	string cmdtype;
	switch (evhttp_request_get_command(request))
	{
	case EVHTTP_REQ_GET:
		cmdtype = "GET";
		break;
	case EVHTTP_REQ_POST:
		cmdtype = "POST";
		break;
	}
	cout << cmdtype << endl;
	// 消息报头
	evkeyvalq *headers = evhttp_request_get_input_headers(request);
	cout << "====== headers ======" << endl;
	for (evkeyval *p = headers->tqh_first; p != NULL; p = p->next.tqe_next)
	{
		cout << p->key << ":" << p->value << endl;
	}

	// 请求正文 ( GET为空,POST有表单信息  )
	evbuffer *inbuf = evhttp_request_get_input_buffer(request);
	char buf[1024] = { 0 };
	cout << "======= Input data ======" << endl;
	while (evbuffer_get_length(inbuf))
	{
		int n = evbuffer_remove(inbuf, buf, sizeof(buf) - 1);
		if (n > 0)
		{
			buf[n] = '\0';
			cout << buf << endl;
		}
	}

	string filepath = WEBROOT;
	filepath += uri;
	if (strcmp(uri, "/") == 0)
	{
		//默认加入首页文件
		filepath += DEFAULTINDEX;
	}

	// 获取消息报头
	evkeyvalq *outhead = evhttp_request_get_output_headers(request);
	//  设置支持 图片 js css 下载zip文件
	int pos = filepath.rfind('.');
	string postfix = filepath.substr(pos + 1, filepath.size() - (pos + 1));
	if (postfix == "jpg" || postfix == "gif" || postfix == "png")
	{
		string tmp = "image/" + postfix;
		evhttp_add_header(outhead, "Content-Type", tmp.c_str());
	}
	else if (postfix == "zip")
	{
		evhttp_add_header(outhead, "Content-Type", "application/zip");
	}
	else if (postfix == "html")
	{
		evhttp_add_header(outhead, "Content-Type", "text/html;charset=UTF8");
	}
	else if (postfix == "css")
	{
		evhttp_add_header(outhead, "Content-Type", "text/css");
	}

	//读取文件返回正文
	FILE *fp = fopen(filepath.c_str(), "rb");
	if (!fp)
	{
		evhttp_send_reply(request, HTTP_NOTFOUND, "", 0);
		return;
	}
	evbuffer *outbuf = evhttp_request_get_output_buffer(request);
	for (;;)
	{
		int len = fread(buf, 1, sizeof(buf), fp);
		if (len <= 0)break;
		evbuffer_add(outbuf, buf, len);
	}
	fclose(fp);
	evhttp_send_reply(request, HTTP_OK, "", outbuf);
}

int main()
{
#ifdef _WIN32 
	//初始化socket库
	WSADATA wsa;
	WSAStartup(MAKEWORD(2, 2), &wsa);
#else
	//忽略管道信号,发送数据给已关闭的socket
	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
		return 1;
#endif

	//创建libevent的上下文
	event_base * base = event_base_new();
	if (!base)
		return 1;

	// http  服务器
	//1	创建evhttp上下文
	evhttp *evh = evhttp_new(base);

	//2  绑定端口和IP
	if (evhttp_bind_socket(evh, "0.0.0.0", 8080) != 0)
	{
		cout << "evhttp_bind_socket failed!" << endl;
	}

	//3   设定回调函数
	evhttp_set_gencb(evh, http_cb, 0);

	//4   事件分发处理
	if (base)
		event_base_dispatch(base);
	if (base)
		event_base_free(base);
	if (evh)
		evhttp_free(evh);
#ifdef _WIN32
	WSACleanup();
#endif
	return 0;
}

一、构建libevent client,解析下载文件

/**
* 回调
*/
void http_client_cb(struct evhttp_request *req, void *ctx)
{
	cout << "http_client_cb" << endl;
	event_base *base = (event_base *)ctx;
	//服务端响应错误
	if (req == NULL)
	{
		int errcode = EVUTIL_SOCKET_ERROR();
		cout << "socket error:" << evutil_socket_error_to_string(errcode) << endl;
		return;
	}

	//获取path 
	const char *path = evhttp_request_get_uri(req);
	cout << "request path is " << path << endl;
	string filepath = ".";
	filepath += path;
	cout << "filepath is " << filepath << endl;


	//下载文件,如果路径中有目录,需要分析出目录,并创建
	FILE *fp = fopen(filepath.c_str(), "wb");
	if (!fp)
	{
		cout << "open file " << filepath << " failed!" << endl;
	}

	//获取返回的code 200 404
	cout << "Response :" << evhttp_request_get_response_code(req); //200
	cout << " " << evhttp_request_get_response_code_line(req) << endl;//OK
	char buf[1024] = { 0 };
	evbuffer *input = evhttp_request_get_input_buffer(req);
	for (;;)
	{
		int len = evbuffer_remove(input, buf, sizeof(buf) - 1);
		if (len <= 0)break;
		buf[len] = 0;
		if (!fp)
			continue;
		fwrite(buf, 1, len, fp);
	}
	if (fp)
		fclose(fp);

	event_base_loopbreak(base);
}


int PostTest()
{
	// 创建libevent的上下文
	event_base * base = event_base_new();
	if (base)
	{
		cout << "event_base_new success!" << endl;
	}
	//   生成请求信息 POST
	string http_url = "http://127.0.0.1:8080/index.html";

	// 分析url地址
	// uri
	evhttp_uri *uri = evhttp_uri_parse(http_url.c_str());
	// http OR https
	const char *scheme = evhttp_uri_get_scheme(uri);
	if (!scheme)
	{
		cerr << "scheme is null" << endl;
		return -1;
	}
	cout << "scheme is " << scheme << endl;

	int port = evhttp_uri_get_port(uri);
	if (port < 0)
	{
		if (strcmp(scheme, "http") == 0)
			port = 80;
	}
	cout << "port is " << port << endl;

	const char *host = evhttp_uri_get_host(uri);
	if (!host)
	{
		cerr << "host is null" << endl;
		return -1;
	}
	cout << "host is " << host << endl;
	
	const char *path = evhttp_uri_get_path(uri);
	if (!path || strlen(path) == 0)
	{
		path = "/";
	}
	if (path)
		cout << "path is " << path << endl;

	//?id=1  后面的内容 id=1
	const char *query = evhttp_uri_get_query(uri);
	if (query)
		cout << "query is " << query << endl;
	else
		cout << "query is NULL" << endl;

	// bufferevent  连接http服务器
	bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
	evhttp_connection *evcon = evhttp_connection_base_bufferevent_new(base,
		NULL, bev, host, port);

	//http client  请求 回调函数设置
	evhttp_request *req = evhttp_request_new(http_client_cb, base);

	// 设置请求的head 消息报头 信息
	evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
	evhttp_add_header(output_headers, "Host", host);

	//发送post数据
	evbuffer *output = evhttp_request_get_output_buffer(req);
	evbuffer_add_printf(output, "xcj=%d&b=%d", 1, 2);

	//发起请求
	evhttp_make_request(evcon, req, EVHTTP_REQ_POST, path);

	//事件分发处理
	if (base) event_base_dispatch(base);
	if (uri) evhttp_uri_free(uri);
	if (evcon) evhttp_connection_free(evcon);
	if (base) event_base_free(base);
}

int GetTest()
{
	//创建libevent的上下文
	event_base * base = event_base_new();

	//   生成请求信息 GET
	string http_url = "http://127.0.0.1/index.html?id=1";

	// 分析url地址
	// uri
	evhttp_uri *uri = evhttp_uri_parse(http_url.c_str());
	// http https
	const char *scheme = evhttp_uri_get_scheme(uri);
	if (!scheme)
	{
		cerr << "scheme is null" << endl;
		return -1;
	}
	cout << "scheme is " << scheme << endl;

	int port = evhttp_uri_get_port(uri);
	if (port < 0)
	{
		if (strcmp(scheme, "http") == 0)
			port = 80;
	}
	cout << "port is " << port << endl;

	//host ffmpeg.club
	const char *host = evhttp_uri_get_host(uri);
	if (!host)
	{
		cerr << "host is null" << endl;
		return -1;
	}
	cout << "host is " << host << endl;
	const char *path = evhttp_uri_get_path(uri);
	if (!path || strlen(path) == 0)
	{
		path = "/";
	}
	if (path)
		cout << "path is " << path << endl;

	//?id=1  后面的内容 id=1
	const char *query = evhttp_uri_get_query(uri);
	if (query)
		cout << "query is " << query << endl;
	else
		cout << "query is NULL" << endl;

	// bufferevent  连接http服务器
	bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
	evhttp_connection *evcon = evhttp_connection_base_bufferevent_new(base,
		NULL, bev, host, port);

	//http client  请求 回调函数设置
	evhttp_request *req = evhttp_request_new(http_client_cb, base);

	// 设置请求的head 消息报头 信息
	evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
	evhttp_add_header(output_headers, "Host", host);

	//发起请求
	evhttp_make_request(evcon, req, EVHTTP_REQ_GET, path);

	//事件分发处理
	if (base)event_base_dispatch(base);
	if (uri)evhttp_uri_free(uri);
	if (evcon)evhttp_connection_free(evcon);
	if (base)event_base_free(base);
}