使用libevent实现的HTTP客户端和服务端

现在越来越多的应用里需要使用http的请求与服务,对于C++而言,也已经有了很多第三方库对此进行支持。在诸多的第三方库里,异步实现也成了主流,主要原因是同步阻塞的模式在现在的大多数应用里是不适用的。在近来的自己参与的多个项目中,由于接入Http的时间不同,因而在使用过程中也选择了不同的第三方库。之前一次总结了Mongoose实现HttpServer,这次又是使用libevent。之所以选择封装,是为了避免在后续的工作中,避免重复使用生涩的第三方库,因为第三方库为了更加的完善,接口设计较多。对于每个要使用的人都要先了解其接口的话,使用成本就有点大了。因为之前也只是使用libevent实现http service, 但之前已经抛上来过http service的代码,感觉再次抛http service的代码感觉有些重复,但进来增加了http client的代码,所以就直接抛上来供大家吐槽吐槽。废话不多说,先将自己封装的代码奉上:

// File:        BasicHttp.hpp  
// Description: ---  
// Notes:       ---  
// Author:      Haust <wyy123_2008@qq.com>  
// Revision:    2015-09-19 by Haust  
#ifndef __BASIC_HTTP__
#define __BASIC_HTTP__

#include <event2/event.h>
#include <event2/http.h>
#include <string>
#include <map>

class HttpServer
{
public:
	HttpServer(void);
	virtual ~HttpServer(void);

	virtual bool Initialize(std::string host, unsigned short port);
	virtual bool Start();
	virtual void Shutdown();
	virtual void Update();

	typedef void (*http_handle_callback)(const char *uri, const unsigned char *data, int len);

	static bool RigisterCallback(const char *uri, http_handle_callback callback);

	static void HttpReply(const char *uri, const char *data, int len);

	typedef std::map<std::string, http_handle_callback> HTTP_CALLBACK_MAP;
	typedef HTTP_CALLBACK_MAP::iterator HTTP_CALLBACK_MAP_IT;

	typedef std::map<std::string, struct evhttp_request*> HTTP_REQUEST_MAP;
	typedef HTTP_REQUEST_MAP::iterator HTTP_REQUEST_MAP_IT;
	
protected:
	static void HttpHandle(struct evhttp_request* request, void* arg);
	
protected:
	struct event_base*				m_event_base;
	struct evhttp*					m_http;
	std::string					m_host;
	unsigned short					m_port;
	static HTTP_CALLBACK_MAP			m_callback_map;
	static HTTP_REQUEST_MAP				m_request_map;
};

class HttpClient
{
public:
	enum HttpRequestType
	{
		HTTP_REQUEST_GET  = EVHTTP_REQ_GET,
		HTTP_REQUEST_POST = EVHTTP_REQ_POST,
	};

	typedef void (*http_request_cb)(int response_code, const unsigned char *data, size_t len, void* arg);
	struct HttpRequest
	{
		std::string url;
		struct evhttp_uri*   uri;
		struct evhttp_connection *cn;
		struct evhttp_request *req;
		HttpRequestType     type;
		http_request_cb call_back;
		HttpClient* client;
		void* arg;
	};

public:
	HttpClient();
	virtual ~HttpClient();

	virtual bool Initialize();
	virtual void Update();

	bool HttpRequestUrl(const std::string& url, http_request_cb call_back, void* arg = NULL, const std::string& post_data = "", const std::string& content_type = "");

private:
	static void DefaultCallBack(int response_code, const unsigned char *data, size_t len, void* arg);
	static void HttpRequestCB(struct evhttp_request* req, void *arg);

	bool HttpRequestUrl(HttpRequest* request);

private:
	struct event_base*					m_event_base;
};


#endif // !__BASIC_HTTP__

实现文件如下:

// File:        BasicHttp.cpp  
// Description: ---  
// Notes:       ---  
// Author:      Haust <wyy123_2008@qq.com>  
// Revision:    2015-09-19 by Haust  

#include "BasicHttp.hpp"

#include <event2/thread.h>
#include <event2/keyvalq_struct.h>
#include <event2/http_struct.h>
#include <event2/buffer.h>

#include <string>
#include <map>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

HttpServer::HTTP_CALLBACK_MAP HttpServer::m_callback_map;
HttpServer::HTTP_REQUEST_MAP HttpServer::m_request_map;

HttpServer::HttpServer(void)
        :m_event_base(NULL)
	,m_http(NULL)
{
}

HttpServer::~HttpServer(void)
{
}

bool HttpServer::Initialize(string host, unsigned port)
{
	m_host = host;
	m_port = port;
	
	return true;
}

bool HttpServer::Start()
{
	evthread_use_pthreads();
	bool ret = false;
	int configflag = EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST;
	struct event_config* eventconfig = NULL;
	
	do 
	{
		eventconfig = event_config_new();
		if (!eventconfig)
		{
			printf("event_config_new() failed!!!\n");
			break;
		}
		
		event_config_set_flag(eventconfig, configflag);
		event_config_set_num_cpus_hint(eventconfig, 4);
		m_event_base = event_base_new_with_config(eventconfig);
		if (!m_event_base)
		{
			printf("event_base_new_with_config() failed!!!\n");
			break;
		}
		
		event_config_free(eventconfig);
		m_http = evhttp_new(m_event_base);
		if (!m_http)
		{
			printf("evhttp_new() failed!!!\n");
			break;
		}
		
		evhttp_set_gencb(m_http, HttpHandle, NULL);
		if (!evhttp_bind_socket_with_handle(m_http, m_host.c_str(), m_port))
		{
			printf("could not bind to %s:%d\n", m_host.c_str(), m_port);
			break;
		}
		
		ret = true;
	} while (!ret);
	
	if (!ret)
	{
		Shutdown();
	}
	
	return ret;
}

void HttpServer::Shutdown()
{
	if (m_event_base)
	{
		event_base_free(m_event_base);
		m_event_base = NULL;
	}
	
	if (m_http)
	{
		evhttp_free(m_http);
		m_http = NULL;
	}
}

void HttpServer::Update()
{
	if (m_event_base)
	{
		event_base_loopexit(m_event_base, NULL);
		event_base_dispatch(m_event_base);
	}
}

bool HttpServer::RigisterCallback(const char *uri, http_handle_callback callback)
{
	m_callback_map[uri] = callback;
	return true;
}

void HttpServer::HttpReply(const char *uri, const char* data, int len)
{
	HTTP_REQUEST_MAP_IT it = m_request_map.find(uri);
	if (m_request_map.end() != it)
	{
		struct evhttp_request* req = it->second;
		if (NULL != data)
		{
			struct evbuffer *databuf=evbuffer_new(); 
			evbuffer_add(databuf, data, len);
			evhttp_send_reply(req, HTTP_OK, "OK", databuf);
			evbuffer_free(databuf);
		}
		else
		{
			evhttp_send_reply(req, HTTP_OK, "OK", NULL);
		}
		
		if (1 == evhttp_request_is_owned(req))
		{
			evhttp_request_free(req);
		}
		m_request_map.erase(it);
	}
	
	printf("Http reply, uri:%s, %s\n", uri, data);
}

void HttpServer::HttpHandle(struct evhttp_request* request, void* arg)
{
	struct evbuffer* evinput = evhttp_request_get_input_buffer(request);
	int length = (int)evbuffer_get_length(evinput);
	unsigned char* data = new unsigned char[length];
	memset(data, 0, length);
	evbuffer_remove(evinput, data, length);
	
	const evhttp_uri* http_uri = evhttp_request_get_evhttp_uri(request);
	std::string path = evhttp_uri_get_path(http_uri);
	HTTP_CALLBACK_MAP_IT it = m_callback_map.find(path);
	if (m_callback_map.end() != it)
	{
		const char *uri = evhttp_request_get_uri(request);
		it->second(uri, data, length);
	}
	m_request_map[path] = request;
	delete []data;
}

///////////////////////////////////////////////////////////////////////////
static inline void print_uri_parts_info(const struct evhttp_uri* uri)
{
	printf( "scheme:%s\n", evhttp_uri_get_scheme(uri));
	printf( "host:%s\n", evhttp_uri_get_host(uri));
	printf( "path:%s\n", evhttp_uri_get_path(uri));
	printf( "port:%d\n", evhttp_uri_get_port(uri));
	printf( "query:%s\n", evhttp_uri_get_query(uri));
	printf( "userinfo:%s\n", evhttp_uri_get_userinfo(uri));
	printf( "fragment:%s\n", evhttp_uri_get_fragment(uri));
}

HttpClient::HttpClient()
{
	m_event_base = NULL;
}

HttpClient::~HttpClient()
{
	if (NULL != m_event_base)
	{
		event_base_free(m_event_base);
	}
}

bool HttpClient::Initialize()
{
	m_event_base = event_base_new();
	if (NULL == m_event_base)
	{
		return false;
	}

	return true;
}

void HttpClient::Update()
{
	if(NULL != m_event_base)
	{
		event_base_loopexit(m_event_base, NULL);
		event_base_dispatch(m_event_base);
	}
}

bool HttpClient::HttpRequestUrl(const std::string& url, http_request_cb call_back, void* arg, const std::string& post_data, const std::string& content_type)
{
	if (NULL == m_event_base)
	{
		printf("Not initialize");
		return false;
	}
	
	printf("HttpRquestUrl: %s\n", url.c_str());
	
	HttpRequest* request = new HttpRequest();
	//request->uri = evhttp_uri_parse(url.c_str());
	//内部使用去掉一切URL合法检查,只做URL解析
	request->uri = evhttp_uri_parse_with_flags(url.c_str(), EVHTTP_URI_NONCONFORMANT);
	if(NULL == request->uri)
	{
		printf("parse url failed!");
		delete request;
		return false;
	}
	
	request->call_back = NULL == call_back?DefaultCallBack:call_back;
	request->client = this;
	request->arg = arg;
	request->req = evhttp_request_new(HttpRequestCB, request);
	if(NULL == request->req)
	{
		printf("evhttp_request_new failed!");
		delete request;
		return false;
	}
	
	int port = evhttp_uri_get_port(request->uri);
	request->cn = evhttp_connection_base_new(m_event_base, NULL, evhttp_uri_get_host(request->uri), -1 == port ? 80 : port);
	if (NULL == request->cn)
	{
		printf("evhttp_connection_base_new failed!");
		delete request;
		return false;
	}
	
	const char *path = evhttp_uri_get_path(request->uri);
	bool post = !post_data.empty();
	if(post)
	{
		request->type = HTTP_REQUEST_POST;

		evbuffer_add(request->req->output_buffer, post_data.c_str(), post_data.length());

		evhttp_add_header(request->req->output_headers, "Content-Type", content_type.empty()?"text/plain":content_type.c_str());

		evhttp_make_request(request->cn, request->req, EVHTTP_REQ_POST, path?path:"/");
	}
	else
	{
		request->type = HTTP_REQUEST_GET;
		const char *query = evhttp_uri_get_query(request->uri);
		std::string query_path = std::string(NULL != path?path:"");
		if (!query_path.empty())
		{
			if (NULL != query)
			{
				query_path += std::string("?") + query;
			}
		}
		else
		{
			query_path = "/";
		}
		
		evhttp_make_request(request->cn, request->req, EVHTTP_REQ_GET, query_path.c_str());
	}
	
	evhttp_add_header(request->req->output_headers, "Host", evhttp_uri_get_host(request->uri));
	return true;
}
bool HttpClient::HttpRequestUrl(HttpRequest* request)
{
	if (NULL == m_event_base)
	{
		printf("Not initialize");
		return false;
	}
	
	if(NULL != request->cn)
		evhttp_connection_free(request->cn);
		
	int port = evhttp_uri_get_port(request->uri);
	request->cn = evhttp_connection_base_new(m_event_base, 
					NULL, 
					evhttp_uri_get_host(request->uri), 
					-1 == port ? 80 : port);
	const char *path = evhttp_uri_get_path(request->uri);
	if(request->type == HTTP_REQUEST_POST)
	{
		evhttp_make_request(request->cn, request->req, EVHTTP_REQ_POST, path?path:"/");	
	}
	else
	{
		const char *query = evhttp_uri_get_query(request->uri);
		std::string query_path = std::string(NULL != path?path:"");
		if (!query_path.empty())
		{
			if (NULL != query)
			{
			query_path += std::string("?") + query;
			}
		}
		else
		{
			query_path = "/";
		}

		evhttp_make_request(request->cn, request->req, EVHTTP_REQ_GET, query_path.c_str());
	}
	
	evhttp_remove_header(request->req->output_headers, "Host");
	evhttp_add_header(request->req->output_headers, "Host", evhttp_uri_get_host(request->uri));
	return true;
}

void HttpClient::DefaultCallBack(int response_code, const unsigned char *data, size_t len, void *arg)
{
	printf("ret:%d %s\n", response_code, (char*)data);
}

void HttpClient::HttpRequestCB(struct evhttp_request* req, void *arg)
{
	HttpRequest* request = (HttpRequest*)arg;
	switch (req->response_code)
	{
	case HTTP_OK:
		{
			struct evbuffer* evinput = evhttp_request_get_input_buffer(req);
			size_t length = evbuffer_get_length(evinput);
			unsigned char* data = new unsigned char[length];
			memset(data, 0, length);
			evbuffer_remove(evinput, data, length);
			
			request->call_back(req->response_code, data, length, request->arg);
			delete []data;
			
			evhttp_connection_free(request->cn);
			evhttp_uri_free(request->uri);
			//evhttp_request_free(request->req);
			delete request;
		}
		break;
	case HTTP_MOVETEMP:
	case HTTP_MOVEPERM:
		{
			const char *new_location = evhttp_find_header(req->input_headers, "Location");
			struct evhttp_uri *new_uri = evhttp_uri_parse(new_location);
			evhttp_uri_free(request->uri);
			
			request->uri = new_uri;
			request->client->HttpRequestUrl(request);
			return;
		}
		break;
	default:
		{
			request->call_back(req->response_code, (unsigned char*)req->response_code_line, strlen(req->response_code_line), request->arg);
			event_base_loopexit(request->client->m_event_base, NULL); 
		}
		break;
	}
}

实际,单纯看上面的代码,就会发现虽然lievent对于http的实现非常丰富,并且接口使用也非常简单,但实际如果在使用过程中,没有经过测试代码的测试,很难保证代码能够顺利执行。这也就是第三方库即使再简单易用,我们有时候也会发现自己使用存在很多问题的缘故。因此封装是很简单的一种使用方式了。这样就避免了以后每次加对应的接口的时候去接触生涩的接口了




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值