add HttpServer class

This commit is contained in:
an-tao 2018-04-28 14:37:32 +08:00
parent 81ef7d0392
commit 3641a3edef
11 changed files with 1636 additions and 4 deletions

2
.gitignore vendored
View File

@ -32,3 +32,5 @@
*.app
build
cmake-build-debug
.idea

6
CMakeLists.txt Normal file → Executable file
View File

@ -2,6 +2,8 @@ cmake_minimum_required (VERSION 2.6)
project (DROGON C CXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fpermissive -g -ggdb")
#EXEC_PROGRAM (gcc ARGS "--version | grep '^gcc'|awk '{print $3}' | sed s'/)//g'" OUTPUT_VARIABLE version)
#MESSAGE(STATUS "This is gcc version:: " ${version})
@ -17,7 +19,7 @@ add_subdirectory(trantor)
SET(CMAKE_INSTALL_PREFIX /usr/local/drogon)
#INSTALL(FILES trantor.cfg DESTINATION conf)
#include_directories(${PROJECT_SOURCE_DIR}/src/base/codec ${PROJECT_SOURCE_DIR}/src/Terminus ${CMAKE_BINARY_DIR}/src/base/protobuf_message)
include_directories(${PROJECT_SOURCE_DIR}/trantor ${PROJECT_SOURCE_DIR}/drogon/inc)
aux_source_directory(${PROJECT_SOURCE_DIR}/drogon/src DIR_SRCS)
@ -33,6 +35,6 @@ add_dependencies(drogon trantor)
# set (LIBCONFIG libconfig++)
#endif()
target_link_libraries(drogon trantor)
target_link_libraries(drogon trantor pthread)
#crypt dl m pqxx pq hiredis mongocxx bsoncxx jsoncpp ${LIBCONFIG})
INSTALL(TARGETS drogon DESTINATION bin)

298
drogon/inc/HttpRequest.h Executable file
View File

@ -0,0 +1,298 @@
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is a public header file, it must only include public header files.
//taken from muduo and modified
//
// Copyright 2018, An Tao. All rights reserved.
//
// Use of this source code is governed by a MIT license
// that can be found in the License file.
#pragma once
#include <trantor/utils/NonCopyable.h>
#include <trantor/utils/Logger.h>
#include <trantor/utils/MsgBuffer.h>
#include <map>
#include <assert.h>
#include <stdio.h>
#include <algorithm>
#include <string>
using std::string;
using namespace trantor;
class HttpRequest
{
public:
friend class HttpContext;
enum Method {
kInvalid, kGet, kPost, kHead, kPut, kDelete
};
enum Version {
kUnknown, kHttp10, kHttp11
};
HttpRequest()
: method_(kInvalid),
version_(kUnknown)
{
}
void setVersion(Version v)
{
version_ = v;
}
Version getVersion() const
{
return version_;
}
void parsePremeter();
bool setMethod(const char* start, const char* end)
{
assert(method_ == kInvalid);
std::string m(start, end);
if (m == "GET") {
method_ = kGet;
} else if (m == "POST") {
method_ = kPost;
} else if (m == "HEAD") {
method_ = kHead;
} else if (m == "PUT") {
method_ = kPut;
} else if (m == "DELETE") {
method_ = kDelete;
} else {
method_ = kInvalid;
}
if(method_!=kInvalid)
{
content_="";
query_="";
cookies_.clear();
premeter_.clear();
headers_.clear();
}
return method_ != kInvalid;
}
bool setMethod(const Method method)
{
method_ = method;
content_="";
query_="";
cookies_.clear();
premeter_.clear();
headers_.clear();
return true;
}
Method method() const
{
return method_;
}
const char* methodString() const
{
const char* result = "UNKNOWN";
switch(method_) {
case kGet:
result = "GET";
break;
case kPost:
result = "POST";
break;
case kHead:
result = "HEAD";
break;
case kPut:
result = "PUT";
break;
case kDelete:
result = "DELETE";
break;
default:
break;
}
return result;
}
void setPath(const char* start, const char* end)
{
path_.assign(start, end);
}
void setPath(const std::string& path)
{
path_ = path;
}
std::map<std::string,std::string > getPremeter() const
{
return premeter_;
}
const std::string& path() const
{
return path_;
}
void setQuery(const char* start, const char* end)
{
query_.assign(start, end);
}
void setQuery(const std::string& query)
{
query_ = query;
}
// const string& content() const
// {
// return content_;
// }
const std::string& query() const
{
if(query_!="")
return query_;
if(method_==kPost)
return content_;
return query_;
}
// void setReceiveTime(Timestamp t)
// {
// receiveTime_ = t;
// }
//
// Timestamp receiveTime() const
// {
// return receiveTime_;
// }
void addHeader(const char* start, const char* colon, const char* end)
{
std::string field(start, colon);
++colon;
while (colon < end && isspace(*colon)) {
++colon;
}
std::string value(colon, end);
while (!value.empty() && isspace(value[value.size() - 1])) {
value.resize(value.size() - 1);
}
headers_[field] = value;
transform(field.begin(), field.end(), field.begin(), ::tolower);
if(field == "cookie") {
LOG_INFO<<"cookies!!!:"<<value;
std::string::size_type pos;
while((pos = value.find(";")) != std::string::npos) {
std::string coo = value.substr(0, pos);
auto epos = coo.find("=");
if(epos != std::string::npos) {
std::string cookie_name = coo.substr(0, epos);
std::string::size_type cpos=0;
while(isspace(cookie_name[cpos])&&cpos<cookie_name.length())
cpos++;
cookie_name=cookie_name.substr(cpos);
std::string cookie_value = coo.substr(epos + 1);
cookies_[cookie_name] = cookie_value;
}
value=value.substr(pos+1);
}
if(value.length()>0)
{
std::string &coo = value;
auto epos = coo.find("=");
if(epos != std::string::npos) {
std::string cookie_name = coo.substr(0, epos);
std::string::size_type cpos=0;
while(isspace(cookie_name[cpos])&&cpos<cookie_name.length())
cpos++;
cookie_name=cookie_name.substr(cpos);
std::string cookie_value = coo.substr(epos + 1);
cookies_[cookie_name] = cookie_value;
}
}
}
}
std::string getHeader(const std::string& field) const
{
std::string result;
std::map<std::string, std::string>::const_iterator it = headers_.find(field);
if (it != headers_.end()) {
result = it->second;
}
return result;
}
std::string getCookie(const std::string& field) const
{
std::string result;
std::map<std::string, std::string>::const_iterator it = cookies_.find(field);
if (it != cookies_.end()) {
result = it->second;
}
return result;
}
const std::map<std::string, std::string>& headers() const
{
return headers_;
}
const std::map<std::string, std::string>& cookies() const
{
return cookies_;
}
const std::string& getContent() const
{
return content_;
}
void swap(HttpRequest& that)
{
std::swap(method_, that.method_);
path_.swap(that.path_);
query_.swap(that.query_);
//receiveTime_.swap(that.receiveTime_);
headers_.swap(that.headers_);
}
void setContent(const std::string& content)
{
content_ = content;
}
void addHeader(const std::string& key, const std::string& value)
{
headers_[key] = value;
}
void addCookie(const std::string& key, const std::string& value)
{
cookies_[key] = value;
}
void appendToBuffer(MsgBuffer* output) const;
private:
Method method_;
Version version_;
std::string path_;
std::string query_;
//trantor::Date receiveTime_;
std::map<std::string, std::string> headers_;
std::map<std::string, std::string> cookies_;
std::map<std::string, std::string> premeter_;
protected:
std::string content_;
size_t contentLen;
};

246
drogon/inc/HttpResponse.h Executable file
View File

@ -0,0 +1,246 @@
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is a public header file, it must only include public header files.
//taken from muduo and modified
//
// Copyright 2018, An Tao. All rights reserved.
//
// Use of this source code is governed by a MIT license
// that can be found in the License file.
#pragma once
#include <trantor/utils/MsgBuffer.h>
#include <map>
#include <string>
using std::string;
#define CT_APPLICATION_JSON 1
#define CT_TEXT_PLAIN 2
#define CT_TEXT_HTML 3
#define CT_APPLICATION_X_JAVASCRIPT 4
#define CT_TEXT_CSS 5
#define CT_TEXT_XML 6
#define CT_APPLICATION_XML 7
#define CT_TEXT_XSL 8
#define CT_APPLICATION_OCTET_STREAM 9
#define CT_APPLICATION_X_FONT_TRUETYPE 10
#define CT_APPLICATION_X_FONT_OPENTYPE 11
#define CT_APPLICATION_FONT_WOFF 12
#define CT_APPLICATION_FONT_WOFF2 13
#define CT_APPLICATION_VND_MS_FONTOBJ 14
#define CT_IMAGE_SVG_XML 15
#define CT_IMAGE_PNG 16
#define CT_IMAGE_JPG 17
#define CT_IMAGE_GIF 18
#define CT_IMAGE_XICON 19
#define CT_IMAGE_ICNS 20
#define CT_IMAGE_BMP 21
using namespace trantor;
class HttpResponse
{
friend class HttpContext;
public:
enum HttpStatusCode {
kUnknown,
k200Ok = 200,
k301MovedPermanently = 301,
k302Found = 302,
k304NotModified = 304,
k400BadRequest = 400,
k404NotFound = 404,
};
enum Version {
kHttp10, kHttp11
};
explicit HttpResponse(bool close)
: statusCode_(kUnknown),
closeConnection_(close),
left_body_length_(0),
current_chunk_length_(0)
{
}
explicit HttpResponse()
: statusCode_(kUnknown),
closeConnection_(false),
left_body_length_(0)
{
}
void setStatusCode(HttpStatusCode code)
{
statusCode_ = code;
setStatusMessage(web_response_code_to_string(code));
}
void setStatusCode(HttpStatusCode code, const std::string& status_message)
{
statusCode_ = code;
setStatusMessage(status_message);
}
void setVersion(const Version v)
{
v_ = v;
}
void setCloseConnection(bool on)
{
closeConnection_ = on;
}
bool closeConnection() const
{
return closeConnection_;
}
void setContentTypeCode(uint8_t type)
{
contentType_=type;
setContentType(web_content_type_to_string(type));
}
std::string getHeader(const std::string& key)
{
if(headers_.find(key) == headers_.end())
{
return "";
}
else
{
return headers_[key];
}
}
void addHeader(const std::string& key, const std::string& value)
{
headers_[key] = value;
}
void addHeader(const char* start, const char* colon, const char* end)
{
std::string field(start, colon);
++colon;
while (colon < end && isspace(*colon)) {
++colon;
}
std::string value(colon, end);
while (!value.empty() && isspace(value[value.size() - 1])) {
value.resize(value.size() - 1);
}
headers_[field] = value;
transform(field.begin(), field.end(), field.begin(), ::tolower);
if(field == "cookie") {
//LOG_INFO<<"cookies!!!:"<<value;
std::string::size_type pos;
while((pos = value.find(";")) != std::string::npos) {
std::string coo = value.substr(0, pos);
auto epos = coo.find("=");
if(epos != std::string::npos) {
std::string cookie_name = coo.substr(0, epos);
std::string::size_type cpos=0;
while(isspace(cookie_name[cpos])&&cpos<cookie_name.length())
cpos++;
cookie_name=cookie_name.substr(cpos);
std::string cookie_value = coo.substr(epos + 1);
cookies_[cookie_name] = cookie_value;
}
value=value.substr(pos+1);
}
if(value.length()>0)
{
std::string &coo = value;
auto epos = coo.find("=");
if(epos != std::string::npos) {
std::string cookie_name = coo.substr(0, epos);
std::string::size_type cpos=0;
while(isspace(cookie_name[cpos])&&cpos<cookie_name.length())
cpos++;
cookie_name=cookie_name.substr(cpos);
std::string cookie_value = coo.substr(epos + 1);
cookies_[cookie_name] = cookie_value;
}
}
}
}
void addCookie(const std::string& key, const std::string& value)
{
cookies_[key] = value;
}
void setBody(const std::string& body)
{
body_ = body;
}
void redirect(const std::string& url)
{
headers_["Location"] = url;
}
void appendToBuffer(MsgBuffer* output) const;
void clear()
{
statusCode_ = kUnknown;
v_ = kHttp11;
statusMessage_.clear();
headers_.clear();
cookies_.clear();
body_.clear();
left_body_length_ = 0;
current_chunk_length_ = 0;
}
// void setReceiveTime(trantor::Date t)
// {
// receiveTime_ = t;
// }
std::string getBody() const
{
return body_;
}
protected:
static const std::string web_content_type_to_string(uint8_t contenttype);
static const std::string web_response_code_to_string(int code);
private:
std::map<std::string, std::string> headers_;
std::map<std::string, std::string> cookies_;
HttpStatusCode statusCode_;
// FIXME: add http version
Version v_;
std::string statusMessage_;
bool closeConnection_;
std::string body_;
size_t left_body_length_;
size_t current_chunk_length_;
uint8_t contentType_;
//trantor::Date receiveTime_;
void setContentType(const std::string& contentType)
{
addHeader("Content-Type", contentType);
}
void setStatusMessage(const std::string& message)
{
statusMessage_ = message;
}
};

53
drogon/inc/HttpServer.h Executable file
View File

@ -0,0 +1,53 @@
// Copyright 2018,antao. All rights reserved.
#pragma once
#include <trantor/net/TcpServer.h>
#include <trantor/utils/NonCopyable.h>
#include <functional>
#include <string>
class HttpRequest;
class HttpResponse;
using namespace trantor;
namespace drogon
{
class HttpServer : trantor::NonCopyable
{
public:
typedef std::function< void (const HttpRequest&,std::function<void (HttpResponse &)>)> HttpAsyncCallback;
HttpServer(EventLoop* loop,
const InetAddress& listenAddr,
const std::string& name);
~HttpServer();
EventLoop* getLoop() const { return server_.getLoop(); }
void setHttpAsyncCallback(const HttpAsyncCallback& cb)
{
httpAsyncCallback_= cb;
}
void setIoLoopNum(int numThreads)
{
server_.setIoLoopNum(numThreads);
}
void start();
private:
void onConnection(const TcpConnectionPtr& conn);
void onMessage(const TcpConnectionPtr&,
MsgBuffer*);
void onRequest(const TcpConnectionPtr&, const HttpRequest&);
trantor::TcpServer server_;
HttpAsyncCallback httpAsyncCallback_;
};
}

405
drogon/src/HttpContext.cc Executable file
View File

@ -0,0 +1,405 @@
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
//taken from muduo and modified
//
// Copyright 2018, An Tao. All rights reserved.
//
// Use of this source code is governed by a MIT license
// that can be found in the License file.
#include <trantor/utils/MsgBuffer.h>
#include <trantor/utils/Logger.h>
#include "HttpContext.h"
#include <iostream>
using namespace trantor;
bool HttpContext::processRequestLine(const char *begin, const char *end)
{
bool succeed = false;
const char *start = begin;
const char *space = std::find(start, end, ' ');
if (space != end && request_.setMethod(start, space))
{
start = space + 1;
space = std::find(start, end, ' ');
if (space != end)
{
const char *question = std::find(start, space, '?');
if (question != space)
{
request_.setPath(start, question);
request_.setQuery(question + 1, space);
}
else
{
request_.setPath(start, space);
}
start = space + 1;
succeed = end - start == 8 && std::equal(start, end - 1, "HTTP/1.");
if (succeed)
{
if (*(end - 1) == '1')
{
request_.setVersion(HttpRequest::kHttp11);
}
else if (*(end - 1) == '0')
{
request_.setVersion(HttpRequest::kHttp10);
}
else
{
succeed = false;
}
}
}
}
return succeed;
}
// return false if any error
bool HttpContext::parseRequest(MsgBuffer *buf)
{
bool ok = true;
bool hasMore = true;
// std::cout<<std::string(buf->peek(),buf->readableBytes())<<std::endl;
while (hasMore)
{
if (state_ == kExpectRequestLine)
{
const char *crlf = buf->findCRLF();
if (crlf)
{
ok = processRequestLine(buf->peek(), crlf);
if (ok)
{
//request_.setReceiveTime(receiveTime);
buf->retrieveUntil(crlf + 2);
state_ = kExpectHeaders;
}
else
{
hasMore = false;
}
}
else
{
hasMore = false;
}
}
else if (state_ == kExpectHeaders)
{
const char *crlf = buf->findCRLF();
if (crlf)
{
const char *colon = std::find(buf->peek(), crlf, ':');
if (colon != crlf)
{
request_.addHeader(buf->peek(), colon, crlf);
}
else
{
// empty line, end of header
std::string len = request_.getHeader("Content-Length");
LOG_INFO << "content len=" << len;
if (len != "")
{
request_.contentLen = atoi(len.c_str());
state_ = kExpectBody;
}
else
{
state_ = kGotAll;
hasMore = false;
}
}
buf->retrieveUntil(crlf + 2);
}
else
{
hasMore = false;
}
}
else if (state_ == kExpectBody)
{
//LOG_INFO << "expectBody:len=" << request_.contentLen;
//LOG_INFO << "expectBody:buf=" << buf;
if (buf->readableBytes() == 0)
{
if (request_.contentLen <= 0)
{
state_ = kGotAll;
}
break;
}
if (request_.contentLen >= buf->readableBytes())
{
request_.contentLen -= buf->readableBytes();
request_.content_ += std::string(buf->peek(), buf->readableBytes());
buf->retrieveAll();
}
else
{
request_.content_ += std::string(buf->peek(), request_.contentLen);
buf->retrieve(request_.contentLen);
request_.contentLen = 0;
}
if (request_.contentLen <= 0)
{
state_ = kGotAll;
LOG_INFO << "post got all:len=" << request_.content_.length();
//LOG_INFO<<"content:"<<request_.content_;
LOG_INFO << "content(END)";
hasMore = false;
}
}
}
return ok;
}
bool HttpContext::processResponseLine(const char *begin, const char *end)
{
const char *start = begin;
const char *space = std::find(start, end, ' ');
if (space != end)
{
LOG_DEBUG << *(space - 1);
if (*(space - 1) == '1')
{
response_.setVersion(HttpResponse::kHttp11);
}
else if (*(space - 1) == '0')
{
response_.setVersion(HttpResponse::kHttp10);
}
else
{
return false;
}
}
start = space + 1;
space = std::find(start, end, ' ');
if (space != end)
{
std::string status_code(start, space - start);
std::string status_message(space + 1, end - space - 1);
LOG_DEBUG << status_code << " " << status_message;
switch (atoi(status_code.c_str()))
{
case 200:
response_.setStatusCode(HttpResponse::k200Ok, status_message);
break;
case 301:
response_.setStatusCode(HttpResponse::k301MovedPermanently, status_message);
break;
case 302:
response_.setStatusCode(HttpResponse::k302Found, status_message);
break;
case 304:
response_.setStatusCode(HttpResponse::k304NotModified, status_message);
break;
case 400:
response_.setStatusCode(HttpResponse::k400BadRequest, status_message);
break;
case 404:
response_.setStatusCode(HttpResponse::k404NotFound, status_message);
break;
default:
return false;
}
return true;
}
return false;
}
// return false if any error
bool HttpContext::parseResponse(MsgBuffer *buf)
{
bool ok = true;
bool hasMore = true;
// std::cout<<std::string(buf->peek(),buf->readableBytes())<<std::endl;
//LOG_INFO<<"response message:"<<std::string(buf->peek(),buf->readableBytes());
while (hasMore)
{
//LOG_DEBUG<<"res_state_: "<<(int)res_state_;
if (res_state_ == HttpResponseParseState::kExpectResponseLine)
{
const char *crlf = buf->findCRLF();
if (crlf)
{
ok = processResponseLine(buf->peek(), crlf);
if (ok)
{
//response_.setReceiveTime(receiveTime);
buf->retrieveUntil(crlf + 2);
res_state_ = HttpResponseParseState::kExpectHeaders;
}
else
{
hasMore = false;
}
}
else
{
hasMore = false;
}
}
else if (res_state_ == HttpResponseParseState::kExpectHeaders)
{
const char *crlf = buf->findCRLF();
if (crlf)
{
const char *colon = std::find(buf->peek(), crlf, ':');
if (colon != crlf)
{
response_.addHeader(buf->peek(), colon, crlf);
}
else
{
// empty line, end of header
// FIXME:
std::string len = response_.getHeader("Content-Length");
LOG_INFO << "content len=" << len;
if (len != "")
{
response_.left_body_length_ = atoi(len.c_str());
res_state_ = HttpResponseParseState::kExpectBody;
}
else
{
std::string encode = response_.getHeader("Transfer-Encoding");
if (encode == "chunked")
{
res_state_ = HttpResponseParseState::kExpectChunkLen;
hasMore = true;
}
else
{
res_state_ = HttpResponseParseState::kExpectClose;
hasMore = true;
}
}
}
buf->retrieveUntil(crlf + 2);
}
else
{
hasMore = false;
}
}
else if (res_state_ == HttpResponseParseState::kExpectBody)
{
//LOG_INFO << "expectBody:len=" << request_.contentLen;
//LOG_INFO << "expectBody:buf=" << buf;
if (buf->readableBytes() == 0)
{
if (response_.left_body_length_ <= 0)
{
res_state_ = HttpResponseParseState::kGotAll;
}
break;
}
if (response_.left_body_length_ >= buf->readableBytes())
{
response_.left_body_length_ -= buf->readableBytes();
response_.body_ += std::string(buf->peek(), buf->readableBytes());
buf->retrieveAll();
}
else
{
response_.body_ += std::string(buf->peek(), response_.left_body_length_);
buf->retrieve(request_.contentLen);
response_.left_body_length_ = 0;
}
if (response_.left_body_length_ <= 0)
{
res_state_ = HttpResponseParseState::kGotAll;
LOG_INFO << "post got all:len=" << response_.left_body_length_;
//LOG_INFO<<"content:"<<request_.content_;
LOG_INFO << "content(END)";
hasMore = false;
}
}
else if (res_state_ == HttpResponseParseState::kExpectClose)
{
response_.body_ += std::string(buf->peek(), buf->readableBytes());
buf->retrieveAll();
break;
}
else if (res_state_ == HttpResponseParseState::kExpectChunkLen)
{
const char *crlf = buf->findCRLF();
if (crlf)
{
//chunk length line
std::string len(buf->peek(), crlf - buf->peek());
char *end;
response_.current_chunk_length_ = strtol(len.c_str(), &end, 16);
LOG_DEBUG << "chun length : " << response_.current_chunk_length_;
if (response_.current_chunk_length_ != 0)
{
res_state_ = HttpResponseParseState::kExpectChunkBody;
}
else
{
res_state_ = HttpResponseParseState::kExpectLastEmptyChunk;
}
buf->retrieveUntil(crlf + 2);
}
else
{
hasMore = false;
}
}
else if (res_state_ == HttpResponseParseState::kExpectChunkBody)
{
const char *crlf = buf->findCRLF();
if (crlf)
{
if (response_.current_chunk_length_ == crlf - buf->peek())
{
//current chunk end crlf
response_.body_ += std::string(buf->peek(), response_.current_chunk_length_);
buf->retrieveUntil(crlf + 2);
response_.current_chunk_length_ = 0;
res_state_ = HttpResponseParseState::kExpectChunkLen;
}
else if (response_.current_chunk_length_ > crlf - buf->peek())
{
//current chunk body crlf
response_.body_ += std::string(buf->peek(), crlf - buf->peek() + 1);
buf->retrieveUntil(crlf + 2);
response_.current_chunk_length_ -= (crlf - buf->peek() + 2);
}
}
else
{
hasMore = false;
}
}
else if (res_state_ == HttpResponseParseState::kExpectLastEmptyChunk)
{
//last empty chunk
const char *crlf = buf->findCRLF();
if (crlf)
{
buf->retrieveUntil(crlf + 2);
res_state_ = HttpResponseParseState::kGotAll;
break;
}
else
{
hasMore = false;
}
}
}
return ok;
}

119
drogon/src/HttpContext.h Executable file
View File

@ -0,0 +1,119 @@
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
// This is an internal header file, you should not include this.
//taken from muduo and modified
//
// Copyright 2018, An Tao. All rights reserved.
//
// Use of this source code is governed by a MIT license
// that can be found in the License file.
#ifndef MUDUO_NET_HTTP_HTTPCONTEXT_H
#define MUDUO_NET_HTTP_HTTPCONTEXT_H
#include "HttpRequest.h"
#include "HttpResponse.h"
#include <trantor/utils/MsgBuffer.h>
using namespace trantor;
class HttpContext
{
public:
enum HttpRequestParseState
{
kExpectRequestLine,
kExpectHeaders,
kExpectBody,
kGotAll,
};
enum class HttpResponseParseState
{
kExpectResponseLine,
kExpectHeaders,
kExpectBody,
kExpectChunkLen,
kExpectChunkBody,
kExpectLastEmptyChunk,
kExpectClose,
kGotAll,
};
HttpContext()
: state_(kExpectRequestLine),
res_state_(HttpResponseParseState::kExpectResponseLine)
{
}
// default copy-ctor, dtor and assignment are fine
// return false if any error
bool parseRequest(MsgBuffer *buf);
bool parseResponse(MsgBuffer *buf);
bool gotAll() const
{
return state_ == kGotAll;
}
bool resGotAll() const
{
return res_state_ == HttpResponseParseState::kGotAll;
}
bool resExpectResponseLine() const
{
return res_state_ == HttpResponseParseState::kExpectResponseLine;
}
void reset()
{
state_ = kExpectRequestLine;
HttpRequest dummy;
request_.swap(dummy);
}
void resetRes()
{
res_state_ = HttpResponseParseState::kExpectResponseLine;
response_.clear();
}
const HttpRequest &request() const
{
return request_;
}
HttpRequest &request()
{
return request_;
}
const HttpResponse &response() const
{
return response_;
}
HttpResponse &response()
{
return response_;
}
private:
bool processRequestLine(const char *begin, const char *end);
bool processResponseLine(const char *begin, const char *end);
HttpRequestParseState state_;
HttpRequest request_;
HttpResponseParseState res_state_;
HttpResponse response_;
};
#endif // MUDUO_NET_HTTP_HTTPCONTEXT_H

185
drogon/src/HttpRequest.cc Executable file
View File

@ -0,0 +1,185 @@
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
//taken from muduo and modified
//
// Copyright 2018, An Tao. All rights reserved.
//
// Use of this source code is governed by a MIT license
// that can be found in the License file.
#include "HttpRequest.h"
//解url编码实现
#include <iostream>
static int urldecode(const char* encd,char* decd)
{
int j,i;
char *cd =(char*) encd;
char p[2];
unsigned int num;
j=0;
for( i = 0; i < strlen(cd); i++ )
{
memset( p,0,2);
if( cd[i] != '%' )
{
if(cd[i]=='+')
decd[j++]=' ';
else
decd[j++] = cd[i];
continue;
}
p[0] = cd[++i];
p[1] = cd[++i];
p[0] = p[0] - 48 - ((p[0] >= 'A') ? 7 : 0) - ((p[0] >= 'a') ? 32 : 0);
p[1] = p[1] - 48 - ((p[1] >= 'A') ? 7 : 0) - ((p[1] >= 'a') ? 32 : 0);
decd[j++] = (unsigned char)(p[0] * 16 + p[1]);
}
decd[j] = 0;
return j;
}
void HttpRequest::parsePremeter()
{
const std::string &type=getHeader("Content-Type");
const std::string &input=query();
if(method_==kGet||(method_==kPost&&(type==""||type.find("application/x-www-form-urlencoded")!=std::string::npos)))
{
std::string::size_type pos=0;
while((input[pos]=='?'||isspace(input[pos]))&&pos<input.length())
{
pos++;
}
std::string value=input.substr(pos);
while((pos = value.find("&")) != std::string::npos) {
std::string coo = value.substr(0, pos);
auto epos = coo.find("=");
if(epos != std::string::npos) {
std::string key = coo.substr(0, epos);
std::string::size_type cpos=0;
while(isspace(key[cpos])&&cpos<key.length())
cpos++;
key=key.substr(cpos);
std::string pvalue = coo.substr(epos + 1);
std::string pdecode=pvalue;
std::string keydecode=key;
int ret=urldecode((char *)key.c_str(),(char *)keydecode.c_str());
keydecode=keydecode.substr(0,ret);
ret=urldecode((char *)pvalue.c_str(),(char *)pdecode.c_str());
pdecode=pdecode.substr(0,ret);
premeter_[keydecode] = pdecode;
}
value=value.substr(pos+1);
}
if(value.length()>0)
{
std::string &coo = value;
auto epos = coo.find("=");
if(epos != std::string::npos) {
std::string key = coo.substr(0, epos);
std::string::size_type cpos=0;
while(isspace(key[cpos])&&cpos<key.length())
cpos++;
key=key.substr(cpos);
std::string pvalue = coo.substr(epos + 1);
std::string pdecode=pvalue;
std::string keydecode=key;
int ret=urldecode((char *)key.c_str(),(char *)keydecode.c_str());
keydecode=keydecode.substr(0,ret);
ret=urldecode((char *)pvalue.c_str(),(char *)pdecode.c_str());
pdecode=pdecode.substr(0,ret);
premeter_[keydecode] = pdecode;
}
}
}
LOG_DEBUG<<"premeter:";
for(auto iter:premeter_)
{
LOG_DEBUG<<iter.first<<"="<<iter.second;
}
}
void HttpRequest::appendToBuffer(MsgBuffer* output) const
{
switch(method_)
{
case kDelete:
output->append("DELETE ");
break;
case kGet:
output->append("GET ");
break;
case kHead:
output->append("HEAD ");
break;
case kPost:
output->append("POST ");
break;
case kPut:
output->append("PUT ");
break;
default:
return;
}
if(path_.size() != 0)
{
output->append(path_);
output->append(" ");
}
else
{
output->append("/ ");
}
if(version_ == kHttp11)
{
output->append("HTTP/1.1");
}
else if(version_ == kHttp10)
{
output->append("HTTP/1.0");
}
else
{
return;
}
output->append("\r\n");
for (std::map<std::string, std::string>::const_iterator it = headers_.begin();
it != headers_.end();
++it) {
output->append(it->first);
output->append(": ");
output->append(it->second);
output->append("\r\n");
}
if(cookies_.size() > 0) {
output->append("Set-Cookie: ");
for(auto it = cookies_.begin(); it != cookies_.end(); it++) {
output->append(it->first);
output->append("= ");
output->append(it->second);
output->append(";");
}
output->unwrite(1);//delete last ';'
output->append("\r\n");
}
output->append("\r\n");
//LOG_INFO<<"request(no body):"<<output->peek();
output->append(content_);
}

176
drogon/src/HttpResponse.cc Executable file
View File

@ -0,0 +1,176 @@
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
//taken from muduo and modified
//
// Copyright 2018, An Tao. All rights reserved.
//
// Use of this source code is governed by a MIT license
// that can be found in the License file.
#include "HttpResponse.h"
#include <trantor/utils/Logger.h>
#include <stdio.h>
using namespace trantor;
const std::string HttpResponse::web_content_type_to_string(uint8_t contenttype)
{
switch(contenttype) {
case CT_TEXT_HTML:
return "text/html; charset=utf-8";
case CT_APPLICATION_XML:
return "application/xml; charset=utf-8";
case CT_APPLICATION_JSON:
return "application/json; charset=utf-8";
case CT_APPLICATION_X_JAVASCRIPT:
return "application/x-javascript; charset=utf-8";
case CT_TEXT_CSS:
return "text/css; charset=utf-8";
case CT_TEXT_XML:
return "text/xml; charset=utf-8";
case CT_TEXT_XSL:
return "text/xsl; charset=utf-8";
case CT_APPLICATION_OCTET_STREAM:
return "application/octet-stream";
case CT_IMAGE_SVG_XML:
return "image/svg+xml";
case CT_APPLICATION_X_FONT_TRUETYPE:
return "application/x-font-truetype";
case CT_APPLICATION_X_FONT_OPENTYPE:
return "application/x-font-opentype";
case CT_APPLICATION_FONT_WOFF:
return "application/font-woff";
case CT_APPLICATION_FONT_WOFF2:
return "application/font-woff2";
case CT_APPLICATION_VND_MS_FONTOBJ:
return "application/vnd.ms-fontobject";
case CT_IMAGE_PNG:
return "image/png";
case CT_IMAGE_JPG:
return "image/jpeg";
case CT_IMAGE_GIF:
return "image/gif";
case CT_IMAGE_XICON:
return "image/x-icon";
case CT_IMAGE_BMP:
return "image/bmp";
case CT_IMAGE_ICNS:
return "image/icns";
default:
case CT_TEXT_PLAIN:
return "text/plain; charset=utf-8";
}
}
const std::string HttpResponse::web_response_code_to_string(int code)
{
switch(code) {
case 200:
return "OK";
case 304:
return "Not Modified";
case 307:
return "Temporary Redirect";
case 400:
return "Bad Request";
case 403:
return "Forbidden";
case 404:
return "Not Found";
case 412:
return "Preconditions Failed";
default:
if(code >= 100 && code < 200)
return "Informational";
if(code >= 200 && code < 300)
return "Successful";
if(code >= 300 && code < 400)
return "Redirection";
if(code >= 400 && code < 500)
return "Bad Request";
if(code >= 500 && code < 600)
return "Server Error";
return "Undefined Error";
}
}
void HttpResponse::appendToBuffer(MsgBuffer* output) const
{
char buf[32];
snprintf(buf, sizeof buf, "HTTP/1.1 %d ", statusCode_);
output->append(buf);
output->append(statusMessage_);
output->append("\r\n");
if (closeConnection_) {
output->append("Connection: close\r\n");
} else {
snprintf(buf, sizeof buf, "Content-Length: %zd\r\n", body_.size());
output->append(buf);
output->append("Connection: Keep-Alive\r\n");
}
for (std::map<std::string, std::string>::const_iterator it = headers_.begin();
it != headers_.end();
++it) {
output->append(it->first);
output->append(": ");
output->append(it->second);
output->append("\r\n");
}
if(cookies_.size() > 0) {
output->append("Set-Cookie: ");
for(auto it = cookies_.begin(); it != cookies_.end(); it++) {
output->append(it->first);
output->append("= ");
output->append(it->second);
output->append(";");
}
output->unwrite(1);//delete last ';'
output->append("\r\n");
}
output->append("\r\n");
LOG_INFO<<"reponse(no body):"<<output->peek();
output->append(body_);
}

109
drogon/src/HttpServer.cc Executable file
View File

@ -0,0 +1,109 @@
// Copyright 2010, Shuo Chen. All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
// Author: Shuo Chen (chenshuo at chenshuo dot com)
//
//taken from muduo and modified
//
// Copyright 2018, An Tao. All rights reserved.
//
// Use of this source code is governed by a MIT license
// that can be found in the License file.
#include "HttpServer.h"
#include <trantor/utils/Logger.h>
#include "HttpContext.h"
#include "HttpRequest.h"
#include "HttpResponse.h"
#include <functional>
using namespace std::placeholders;
using namespace drogon;
using namespace trantor;
static void defaultHttpAsyncCallback(const HttpRequest&, std::function<void( HttpResponse& resp)>callback)
{
HttpResponse resp(true);
resp.setStatusCode(HttpResponse::k404NotFound);
resp.setCloseConnection(true);
callback(resp);
}
HttpServer::HttpServer(EventLoop* loop,
const InetAddress& listenAddr,
const std::string& name)
: server_(loop, listenAddr, name.c_str()),
httpAsyncCallback_(defaultHttpAsyncCallback)
{
server_.setConnectionCallback(
std::bind(&HttpServer::onConnection, this, _1));
server_.setRecvMessageCallback(
std::bind(&HttpServer::onMessage, this, _1, _2));
}
HttpServer::~HttpServer()
{
}
void HttpServer::start()
{
LOG_WARN << "HttpServer[" << server_.name()
<< "] starts listenning on " << server_.ipPort();
server_.start();
}
void HttpServer::onConnection(const TcpConnectionPtr& conn)
{
if (conn->connected()) {
conn->setContext(new HttpContext());
}
}
void HttpServer::onMessage(const TcpConnectionPtr& conn,
MsgBuffer* buf)
{
HttpContext* context = (HttpContext*)(conn->getContext());
// LOG_INFO << "###:" << string(buf->peek(), buf->readableBytes());
if (!context->parseRequest(buf)) {
conn->send("HTTP/1.1 400 Bad Request\r\n\r\n");
conn->shutdown();
}
if (context->gotAll()) {
context->request().parsePremeter();
onRequest(conn, context->request());
context->reset();
}
}
void HttpServer::onRequest(const TcpConnectionPtr& conn, const HttpRequest& req)
{
const std::string& connection = req.getHeader("Connection");
bool close = connection == "close" ||
(req.getVersion() == HttpRequest::kHttp10 && connection != "Keep-Alive");
httpAsyncCallback_(req, [ = ](HttpResponse & response) {
MsgBuffer buf;
response.setCloseConnection(close);
response.appendToBuffer(&buf);
conn->send(buf.peek(),buf.readableBytes());
if (response.closeConnection()) {
conn->shutdown();
}
});
}

View File

@ -1,4 +1,41 @@
#include <trantor/net/TcpServer.h>
#include <trantor/utils/Logger.h>
#include <trantor/net/EventLoopThread.h>
#include <string>
#include <iostream>
using namespace trantor;
#define USE_IPV6 0
int main()
{
return 0;
}
LOG_DEBUG<<"test start";
Logger::setLogLevel(Logger::TRACE);
//EventLoopThread loopThread;
EventLoop loop;
//loopThread.run();
#if USE_IPV6
InetAddress addr(7676,true,true);
#else
InetAddress addr(7676);
#endif
TcpServer server(&loop,addr,"test");
server.setRecvMessageCallback([](const TcpConnectionPtr &connectionPtr,MsgBuffer *buffer){
//LOG_DEBUG<<"recv callback!";
std::cout<<std::string(buffer->peek(),buffer->readableBytes());
connectionPtr->send(buffer->peek(),buffer->readableBytes());
buffer->retrieveAll();
connectionPtr->forceClose();
});
server.setConnectionCallback([](const TcpConnectionPtr& connPtr){
if(connPtr->connected())
{
LOG_DEBUG<<"New connection";
}
else if(connPtr->disconnected())
{
LOG_DEBUG<<"connection disconnected";
}
});
server.setIoLoopNum(3);
server.start();
loop.loop();
}