(8)libevent 构建libevent http服务,支持文件下载

一、构建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);
}
posted @ 2023-11-12 18:06  osbreak  阅读(54)  评论(0编辑  收藏  举报