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

推荐订阅源

S
SegmentFault 最新的问题
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
B
Blog RSS Feed
Y
Y Combinator Blog
T
Tailwind CSS Blog
博客园 - 三生石上(FineUI控件)
J
Java Code Geeks
Stack Overflow Blog
Stack Overflow Blog
aimingoo的专栏
aimingoo的专栏
Jina AI
Jina AI
The GitHub Blog
The GitHub Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
A
About on SuperTechFans
H
Hackread – Cybersecurity News, Data Breaches, AI and More
D
Docker
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Check Point Blog
M
MIT News - Artificial intelligence
Last Week in AI
Last Week in AI
V
V2EX
腾讯CDC
F
Fortinet All Blogs
博客园 - 叶小钗
T
The Blog of Author Tim Ferriss

博客园 - 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);
}