欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析
🌎推荐文章:承接上文【Linux】HTTP协议1
目录
- 👉🏻HTTP方法
- 👉🏻HTTP状态码
- 👉🏻HTTP常见header
- 👉🏻HTTP Cookie
- 👉🏻Http Session
- 👉🏻HTTP协议(版本3,增设响应)
- HttpProtocol.hpp
👉🏻HTTP方法
HTTP(Hypertext Transfer Protocol,超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP之上。它允许客户端向服务器发送请求,并获取服务器响应。通过HTTP,用户可以从网络上获取各种信息,例如网页、图片、音频、视频等。
HTTP定义了一系列的方法,或称为“动作”,用于指定客户端对服务器上资源的操作方式。这些方法包括:
- GET:用于请求服务器发送某个资源。这是最常用的方法之一,常用于请求网页、图片等资源。
- POST:用于向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立或现有资源的修改。
- PUT:用于向指定资源位置上传其最新内容。与POST不同,PUT请求具有幂等性,即多次执行相同的PUT请求,结果应该是一样的。
- DELETE:请求服务器删除指定的页面。
- HEAD:类似于GET请求,但服务器在响应中只返回HTTP头部,不返回实际数据。这种方式常用于获取报文的元数据,如内容类型或长度。
- OPTIONS:返回服务器支持的通信选项。客户端可以对特定的URL使用这个方法,以获取服务器支持的通信选项。
此外,HTTP1.1还新增了其他方法,如PATCH(用于对资源进行部分修改)、TRACE(用于回显服务器收到的请求,主要用于测试或诊断)和CONNECT(HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器)。
HTTP请求和响应都遵循特定的格式,包括请求行、请求头部、请求主体以及响应行、响应头部、响应主体。HTTP也使用状态码来表示请求的处理结果,并支持缓存机制,客户端可以将请求的资源保存到本地,并在下一次请求时直接使用本地资源,减少对服务器的访问。同时,HTTP还可以通过使用SSL/TLS等安全协议来增强数据传输的安全性。
👉🏻HTTP状态码
HTTP状态码是用于表示客户端请求与服务器响应之间的状态的三位数字代码。它们由RFC 2616规范定义,并得到其他相关规范的扩展。HTTP状态码的主要作用是提供有关请求是否成功、出现错误或需要进一步操作的信息,是Web开发中非常重要的一部分。
HTTP状态码主要分为五类:
- 1xx(信息性状态码):表示请求已经接收,继续处理。这一类的状态码是临时的响应,指示客户端应继续其请求或忽略该响应。
- 2xx(成功状态码):表示请求已经被成功接收、理解、并处理。这一类的状态码代表请求已成功被服务器接收、理解,并接受。
- 3xx(重定向状态码):表示请求需要进一步操作,以完成请求。这一类的状态码表示要完成请求,必须进一步执行的动作。
- 4xx(客户端错误状态码):表示客户端的请求有问题,服务器无法处理。这一类的状态码表示请求包含错误语法或无法完成请求。
- 5xx(服务器错误状态码):表示服务器处理请求时出现了错误。这一类的状态码表示服务器在尝试处理请求时发生了错误。
常见的HTTP状态码包括:
- 200 OK:请求成功。
- 404 Not Found:请求的页面不存在。
- 500 Internal Server Error:服务器内部错误。
👉🏻HTTP常见header
HTTP的常见header包括请求头(Request Header)和响应头(Response Header),它们携带了关于HTTP请求和响应的元数据。以下是一些常见的HTTP header字段及其作用的简要说明:
请求头(Request Header):
- Host:指定目标服务器的域名或IP地址。这对于虚拟主机环境中的服务器至关重要,因为它告诉服务器客户端想要访问哪个域名的内容。
- User-Agent:发送请求的用户代理信息,通常是浏览器标识。它告诉服务器关于客户端的一些信息,如浏览器类型、版本和操作系统等。
- Accept:指定客户端可以接受的内容类型。例如,它可以告诉服务器客户端可以接受HTML、JSON或XML等格式的数据。
- Referer:指定当前请求的来源页面URL。它用于跟踪用户从哪个页面导航到当前页面,有助于分析用户行为和流量来源。
- Connection:指定是否保持网络连接。例如,“keep-alive”表示客户端希望保持连接,以便在多个请求之间重用同一个TCP连接,提高性能。
- location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
- Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
响应头(Response Header):
- Content-Type:指定响应体的媒体类型。例如,它可以告诉客户端返回的数据是HTML、图片、JSON或其他类型。
- Content-Length:指定响应体的长度(以字节为单位)。这有助于客户端了解接收到的数据量。
- Cache-Control:指定缓存策略,如缓存的有效期、是否可以缓存等。它帮助客户端和缓存服务器确定如何缓存和重用响应内容。
- Set-Cookie:在客户端设置Cookie。Cookie是一种用于在客户端存储会话信息的机制,可以在多个请求之间共享状态。
👉🏻HTTP Cookie
HTTP的cookie(浏览网页后存储在计算机的缓存文件)是一种在HTTP协议下,服务器或脚本可以维护客户工作站上信息的方式。它是由Web服务器保存在用户浏览器(客户端)上的小文本文件,通常内容会经过加密处理。每当用户链接到服务器时,Web站点都可以访问这些cookie信息,因此,cookie可以被看作是浏览器缓存。
cookie的主要特点包括:
- 创建与存储:cookie是在用户浏览网站时由服务器端创建的,并由用户的浏览器放置在用户的计算机或其他设备上。浏览器负责存储cookie,并在之后的请求中将其发送回服务器。
- 数据格式:cookie的格式通常是以键值对的形式出现,使用分号和空格进行分隔。一个cookie可以包含多个键值对,每个键值对表示一项具体的数据。
- 属性设置:cookie可以设置多种属性,如过期时间(Expires)、可访问路径(Path)、可访问域名(Domain)以及是否只在HTTPS连接中传输(Secure)等。这些属性可以帮助服务器更精细地控制cookie的使用范围和行为。
- 应用场景:cookie的主要应用场景包括
会话管理
、个性化
和跟踪
。例如,通过cookie,服务器可以保持用户的登录状态、记录购物车信息、设置个性化主题等。同时,服务器还可以利用cookie来记录和分析用户行为,以优化服务。
cookie的存在,就解释了为什么HTTP协议虽然说是无状态(即服务器不会保留之前请求的状态信息),是因为这些状态信息被本地浏览器缓存存储,本地浏览器访问服务器时,会将这些信息放在报头中发送给服务端,服务端自然就认识你了
此外,cookie还具有httpOnly属性,可以防止XSS攻击,提供了一定的安全性保障。但需要注意的是,虽然cookie可以增强Web应用程序的功能和用户体验,但如果不当使用或管理不善,也可能带来安全风险,如泄露用户隐私等。因此,在使用cookie时,需要遵循相关的最佳实践和安全规范。
👉🏻Http Session
HTTP的session会话代表客户端与服务器之间的一次交互过程,这个过程可以是连续的,也可以是时断时续的。session机制用于在HTTP请求中识别和区分不同的客户端,并记录它们的状态信息。这对于需要保持用户状态的Web应用程序尤为重要,比如Web登录和购物车等功能。
当用户与服务器交互时,服务器会为用户创建一个session,并分配一个唯一的session ID。这个session ID通常通过两种方式传递给客户端:一种是作为cookie保存在客户端浏览器中,另一种是直接携带在URL中。每次客户端发送请求时,都会带上这个session ID,以便服务器能够识别并关联到对应的session。
在session中,服务器可以保存用户的各种信息,如登录状态、购物车内容等。这些信息在用户的整个会话期间都是有效的,并且可以在多个请求之间共享。因此,通过session机制,服务器能够跟踪和管理用户的会话状态,从而提供个性化的服务和体验。
需要注意的是,session信息需要存储在服务器上,可以是系统文件、数据库或内存等。此外,为了安全性考虑,session ID通常会进行加密处理,以防止被恶意攻击者利用。
session还有一个重要的属性是可过期性。服务器可以设置session的有效期,一旦session过期,服务器将不再识别该session ID,用户需要重新登录或进行其他身份验证操作。这有助于保护用户数据的安全性,并防止未授权的访问。
总之,HTTP的session会话机制为Web应用程序提供了一种有效的手段来跟踪和管理用户的会话状态,从而提供个性化的服务和体验。
在ssesion出来前,cookie的安全性其实非常低,因为如果我的登录账号和密码都存储在cookie中,那么我的http请求信息一旦被黑客截取,那么密码就直接暴露了,账号的安全就得不到保障。
所以现在,基本就是cookie存储的是ssesionID,服务器负责在服务器本地开放一片区域session存储你的登录信息,服务端只要根据你发送来的ssesionID,就可以在服务端找到相关的信息。
👉🏻HTTP协议(版本3,增设响应)
HttpProtocol.hpp
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;
const string HttpSep = "\r\n";
const string homepage = "index.html";
const string wwwroot = "./wwwroot"; // 根目录
class HttpRequest
{
public:
HttpRequest() : _req_blank(HttpSep), _path(wwwroot)
{
}
~HttpRequest()
{
}
bool Getline(string &str, string *line) // 获取请求行的内容
{
// 1.找到内容停止符的位置
auto pos = str.find(HttpSep);
if (pos == string::npos)
return false;
// 2.截取正文
*line = str.substr(0, pos);
// 3.读取完后,将str中已提取的内容清空
str.erase(0, pos + HttpSep.size());
return true;
}
void Deserialize(string &request)
{
// 1.先读请求行
string line;
bool ok = Getline(request, &line);
if (!ok)
return; // 如果读不到\r\n则退出
_req_line = line;
// 2.再读报文每一行的内容
while (true)
{
bool ok = Getline(request, &line); // 再接着往下读取
if (ok && line.empty())
{
// 如果读到了,但是内容为空
// 则剩下的内容为
_req_content = request;
}
else if (ok && !line.empty())
{
_req_header.push_back(line);
}
else
break;
}
}
void ParseReqLine()
{
stringstream ss(_req_line);
ss >> _method >> _url >> _http_version;
if (_url == "/")
{
// 如果是在当前下目录,则直接令它的路径资源终点为index.html
_path += _url;
_path += homepage;
}
else
{
_path += _url;
}
}
void ParseSuffix()
{
auto pos = _path.rfind(".");
if (pos == string::npos)
_suffix = ".html";
else
_suffix = _path.substr(pos);
}
void Parse()
{
// 1.解析请求行
ParseReqLine();
// 2.解析资源后缀
ParseSuffix();
}
void DebugHttp()
{
std::cout << "_req_line" << _req_line << std::endl;
for (auto &line : _req_header)
{
std::cout << "---> " << line << std::endl;
}
std::cout << "_req_blank: " << _req_blank << std::endl;
std::cout << "_req_content: " << _req_content << std::endl;
std::cout << "Method: " << _method << std::endl;
std::cout << "url: " << _url << std::endl;
std::cout << "http_version: " << _http_version << std::endl;
}
// 接下来再整几个返回解析后内容的函数接口
string Url()
{
return _url;
}
string Path()
{
return _path;
}
string Suffix()
{
return _suffix;
}
// 返回正文内容的函数
string GetFileContent(const string &path)
{
// 为了中间不出现字符解码出现问题
// 我们读取文件内容,而是以二进制形式读取
ifstream in(path, std::ios::binary); // 二进制读写
if (!in.is_open())
return "";
in.seekg(0, in.end); // 位置移到文件末尾
int filesize = in.tellg(); // 读取文件当前位置之前的大小
in.seekg(0, in.beg); // 读完大小后回到开头
string content; // 存储读到的二进制内容
content.resize(filesize);
in.read((char *)content.c_str(), filesize);
in.close();
return content;
}
private:
string _req_line; // 请求行
vector<string> _req_header; // 用来存储请求数据中所有的请求行数据
string _req_content; // 报文内容
string _req_blank; // 空行
// 解析之后的内容
string _method; // 请求方法
string _url; // 资源路径
string _http_version; // http版本
string _path; // 资源的具体文件路径位置,path包含在url中,所以需要从url中解析
string _suffix; // 请求资源的后缀
};
const string BlankSep = " ";
const string LineSep = "\r\n";
class HttpResponse
{
public:
HttpResponse()
: _http_version("HTTP/1.0"), _status_code(200), _status_code_desc("OK"),
_resp_blank(LineSep)
{
}
~HttpResponse() {}
void SetCode(int code)
{
_status_code = code;
}
void SetDesc(const string &desc)
{
_status_code_desc = desc;
}
void SetContent(const string &content) // 设置正文内容
{
_resp_content = content;
}
void MakeStatusLine() // 组合成状态行
{
_status_line = _http_version + BlankSep + to_string(_status_code) + BlankSep + _status_code_desc + LineSep;
}
void AddHeader(string &header) // 添加报头内容
{
_resp_header.push_back(header);
}
// 接下来将以上信息全部整合序列化
string Serialize()
{
string response_str = _status_line;
for (auto &header : _resp_header)
{
response_str += header;
}
response_str += _resp_blank;
response_str += _resp_content;
return response_str;
}
private:
string _status_line; // 状态栏
vector<string> _resp_header; // 报头内容
string _resp_blank; // 空行
string _resp_content; // 正文
// 接下来是要组成状态栏的部分:版本,状态码,状态描述码
string _http_version;
int _status_code;
string _status_code_desc;
};
如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长