近來在項目中需要實現一個http service的功能。雖然可以通過封裝socket自己實現http的發送和解析。但考慮到目前網路上還是有大量的http的解析源碼,自己再從頭實現一番稍顯麻煩。因此在網路上搜颳了一番,看到很多實現http的代碼, 無一不體現了其輕量級的特點。然而,雖然輕量級,但從代碼的量上來說,為了一個http service的功能,引入這許多的代碼,也著實不是十分情願呢。後在同事的推薦下選擇了mongoose的代碼。
mongoose的代碼著實輕量,先看看它的特點:
1. 在整個的實現是使用c語言編寫
2. 整個代碼也只有一個mongoose.c和mongoose.h兩個檔案, 從引入第三方的考慮上也著實不多。
3. 實現的功能還是非常多的,從使用的層面上來說功能還是比較全面。只不過不知道是否是為了第三方使用的方便還是怎麼地,它的代碼只用了兩個源檔案罷了。諸多的功能也大以宏的開始與結束來區分。
4. 樣本非常齊全,所有的功能都有單獨的樣本
然而,不管它實現多少功能,對於我來說只需要三個:
1. 有http的解析等
2. 檔案少,使用方便,不需要因為使用一個簡單的功能引入大量代碼,而且引入的代碼著實沒有用到。
3. 有完整的樣本
當我們拿到一個第三方庫或者第三方源碼的時候,第一件事情就是看看代碼的樣本,並且自己動手謝謝測試的代碼,完成自己想要的功能。於是,我花了一點時間自己寫了一個測試的代碼,最後發現測試的時候並不通。雖然它的程式碼範例很全面,然而對於我們來說,或許在它的代碼中,有些函數我們不需要,而有些函數卻不再這個樣本中使用,因此需要自己測試。耗費了一些時間以後,我個人做了一下的簡單封裝,算是簡單的實現一個http service的功能,其中使用到了一點c++11的特性。可丟磚頭,也可交流。
// File: basic_http.h// Description: ---// Notes: ---// Author: Haust <wyy123_2008@qq.com>// Revision: 2015-11-19 by Haust#pragma once#include "mongoose.h"#include <map>#include <string>#include <functional>class BasicHttp {public: using handler = std::function<void(std::string, std::string)>;public: virtual ~BasicHttp(){}; void Init(uint32_t port); bool Start(); bool Close(); bool RegisterHandler(std::string uri, handler f); void UnRegisterHandler(std::string uri); void Loop(int milli); void SendReply(std::string uri, std::string reply); void SendError(std::string uri, int errcode, std::string reply);protected: using handler_map = std::map<std::string, handler>; using connection_map = std::multimap<std::string, mg_connection*>;private: static void EvHandler(struct mg_connection* nc, int ev, void* ev_data); static void HandleRequst(struct mg_connection* nc, int ev, void *ev_data); public: static handler_map _handlers; static connection_map _connections; char _port[11]; struct mg_mgr _mgr;};
// File: basic_http.cpp// Description: ---// Notes: ---// Author: Haust <wyy123_2008@qq.com>// Revision: 2015-11-19 by Haust#include "basic_http.h"BasicHttp::handler_map BasicHttp::_handlers;BasicHttp::connection_map BasicHttp::_connections;void BasicHttp::Init(uint32_t port){ memset(_port, 0, sizeof(_port)); snprintf(_port, sizeof(_port), "%u", port);}bool BasicHttp::Start(){ mg_mgr_init(&_mgr, NULL); auto nc = mg_bind(&_mgr, _port, EvHandler); if(nullptr == nc) return false; mg_set_protocol_http_websocket(nc); return true;}bool BasicHttp::Close(){ mg_mgr_free(&_mgr); return true;}bool BasicHttp::RegisterHandler(std::string uri, handler f){ auto it = _handlers.find(uri); if(_handlers.end() != it) return false; return _handlers.emplace(uri, f).second;}void BasicHttp::UnRegisterHandler(std::string uri){ auto it = _handlers.find(uri); if(_handlers.end() != it) _handlers.erase(it);}void BasicHttp::Loop(int milli){ mg_mgr_poll(&_mgr, milli);}void BasicHttp::EvHandler(struct mg_connection* nc, int ev, void* ev_data){ switch(ev){ case MG_EV_HTTP_REQUEST: HandleRequst(nc, ev, ev_data); break; default: break; }}void BasicHttp::HandleRequst(struct mg_connection *nc, int ev, void* ev_data){ http_message* hm = (http_message*)ev_data; std::string uri(hm->uri.p, hm->uri.len); auto it = _handlers.find(uri); if(_handlers.end() == it) return; _connections.emplace(uri, nc); it->second(std::string(hm->query_string.p, hm->query_string.len), std::string(hm->body.p, hm->body.len));}void BasicHttp::SendReply(std::string uri, std::string reply){ auto range = _connections.equal_range(uri); if(range.first == range.second) return; auto it = range.first; mg_printf(it->second, "HTTP/1.1 200 OK\r\niConnection: close\r\nContent-Type: text/html\r\nContent-Length: %u\r\n\r\n%s\r\n", (uint32_t)reply.length(), reply.c_str()); it->second->flags |= MG_F_SEND_AND_CLOSE; _connections.erase(it);}void BasicHttp::SendError(std::string uri, int errcode, std::string reply){ auto range = _connections.equal_range(uri); if(range.first == range.second) return; auto it = range.first; mg_printf(it->second, "HTTP/1.1 %d %s\r\n", errcode, reply.c_str()); it->second->flags |= MG_F_SEND_AND_CLOSE; _connections.erase(it);}#include "mongoose.c"