HTTP请求包含: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表示短连接
GET / HTTP/1.1
Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Tablet PC 2.0)
Accept-Encoding: gzip, deflate
Host: 192.168.159.188:8000
Connection: Keep-Alive
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
This is title Hello
Now is 20130611 02:14:31.518462
#ifndef MUDUO_NET_HTTP_HTTPREQUEST_H
#define MUDUO_NET_HTTP_HTTPREQUEST_H
#include
#include
#include
#include
#ifndef MUDUO_NET_HTTP_HTTPREQUEST_H
#define MUDUO_NET_HTTP_HTTPREQUEST_H
#include
#include
#include
#include
#ifndef MUDUO_NET_HTTP_HTTPCONTEXT_H
#define MUDUO_NET_HTTP_HTTPCONTEXT_H
#include
#include
namespace muduo
{
namespace net
{
class HttpContext : public muduo::copyable
{
public:
enum HttpRequestParseState
{
kExpectRequestLine,
kExpectHeaders,
kExpectBody,
kGotAll,
};
HttpContext()
: state_(kExpectRequestLine)
{
}
// default copy-ctor, dtor and assignment are fine
bool expectRequestLine() const
{ return state_ == kExpectRequestLine; }
bool expectHeaders() const
{ return state_ == kExpectHeaders; }
bool expectBody() const
{ return state_ == kExpectBody; }
bool gotAll() const
{ return state_ == kGotAll; }
void receiveRequestLine()
{ state_ = kExpectHeaders; }
void receiveHeaders()
{ state_ = kGotAll; } // FIXME
// 重置HttpContext状态
void reset()
{
state_ = kExpectRequestLine;
HttpRequest dummy;
request_.swap(dummy);
}
const HttpRequest& request() const
{ return request_; }
HttpRequest& request()
{ return request_; }
private:
HttpRequestParseState state_; // 请求解析状态
HttpRequest request_; // http请求
};
}
}
#endif // MUDUO_NET_HTTP_HTTPCONTEXT_H
#ifndef MUDUO_NET_HTTP_HTTPSERVER_H
#define MUDUO_NET_HTTP_HTTPSERVER_H
#include
#include
namespace muduo
{
namespace net
{
class HttpRequest;
class HttpResponse;
/// 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 HttpCallback;
HttpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& name);
~HttpServer(); // force out-line dtor, for scoped_ptr members.
/// 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();
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)的过程中回调此函数,对请求进行具体的处理
};
}
}
#endif // MUDUO_NET_HTTP_HTTPSERVER_H
#include
#include
#include
#include
#include
#include
using namespace muduo;
using namespace muduo::net;
namespace muduo
{
namespace net
{
namespace detail
{
// FIXME: move to HttpContext class
bool processRequestLine(const char* begin, const char* end, HttpContext* context)
{
bool succeed = false;
const char* start = begin;
const char* space = std::find(start, end, ' ');
HttpRequest& request = context->request();
if (space != end && request.setMethod(start, space)) // 解析请求方法
{
start = space+1;
space = std::find(start, end, ' ');
if (space != end)
{
request.setPath(start, space); // 解析PATH
start = space+1;
succeed = end-start == 8 && std::equal(start, end-1, "HTTP/1.");
if (succeed)
{
if (*(end-1) == '1')
{
request.setVersion(HttpRequest::kHttp11); // HTTP/1.1
}
else if (*(end-1) == '0')
{
request.setVersion(HttpRequest::kHttp10); // HTTP/1.0
}
else
{
succeed = false;
}
}
}
}
return succeed;
}
// FIXME: move to HttpContext class
// return false if any error
bool parseRequest(Buffer* buf, HttpContext* context, Timestamp receiveTime)
{
bool ok = true;
bool hasMore = true;
while (hasMore)
{
if (context->expectRequestLine()) // 处于解析请求行状态
{
const char* crlf = buf->findCRLF();
if (crlf)
{
ok = processRequestLine(buf->peek(), crlf, context); // 解析请求行
if (ok)
{
context->request().setReceiveTime(receiveTime); // 设置请求时间
buf->retrieveUntil(crlf + 2); // 将请求行从buf中取回,包括\r\n
context->receiveRequestLine(); // 将HttpContext状态改为kExpectHeaders
}
else
{
hasMore = false;
}
}
else
{
hasMore = false;
}
}
else if (context->expectHeaders()) // 解析header
{
const char* crlf = buf->findCRLF();
if (crlf)
{
const char* colon = std::find(buf->peek(), crlf, ':'); //冒号所在位置
if (colon != crlf)
{
context->request().addHeader(buf->peek(), colon, crlf);
}
else
{
// empty line, end of header
context->receiveHeaders(); // HttpContext将状态改为kGotAll
hasMore = !context->gotAll();
}
buf->retrieveUntil(crlf + 2); // 将header从buf中取回,包括\r\n
}
else
{
hasMore = false;
}
}
else if (context->expectBody()) // 当前还不支持请求中带body
{
// FIXME:
}
}
return ok;
}
void defaultHttpCallback(const HttpRequest&, HttpResponse* resp)
{
resp->setStatusCode(HttpResponse::k404NotFound);
resp->setStatusMessage("Not Found");
resp->setCloseConnection(true);
}
}
}
}
HttpServer::HttpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& name)
: server_(loop, listenAddr, name),
httpCallback_(detail::defaultHttpCallback)
{
server_.setConnectionCallback(
boost::bind(&HttpServer::onConnection, this, _1));
server_.setMessageCallback(
boost::bind(&HttpServer::onMessage, this, _1, _2, _3));
}
HttpServer::~HttpServer()
{
}
void HttpServer::start()
{
LOG_WARN << "HttpServer[" << server_.name()
<< "] starts listenning on " << server_.hostport();
server_.start();
}
void HttpServer::onConnection(const TcpConnectionPtr& conn)
{
if (conn->connected())
{
conn->setContext(HttpContext()); // TcpConnection与一个HttpContext绑定
}
}
void HttpServer::onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp receiveTime)
{
HttpContext* context = boost::any_cast(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,适用于长连接
}
}
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);
Buffer buf;
response.appendToBuffer(&buf);
conn->send(&buf);
if (response.closeConnection())
{
conn->shutdown();
}
}