request line + header + body (header分为普通报头,请求报头与实体报头)
header与body之间有一空行(CRLF)
请求方法有:Get, Post, Head, Put, Delete等协议版本1.0、1.1
常用请求头
Accept:浏览器可接受的媒体(MIME)类型;Accept-Language:浏览器所希望的语言种类Accept-Encoding:浏览器能够解码的编码方法,如gzip,deflate等User-Agent:告诉HTTP服务器, 客户端使用的操作系统和浏览器的名称和版本Connection:表示是否需要持久连接,Keep-Alive表示长连接,close表示短连接
2、http response:
status line + header + body (header分为普通报头,响应报头与实体报头)
header与body之间有一空行(CRLF)
状态响应码
1XX 提示信息 - 表示请求已被成功接收,继续处理2XX 成功 - 表示请求已被成功接收,理解,接受3XX 重定向 - 要完成请求必须进行更进一步的处理4XX 客户端错误 - 请求有语法错误或请求无法实现5XX 服务器端错误 - 服务器执行一个有效请求失败
HTTP/1.1 200 OK
Content-Length: 112
Connection: Keep-Alive
Content-Type: text/html
Server: Muduo
1
2 3 4 5 6 7 8 9 10 11 |
class HttpRequest :
public muduo::copyable
{ public: void addHeader( const char *start, const char *colon, const char *end); private: Method method_; // 请求方法 Version version_; // 协议版本1.0/1.1 string path_; // 请求路径 Timestamp receiveTime_; // 请求时间 std::map<string, string> headers_; // header列表 } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class HttpResponse :
public muduo::copyable
{ public: void appendToBuffer(Buffer *output) const; // 将HttpResponse添加到Buffer private: std::map<string, string> headers_; // header列表 HttpStatusCode statusCode_; // 状态响应码 // FIXME: add http version string statusMessage_; // 状态响应码对应的文本信息 bool closeConnection_; // 是否关闭连接 string body_; // 实体 }; |
1
2 3 4 5 6 7 |
class HttpContext : public muduo::copyable { private: HttpRequestParseState state_; // 请求解析状态 HttpRequest request_; // http请求 }; |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/// A simple embeddable HTTP server designed for report status of a program. /// It is not a fully HTTP 1.1 compliant server, but provides minimum features /// that can communicate with HttpClient and Web browser. /// It is synchronous, just like Java Servlet. class HttpServer : boost::noncopyable { public: typedef boost::function < void ( const HttpRequest &, HttpResponse *) > HttpCallback; /// Not thread safe, callback be registered before calling start(). void setHttpCallback( const HttpCallback &cb) { httpCallback_ = cb; } void setThreadNum( int numThreads) { server_.setThreadNum(numThreads); } void start() { server_.start(); } private: void onConnection( const TcpConnectionPtr &conn); void onMessage( const TcpConnectionPtr &conn, Buffer *buf, Timestamp receiveTime); void onRequest( const TcpConnectionPtr &, const HttpRequest &); TcpServer server_; HttpCallback httpCallback_; // 在处理http请求(即调用onRequest)的过程中回调此函数,对请求进行具体的处理 }; |
1
2 3 4 |
server_.setConnectionCallback(
boost::bind(&HttpServer::onConnection, this, _1)); server_.setMessageCallback( boost::bind(&HttpServer::onMessage, this, _1, _2, _3)); |
1
2 3 4 |
if (conn->connected())
{ conn->setContext(HttpContext()); // TcpConnection与一个HttpContext绑定 } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void HttpServer::onMessage(
const TcpConnectionPtr &conn,
Buffer *buf, Timestamp receiveTime) { HttpContext *context = boost::any_cast<HttpContext>(conn->getMutableContext()); if (!detail::parseRequest(buf, context, receiveTime)) { conn->send( "HTTP/1.1 400 Bad Request\r\n\r\n"); conn->shutdown(); } // 请求消息解析完毕 if (context->gotAll()) { onRequest(conn, context->request()); context->reset(); // 本次请求处理完毕,重置HttpContext,适用于长连接(一个连接多次请求) } } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void HttpServer::onRequest(
const TcpConnectionPtr &conn,
const HttpRequest &req)
{ const string &connection = req.getHeader( "Connection"); bool close = connection == "close" || (req.getVersion() == HttpRequest::kHttp10 && connection != "Keep-Alive"); HttpResponse response(close); httpCallback_(req, &response); // 客户代码设置的回调函数,填充response Buffer buf; response.appendToBuffer(&buf); // 将响应填充到buf conn->send(&buf); // 将buf 中的响应发送给客户端 if (response.closeConnection()) { conn->shutdown(); //短连接直接关闭 } } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
#include <muduo/net/http/HttpServer.h>
#include <muduo/net/http/HttpRequest.h> #include <muduo/net/http/HttpResponse.h> #include <muduo/net/EventLoop.h> #include <muduo/base/Logging.h> #include <iostream> #include <map> using namespace muduo; using namespace muduo::net; extern char favicon[ 555]; bool benchmark = false; // 实际的请求处理 void onRequest( const HttpRequest &req, HttpResponse *resp) { std::cout << "Headers " << req.methodString() << " " << req.path() << std::endl; if (!benchmark) { const std::map<string, string> &headers = req.headers(); for (std::map<string, string>::const_iterator it = headers.begin(); it != headers.end(); ++it) { std::cout << it->first << ": " << it->second << std::endl; } } if (req.path() == "/") { resp->setStatusCode(HttpResponse::k200Ok); resp->setStatusMessage( "OK"); resp->setContentType( "text/html"); resp->addHeader( "Server", "Muduo"); string now = Timestamp::now().toFormattedString(); resp->setBody( "<html><head><title>This is title</title></head>" "<body><h1>Hello</h1>Now is " + now + "</body></html>"); } else if (req.path() == "/favicon.ico") { resp->setStatusCode(HttpResponse::k200Ok); resp->setStatusMessage( "OK"); resp->setContentType( "image/png"); resp->setBody(string(favicon, sizeof favicon)); } else if (req.path() == "/hello") { resp->setStatusCode(HttpResponse::k200Ok); resp->setStatusMessage( "OK"); resp->setContentType( "text/plain"); resp->addHeader( "Server", "Muduo"); resp->setBody( "hello, world!\n"); } else { resp->setStatusCode(HttpResponse::k404NotFound); resp->setStatusMessage( "Not Found"); resp->setCloseConnection( true); } } int main( int argc, char *argv[]) { int numThreads = 0; if (argc > 1) { benchmark = true; Logger::setLogLevel(Logger::WARN); numThreads = atoi(argv[ 1]); } EventLoop loop; HttpServer server(&loop, InetAddress( 8000), "dummy"); server.setHttpCallback(onRequest); server.setThreadNum(numThreads); server.start(); loop.loop(); } // 这是一个图片数据 char favicon[ 555] = { ..... }; |