HTTP协议

请求和响应


看看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


状态码

1XXInformational(信息性状态码)接收的请求正在处理
2XXSucess(成功状态码)请求正常处理完毕
3XXRedirection(重定向状态码)需要进行附加操作以完成操作
4XXClient Error(客户端错误状态码)服务器无法处理请求
5XXServer 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的全部内容;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值