请求和响应
看看http请求
我们先来看一个简单的http请求,这里用浏览器作为客户端;
main.cpp
// ./tcpserver 8888
int main(int argc, char *argv[])
{
if (argc != 2)
{
std::cerr << "Usage: " << argv[0] << " local-port" << std::endl;
exit(0);
}
uint16_t port = std::stoi(argv[1]);
HttpServer hserver;
std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(
std::bind(&HttpServer::HandlerHttpRequest, &hserver, std::placeholders::_1),
port
);
tsvr->loop();
return 0;
}
TcpServer.hpp
void loop()
{
_isrun = true;
while (_isrun)
{
// 获取新连接
InetAddr client;
SockSptr newsock = _listensockfd->Accepter(&client);
if (newsock == nullptr)
continue;
LOG(INFO, "get a new link, client info : %s\n", client.Addrstr().c_str());
// version 2 多线程版本 ---不能关闭fd,因为是共享的
pthread_t tid;
ThreadDate *td = new ThreadDate(newsock, this, client);
pthread_create(&tid, nullptr, Execute, td); // 新线程分离
}
_isrun = false;
}
static void *Execute(void *args)
{
pthread_detach(pthread_self());
ThreadDate *td = static_cast<ThreadDate *>(args);
std::string requeststr;
ssize_t n = td->_sockfd->Recv(&requeststr);
if (n > 0)
{
string responsestr = td->_self->_service(requeststr);
td->_sockfd->Send(responsestr);
}
td->_sockfd->Close();
delete td;
return nullptr;
}
http.hpp
string HandlerHttpRequest(string &reqstr)
{
cout << "---------------------------" << endl;
cout << reqstr;
string responsestr = "HTTP/1.1 200 ok\r\n";
responsestr += "Content-Typr: text/html\r\n";
responsestr += "\r\n";
responsestr += "<html><h1>hello Linux!</h1><html>";
return responsestr;
}
运行结果:
http请求
宏观格式:
有了宏观格式,我们在http.hpp中定义一个HttpResquest协议其结构化字段:
// 基本的httpresquest的格式
string _req_line; // 请求行
vector<string> _req_headers; // 请求报头
string _blank_line; // 空行
string _body_test; // 正文
// 更具体的属性字段,需要进一步的反序列化
string _method;
string _url;
string _path;
string _version;
string _suffix; // 资源后缀
unordered_map<string, string> _headers_kv;
反序列化
我们看一下刚才我们运行代码得到的http请求,虽然看着不是一行字符串,其实它只不过是被\r\n分开了;
http请求就是一个长长的字符串,说明这个请求曾经被客户端序列化过!!!
当我们收到这个请求后,接下来就要对她进行反序列化:
// 反序列化
void Deserialize(string &reqstr)
{
// 基本的反序列化
_req_line = GetLine(reqstr);//请求行
string header;
do
{
header = GetLine(reqstr);
if (header.empty())
break;
else if (header == base_sep)
break;
_req_headers.push_back(header);//添加到请求报头
} while (true);
if (!reqstr.empty())
{
_body_test = reqstr;//正文
}
// 进一步反序列化
ParseReqLine();//将请求行进一步反序列化
ParseReqHeader();//将请求报头进一步反序列化
}
这样我们就完成的接受到请求的处理;
http响应
宏观格式:
和请求一样,我们的响应也要有定义一个协议:Httpresponse协议,其结构化字段:
// httpresponse base属性
string _version; // 版本
int statuc_code; // 状态码
string _desc; //状态码描述
unordered_map<string, string> _headers_kv;
// 基本的httpresquest的格式
string _status_line; // 状态行
vector<string> _resp_headers; // 响应报头
string _blank_line; // 空行
string _resp_body_test; // 正文
序列化
string Seserialize()
{
// 1、构建报头
_status_line = _version + spacesep + to_string(statuc_code) + spacesep + _desc + base_sep;
// 2、构建响应报头
for (auto &header : _headers_kv)
{
string header_line = header.first + line_sep + header.second + base_sep;
_resp_headers.push_back(header_line);
}
// 3、正文
// 4、正式序列化
string responsestr = _status_line;
for (auto &line : _resp_headers)
{
responsestr += line;
}
responsestr += _blank_line;
responsestr += _resp_body_test;
return responsestr;
}
fiddler
状态码
1XX | Informational(信息性状态码) | 接收的请求正在处理 |
2XX | Sucess(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成操作 |
4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定
向), 504(Bad Gateway)
3XX
301永久重定向,302临时重定向
HTTP 状态码 301(永久重定向) 和 302(临时重定向) 都依赖 Location 选项;
HTTP 状态码 301(永久重定向) :
- 当服务器返回 HTTP 301 状态码时, 表示请求的资源已经被永久移动到新的位置。
- 在这种情况下, 服务器会在响应中添加一个 Location 头部, 用于指定资源的新位置。 这个 Location 头部包含了新的 URL 地址, 浏览器会自动重定向到该地址;
在 HTTP 响应中, 可能会看到类似于以下的头部信息:
HTTP/1.1 301 Moved Permanently\r\n
Location: https://www.new-url.com\r\n
HTTP 状态码 302(临时重定向) :
- 当服务器返回 HTTP 302 状态码时, 表示请求的资源临时被移动到新的位置。
- 同样地, 服务器也会在响应中添加一个 Location 头部来指定资源的新位置。 浏览器会暂时使用新的 URL 进行后续的请求, 但不会缓存这个重定向。
例如, 在 HTTP 响应中, 可能会看到类似于以下的头部信息:
HTTP/1.1 302 Found\r\n
Location: https://www.new-url.com\r\n
无论是 HTTP 301 还是 HTTP 302 重定向, 都需要依赖 Location 选项来指定资
源的新位置。 这个 Location 选项是一个标准的 HTTP 响应头部, 用于告诉浏览器应该
将请求重定向到哪个新的 URL 地址;
重定向代码:
if (req.Path() == "wwwroot/redir")
{
string redir_path = "https://www.qq.com";
resp.AddCode(301, _code_to_desc[301]);
resp.AddHeader("Location", redir_path);
}
http方法
http请求方法:method
最常用的就是 GET 方法和 POST 方法;
GET | 获取资源 |
POST | 传输实体主体 |
GET一般用来获取静态资源,也可以通过url向我们的服务器传递参数;
POST可以通过 httpprequest 正文来进行参数传递;
- version 1:Postman
- version 2:form表单完成get or post 请求
将POST改成GET:
怎么把url的参数转移到正文:
通过对比:POST方法,比GET方法传参更私密,但是都不安全!!!------->对http参数进行部分加密 ------->https
我们看一下在.html中的action是什么?
其实就是提供的服务方法;
在代码中实现login服务:
在http.hpp中要有服务列表,然后对服务有判断是否在服务列表中,将服务插入到服务列表中;
这个服务要在main.cpp中实现的话,我们就要调用回调函数;
http.hpp:
using func_t =std::function<HttpResponse(HttpResquest &)>;//回调函数
std::unordered_map<std::string,func_t> _service_list;//服务列表
void InsertService(const string servicename,func_t f)//插入服务
{
string s =prefixpath+servicename;
_service_list[s]=f;
}
bool IsService(const string &servicename)//判断服务是否存在
{
auto iter=_service_list.find(servicename);
if(iter==_service_list.end())return false;
return true;
}
main.cpp:
//想要支持登录功能:
hserver.InsertService("/login",Login);
HttpResponse Login(HttpResquest& req)//实现登录功能
{
HttpResponse resp;
req.GetRequestBody();
resp.AddCode(200,"OK");
resp.AddBodyText("<html><h1>result</h1></html>");
return resp;
}
Cookie
resp.AddHeader("Set-Cookie","username=shhh");
resp.AddHeader("Set-Cookie","passwd=12345");
以上就是http的全部内容;