HTTPcookie与session实现

1.HTTP Cookie

定义

HTTP Cookie (也称为 Web Cookie 、浏览器 Cookie 或简称 Cookie )是服务器发送到
用户浏览器并保存在浏览器上的一小块数据,它会在浏览器之后向同一服务器再次发
起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一
浏览器,如保持用户的登录状态、记录用户偏好等。
工作原理
用户第一次访问网站时,服务器会在响应的HTTP头中设置Set-Cookie字段,发送Cookie到用户的浏览器上,浏览器接收到cookie后,会保存到本地,之后的请求中,浏览器会自动在HTTP请求头中携带Cookie字段,将之前保存的Cookie信息发送到服务器。
分类
会话Cookie(Session Cookie):在浏览器关闭时失效
持久Cookie(Persistent Cookie):带有明确的过期日期或者持续时间,可以跨多个浏览器会话存在。
如果是一个持久的cookie,其实就是浏览器相关的,特定目录下的一个文件,但是无法直接查看文件,因为cookie文件一般时以二进制或者sqlite格式存储,查看就需要到浏览器对应的cookie中查看。

认识cookie

HTTP存在一个报头选项:Set-cookie,可以用来进行给浏览器设置cookie值。

在HTTP响应头中添加,客服端获取并自行设置并保存。

完整的Set-Cookie实例

C++
Set-Cookie: username=peter; expires=Thu, 18 Dec 2024 12:00:00
UTC; path=/; domain=.example.com; secure; HttpOnly
时间格式必须遵守 RFC 1123 标准,具体格式样例: Tue, 01 Jan 2030 12:34:56
GMT 或者 UTC( 推荐 )
关于时间解释
Tue : 星期二(星期几的缩写)
, : 逗号分隔符
01 : 日期(两位数表示)
Jan : 一月(月份的缩写)
2030 : 年份(四位数)
12:34:56 : 时间(小时、分钟、秒)
GMT : 格林威治标准时间(时区缩写)
区别:
计算方式: GMT 基于地球的自转和公转,而 UTC 基于原子钟。
准确度:由于 UTC 基于原子钟,它比基于地球自转的 GMT 更加精确。
在实际使用中, GMT UTC 之间的差别通常很小,大多数情况下可以互换使用。但
在需要高精度时间计量的场合,如科学研究、网络通信等, UTC 是更为准确的选择。
关于其他可选属性的解释
expires=<date> [ 要验证 ] :设置 Cookie 的过期日期 / 时间。如果未指定此属
性,则 Cookie 默认为会话 Cookie ,即当浏览器关闭时过期。
path=<some_path> [ 要验证 ] :限制 Cookie 发送到服务器的哪些路径。默认
为设置它的路径。
domain=<domain_name> [ 了解即可 ] :指定哪些主机可以接受该 Cookie 。默
认为设置它的主机。
secure [ 了解即可 ] :仅当使用 HTTPS 协议时才发送 Cookie 。这有助于防止
Cookie 在不安全的 HTTP 连接中被截获。
HttpOnly [ 了解即可 ] :标记 Cookie HttpOnly ,意味着该 Cookie 不能被
客户端脚本(如 JavaScript )访问。这有助于防止跨站脚本攻击( XSS )。

2.HTTP Session

定义
HTTP Session 是服务器用来跟踪用户与服务器交互期间用户状态的机制。由于 HTTP
协议是无状态的(每个请求都是独立的),因此服务器需要通过 Session 来记住用户
的信息。(无状态是无法记录历史访问)
工作原理
用户首次访问网站时,服务器会为用户创建一个唯一的Session ID,通过Cookie将其发送到客服端。客服端在之后的请求都会携带这个Session ID,服务区通过Session ID来标识用户,从而获取用户的会话信息。服务器通常会将Session信息存储在内存,数据库或缓存中。
安全性:
Cookie 相似,由于 Session ID 是在客户端和服务器之间传递的,因此也存 在被窃取的风险。
但是一般虽然 Cookie 被盗取了,但是用户只泄漏了一个 Session ID ,私密信息暂时没有被泄露的风险。Session ID 便于服务端进行客户端有效性的管理,比如异地登录。 可以通过 HTTPS 和设置合适的 Cookie 属性(如 HttpOnly Secure )来增强安全性。
没有session的风险

 没有Session时的隐私风险

如果没有Session机制,Web应用可能需要依赖其他方式(如Cookie)来跟踪用户状态。Cookie存储在客户端,容易被恶意程序获取和篡改,从而导致隐私泄露。此外,没有Session的加密通信工具可能无法提供端到端加密或去中心化存储,从而增加数据被窃取或监控的风险。

有session好处
  • 数据存储位置:Session数据存储在服务器端,而不是客户端。即使客户端的Session ID被窃取,攻击者也无法直接获取到存储在服务器上的会话数据。

  • 安全性:服务器可以更好地控制Session的生命周期,例如定期更新Session ID、限制Session的使用范围等,从而降低被攻击的风险。

补充
favicon.ico是一个网站图标,通常显示浏览器的标签页上,地址栏旁边或者收藏夹中。

3.cookie实现

HttpProtocol.hpp文件

第一个类是实现网络请求的,GetLine函数用来截取一行,以HttpSep(\r\n)来作为分隔符,则0到pos位置就是一行的内容了,把读取到的内容在删除掉,读取下一行内容。Deserialize函数是序列化,request是网络请求,用getline函数获取一行出来,第一行是请求行,单独存储起来,然后就是一直死循环读取剩下的报头数据,直到读到了空行,如果是空行的话ok为true且empty也会true则就会把剩下的内容全都放到req_content里,这里就是文本内容了,也是要单独存储的,empty不为空就说明还是在读取报头信息,就把报头信息放到vector里面存储,这里是把键和值一起放到string里面的。

#pragma once

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <memory>
#include <ctime>
#include "TcpServer.hpp"

const std::string HttpSep = "\r\n";
// 可以配置的
const std::string homepage = "index.html";
const std::string wwwroot = "./wwwroot";



class HttpRequest
{
public:
    HttpRequest():_req_blank(HttpSep),_path(wwwroot)
    {}

    bool GetLine(std::string& str,std::string* line)
    {
        auto pos=str.find(HttpSep);
        if(pos==std::string::npos)
            return false;
        *line=str.substr(0,pos);
        str.erase(0,pos+HttpSep.size());
        return true;
    }
    bool Deserialize(std::string& request)
    {
        std::string line;
        bool ok=GetLine(request,&line);
        if(!ok)
            return false;
        _req_line=line;
        while(true)
        {
            bool ok=GetLine(request,&line);

            if(ok&&line.empty())
            {
                _req_content=request;
                break;
            }
            else if(ok&&!line.empty())
            {
                _req_header.push_back(line);
            }
            else
                break;


        }
        return true;
    }

    void DebugHttp()
    {
        std::cout<<"_req_line:"<<_req_line<<std::endl;
        for(auto& line:_req_header)
        {
            std::cout<<"--->"<<line<<std::endl;

        }

    }

    ~HttpRequest()
    {}
private:
    std::string _req_line;
    std::vector<std::string> _req_header;
    std::string _req_blank;
    std::string _req_content;

    std::string _method;
    std::string _url;
    std::string _http_version;
    std::string _path;
    std::string _suffix;
};

 第二个代码是网络响应的,构造函数设置网络版本和状态码和状态信息。SetCode函数设置状态码的值,SetDesc函数设置状态信息,MakeStatusLine函数构造状态行,网络版本+空格号+状态码+空格号+状态信息+换行拼接在一起。AddHeader函数把报头信息+换行符插入到vector中。AddContent函数设置文内容。Serialize函数先构造出状态行,然后把报头信息一一取出拼接在状态行的后面,for结束后标识状态行和报头信息已经填好了,接下来就是空行和文本内容的填充,把填充完整的响应返回。

const std::string BlankSep=" ";
const std::string LineSep="\r\n";

class HttpResponse
{
public:
    HttpResponse():_http_version("HTTP/1.0"),_status_code(200),_status_code_desc("OK"),_resp_blank(LineSep)
    {

    }

    void SetCode(int code)
    {
        _status_code=code;
    }
    void SetDesc(const std::string& desc)
    {
        _status_code_desc=desc;
    }
    void MakeStatusLine()
    {
        _status_line=_http_version+BlankSep+std::to_string(_status_code)+BlankSep+_status_code_desc+LineSep;
    }

    void AddHeader(const std::string& header)
    {
        _resp_header.push_back(header+LineSep);
    }
    void AddContent(const std::string& content)
    {
        _resp_content=content;
    }
    std::string Serialize()
    {
        MakeStatusLine();
        std::string response_str=_status_line;
        for(auto& header:_resp_header)
        {
            response_str+=header;
        }
        response_str+=_resp_blank;
        response_str+=_resp_content;
        return response_str;

    }
    ~HttpResponse()
    {}

private:
    std::string _status_line;
    std::vector<std::string> _resp_header;
    std::string _resp_blank;
    std::string _resp_content;

    std::string _http_version;
    int _status_code;
    std::string _status_code_desc;

};

这个类就是网络处理部分。构造函数要接收一个端口号,make_unique构造一个TcpServer对象出来,参数是端口号和一个bind对象,再调用Init函数。ProveCookieWrite函数和ProveCookieTimeOut函数都是添加报头信息,一个是用户名一个是用户名和过期时间(这里设置一分钟),ProvePath函数添加了路径,ProveOtherCookie添加了密码。HandlerHttp函数就是处理部分了,创建请求对象,调用请求的反序列化方法,显示报头信息,创建响应对象,设置状态码,状态信息,添加报头内容,添加文本内容helloworld(前端格式写的,最后网页访问会看到helloworld),最后返回完整的且序列化后的信息。

class Http
{
    std::string GetMonthName(int month)
    {
        std::vector<std::string> months={"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
        return months[month];
    }
    std::string GetWeekDayName(int day)
    {
        std::vector<std::string> weekdays={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
        return weekdays[day];
    }

    std::string ExpireTimeUseRfc1123(int t) // 秒级别的未来UTC时间
    {
        time_t timeout = time(nullptr) + t;
        struct tm *tm = gmtime(&timeout); // 这里不能用localtime,因为localtime是默认带了时区的. gmtime获取的就是UTC统一时间
        char timebuffer[1024];
        //时间格式如: expires=Thu, 18 Dec 2024 12:00:00 UTC
        snprintf(timebuffer, sizeof(timebuffer), "%s, %02d %s %d %02d:%02d:%02d UTC", 
            GetWeekDayName(tm->tm_wday).c_str(),
            tm->tm_mday,
            GetMonthName(tm->tm_mon).c_str(),
            tm->tm_year+1900,
            tm->tm_hour,
            tm->tm_min,
            tm->tm_sec
        );
        return timebuffer;
    }
public:
    Http(uint16_t port)
    {
        _tsvr=std::make_unique<TcpServer>(port,std::bind(&Http::HandlerHttp,this,std::placeholders::_1));
        _tsvr->Init();
    }
    std::string ProveCookieWrite()
    {
        return "Set-Cookie: username=zhangsan;";
    }
    std::string ProveCookieTimeOut()
    {
        return "Set-Cookie: username=zhangsan;expires="+ExpireTimeUseRfc1123(60)+";";

    }
    std::string ProvePath()
    {

        return "Set-Cookie: username=zhangsan; path=/a/b";
    }
    std::string ProveOtherCookie()
    {
        return "Set-Cookie: passwd=1234567890; path=/a/b;";
    }
    std::string HandlerHttp(std::string request)
    {
        HttpRequest req;
        req.Deserialize(request);
        req.DebugHttp();
        lg.LogMessage(Debug,"%s\n",ExpireTimeUseRfc1123(60).c_str());
        HttpResponse resp;
        resp.SetCode(200);
        resp.SetCode(200);
        resp.SetDesc("OK");
        resp.AddHeader("Content-Type: text/html");
        resp.AddHeader(ProvePath());
        resp.AddHeader(ProveOtherCookie());
        resp.AddContent("<html><h1>helloworld</h1></html>");
        return resp.Serialize();
    }

    void Run()
    {
        _tsvr->Start();
    }
    ~Http()
    {}
private:
    std::unique_ptr<TcpServer> _tsvr;
};

TcpServer.hpp文件

构造函数接收端口号和回调函数,以及套接字的值初始化等。ProcessConnection函数参数为Socket对象和InetAddr对象,调用sock方法recv在创建的套接字中读取内容,调用handler处理读取到的信息,把响应信息发送回去,关闭套接字(关闭后就不能传入命令了 telnet指令)。Start函数则会调用AcceptConnection函数进行连接(Tcp协议需要双方连接才能通信),创建task_t对象赋值为bind对象,bind是第一个是函数地址,但是类的话不是这样,类函数需要&来获取函数地址,后面的都是参数,而类函数还需要传递this指针,因为类函数隐式参数this。接着再创建实例并调用插入函数把task插入进去。

#pragma once

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <functional>
#include <memory>
#include "ThreadPool.hpp"
#include "Log.hpp"
#include "Comm.hpp"
#include "Socket.hpp"
#include "InetAddr.hpp"


using namespace Net_Work;
const static int default_backlog = 6;
using task_t = std::function<void()>;
using handler_t = std::function<std::string(std::string)>;


class TcpServer
{
public:
    TcpServer(uint16_t port,handler_t handler):_port(port),_isrunning(false),_listensock(new TcpSocket()),_handler(handler)
    {}

    void Init()
    {
        _listensock->BuildListenSocketMethod(_port,default_backlog);

    }

    void ProcessConnection(std::shared_ptr<Socket> sock,InetAddr addr)
    {
        std::string request_str;
        if(sock->Recv(&request_str,4096))
        {
            std::string response=_handler(request_str);
            sock->Send(response);
            sock->CloseSocket();
        }
    }

    void Start()
    {
        _isrunning=true;
        while(_isrunning)
        {
            std::string clientip;
            uint16_t clientport;
            std::shared_ptr<Socket> sock=_listensock->AcceptConnection(&clientip,&clientport);
            if(sock==nullptr)
            {
                continue;
            }
            InetAddr addr(clientip,clientport);
            task_t task=std::bind(&TcpServer::ProcessConnection,this,sock,addr);
            ThreadPool<task_t>::GetInstance()->Push(task);
        }
        _isrunning=false;
    }
    ~TcpServer()
    {}
private:
    uint16_t _port;
    std::unique_ptr<Socket> _listensock;
    bool _isrunning;
    handler_t _handler;
};

成员函数与非成员函数的区别

  • 非成员函数 :对于非成员函数(即全局函数或命名空间内的函数),函数名本身可以直接作为函数的指针。例如,对于一个非成员函数 void foo(),可以将 foo 作为函数指针使用。

  • 成员函数 :成员函数属于类的一部分,它们的调用需要一个对象上下文(即通过对象来调用)。因此,要获取成员函数的地址,必须使用 & 操作符。例如,对于类 A 中的成员函数 void bar(),要获取其地址,必须写成 &A::bar

Main.cc文件

#include "HttpProtocol.hpp"
#include <memory>

using namespace std;

void Usage(std::string proc)
{
    std::cout<<"Usage: \n\t"<<proc<<" local_port\n"<<std::endl;
}

int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        Usage(argv[0]);
        return Usage_Err;

    }
    uint16_t port=stoi(argv[1]);
    std::unique_ptr<Http> http=make_unique<Http>(port);
    http->Run();
    return 0;
}

ThreadPool.hpp文件

#pragma once

#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"

static const int defaultnum=5;

class ThreadData
{
public:
    ThreadData(const std::string& name):threadname(name)
    {}
    ~ThreadData()
    {}
public:
    std::string threadname;
};


template<class T>
class ThreadPool
{
private:
    ThreadPool(int thread_num=defaultnum):_thread_num(thread_num)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_cond,nullptr);
        for(int i=0;i<_thread_num;i++)
        {
            std::string threadname="thread-";
            threadname+=std::to_string(i+1);
            ThreadData td(threadname);
            _threads.emplace_back(threadname,std::bind(&ThreadPool<T>::ThreadRun,this,std::placeholders::_1),td);
            lg.LogMessage(Info,"%s is created...\n", threadname.c_str());

        }
    }

    ThreadPool(const ThreadPool<T>& tp)=delete;
    const ThreadPool<T>& operator=(const ThreadPool<T>)=delete;


public:
    static ThreadPool<T>* GetInstance()
    {
        if(instance==nullptr)
        {
            LockGuard lockguard(&sig_lock);
            if(instance==nullptr)
            {
                 lg.LogMessage(Info, "创建单例成功...\n");
                 instance =new ThreadPool<T>();
                 instance->Start();
            }
        }
        return instance;
    }
    bool Start()
    {
        for(auto& thread:_threads)
        {
            thread.Start();
            lg.LogMessage(Info, "%s is running ...\n", thread.ThreadName().c_str());
        }
        return true;
    }

    void ThreadWait(const ThreadData& td)
    {
        lg.LogMessage(Debug, "no task, %s is sleeping...\n", td.threadname.c_str());
        pthread_cond_wait(&_cond,&_mutex);

    }
    void ThreadWakeup()
    {
        pthread_cond_signal(&_cond);
    }
    void checkSelf()
    {

    }
    void ThreadRun(ThreadData &td)
    {
        while(true)
        {
            T t;
            {
                LockGuard lockguard(&_mutex);
                while(_q.empty())
                {
                    ThreadWait(td);
                    lg.LogMessage(Debug, "thread %s is wakeup\n", td.threadname.c_str());

                }
                t=_q.front();
                _q.pop();
            }
            t();
        }

    }
    void Push(T& in)
    {
        LockGuard Lockguard(&_mutex);
        _q.push(in);
        ThreadWakeup();
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
    void Wait()
    {
        for(auto& thread: _threads)
        {
            thread.Join();
        }
    }
private:
    std::queue<T> _q;
    std::vector<Thread<ThreadData>> _threads;
    int _thread_num;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
    static ThreadPool<T>* instance;
    static pthread_mutex_t sig_lock;

};
template <class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;

thread.hpp文件

#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>

// 设计方的视角
//typedef std::function<void()> func_t;
template<class T>
using func_t = std::function<void(T&)>;

template<class T>
class Thread
{
public:
    Thread(const std::string &threadname, func_t<T> func, T &data)
    :_tid(0), _threadname(threadname), _isrunning(false), _func(func), _data(data)
    {}

    static void *ThreadRoutine(void *args) // 类内方法,
    {
        // (void)args; // 仅仅是为了防止编译器有告警
        Thread *ts = static_cast<Thread *>(args);

        ts->_func(ts->_data);

        return nullptr;
    }

    bool Start()
    {
        int n = pthread_create(&_tid, nullptr, ThreadRoutine, this/*?*/);
        if(n == 0) 
        {
            _isrunning = true;
            return true;
        }
        else return false;
    }
    bool Join()
    {
        if(!_isrunning) return true;
        int n = pthread_join(_tid, nullptr);
        if(n == 0)
        {
            _isrunning = false;
            return true;
        }
        return false;
    }
    std::string ThreadName()
    {
        return _threadname;
    }
    bool IsRunning()
    {
        return _isrunning;
    }
    ~Thread()
    {}
private:
    pthread_t _tid;
    std::string _threadname;
    bool _isrunning;
    func_t<T> _func;
    T _data;
};

代码流程

main中unique_ptr创建了Http对象的智能指针,就会调用Http类的构造函数,会给Http的tsvr赋值上一个make出来的TcpServer对象,参数是端口号和bind对象,Tcp类也会调用构造函数确定了端口号和回调函数handler,接着调用init函数,里面会调用BuildListenSocketMethod创建套接字并初始化。这时走完了main的第20行代码,接着21调用http的run函数,里面调用tsvr的Start函数,又到了Tcpserver部分,这里就会开始连接然后调用GetInstacnce函数,此时会跳到threadPool部分,执行threadpool的构造函数,锁和条件变量初始化,然后开始往_threads里面插入线程名字和bind对象(ThreadRun函数),因为_thread<Thread<ThreadData>>,所以就会调用thread构造函数,确定了回调函数是Threadrun函数,然后再继续执行GetInstance函数,开辟空间调用线程池的Start函数,start函数会调用所有线程的start函数(thread.start()),就会跳到thread部分,这里就会创建线程执行routine函数,然后调用回调函数threadrun,参数是线程对象ThreadData型,就会回到线程池部分,run会一直检查任务队列是否为空,为空就把这个线程放到条件变量中,不为空就取出任务队列并执行,这里是全部到条件变量中等待,还没有插入。此时才到Push(task)地方,开始插入任务,然后唤醒线程,唤醒的线程就会执行routine函数走回调函数func(Threadrun函数),run函数while队列不是空,就可以取出任务执行了,而这个任务就是httphandler函数,处理请求,构造响应返回。

4.session实现

HttpProcotol.hpp文件

请求类方法实现,GetLine把套接字读取的内容进行行划分,以间隔号作为基准划分多行。Parse函数把提取的行进行分离,stringstream字符串流,会以空格号为间隔把方法,uri和版本号提取出来存储在成员变量中,接着定义prefix,然后范围for遍历报头信息,如果报头信息中有与prefix一样的字符串就把跟prefix一样的部分之后的地方提取出来存储在cookie中,在插入到vector中管理,接着改变prefix值,在来一次,这次找的就是sessionid=,会提取=后面的值存储到vector中管理。

#pragma once

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <memory>
#include <ctime>
#include <functional>
#include "TcpServer.hpp"
#include "Session.hpp" // 引入session

const std::string HttpSep = "\r\n";
// 可以配置的
const std::string homepage = "index.html";
const std::string wwwroot = "./wwwroot";



class HttpRequest
{
public:
    HttpRequest():_req_blank(HttpSep),_path(wwwroot)
    {}
    bool GetLine(std::string& str,std::string* line)
    {
        auto pos=str.find(HttpSep);
        if(pos==std::string::npos)
        {
            return false;
        }
        *line=str.substr(0,pos);
        str.erase(0,pos+HttpSep.size());
        return true;
    }

    void Parse()
    {
        std::stringstream ss(_req_line);
        ss>>_method>>_url>>_http_version;
        std::string prefix="Cookie:";
        for(auto& line:_req_header)
        {
            std::string cookie;
            if(strncmp(line.c_str(),prefix.c_str(),prefix.size())==0)
            {
                cookie=line.substr(prefix.size());
                _cookies.emplace_back(cookie);
                break;
            }
        }

        prefix="sessionid=";
        for(const auto& cookie:_cookies)
        {
            if(strncmp(cookie.c_str(),prefix.c_str(),prefix.size())==0)
            {
                _sessionid=cookie.substr(prefix.size());
            }
        }


    }

    std::string Url()
    {
        return _url;

    }
    std::string SessionId()
    {
        return _sessionid;
    }
    bool Deserialize(std::string& request)
    {
        std::string line;
        bool ok=GetLine(request,&line);
        if(!ok)
            return false;
        _req_line=line;
        while(true)
        {
            bool ok=GetLine(request,&line);
            if(ok&& line.empty())
            {
                _req_content=request;
                break;
            }
            else if(ok&& !line.empty())
            {
                _req_header.push_back(line);
            }
            else
            {
                break;
            }
        }
        return true; 
    }
    void DebugHttp()
    {
        std::cout<<"_req_line:"<<_req_line<<std::endl;
        for(auto& line:_req_header)
        {
            std::cout<<"--->"<<line<<std::endl;
        }
    }

    ~HttpRequest()
    {

    }
private:
    std::string _req_line;
    std::vector<std::string> _req_header;
    std::string _req_blank;
    std::string _req_content;

    std::string _method;
    std::string _url;
    std::string _http_version;
    std::string _path;
    std::string _suffix;
    std::vector<std::string> _cookies;
    std::string _sessionid;
};

 处理请求的地方,构建请求和应答类,反序列化请求并提取请求行的信息,定义number变量,走if判断,如果为login就说明要登陆,然后提取sessionid值,如果为空说明没有登陆过,定义用户名为number+1,创建session对象,会走session构造函数,得到名字和状态,调用Sessionmanager类方法Addsession插入创建的s对象,会创建一个sessionid值出来,通过随机数加时间戳,并把创建好的sessionid和s存储到map里面管理,把sessionid信息添加到报头中。如果不是login则会查看sessionid值,不为空就调用Getsession函数,在Session.hpp文件中的SessionManger类中成员变量_sessions中查看是否存在这个sessionid,存在就打印正在活跃,没有就标识过期,后面就是设置状态码状态信息等内容。

 std::string HandlerHttp(std::string request)
    {
        HttpRequest req;
        HttpResponse resp;

        req.Deserialize(request);
        req.Parse();

        static int number=0;
        if(req.Url()=="/login")
        {
            std::string sessionid=req.SessionId();
            if(sessionid.empty())
            {
                std::string user="user-"+std::to_string(number++);
                session_ptr s=std::make_shared<Session>(user,"logined");
                std::string sessionid=_session_manager->AddSession(s);
                lg.LogMessage(Debug, "%s 被添加, sessionid是: %s\n", user.c_str(), sessionid.c_str());
                resp.AddHeader(ProveSession(sessionid));

            }
        }
        else
        {
            std::string sessionid=req.SessionId();
            if(!sessionid.empty())
            {
                session_ptr s=_session_manager->GetSession(sessionid);
                if(s!=nullptr)
                    lg.LogMessage(Debug, "%s 正在活跃.\n", s->_username.c_str());
                else
                    lg.LogMessage(Debug, "cookie : %s 已经过期, 需要清理\n", sessionid.c_str()); 

            }
        }
        resp.SetCode(200);
        resp.SetDesc("OK");
        resp.AddHeader("Content-Type:text/html");
        resp.AddContent("<html><h1>helloworld</h1></html>");
        return resp.Serialize();



    }

Session.hpp文件

Session类主要是定义用户名字和访问什么文件,SessionManger则是管理session的类,添加session和检验并返回session。

#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <ctime>
#include <unistd.h>
#include <unordered_map>

// 用来进行测试说明
class Session
{
public:
    Session(const std::string &username, const std::string &status)
    :_username(username), _status(status)
    {
        _create_time = time(nullptr); // 获取时间戳就行了,后面实际需要,就转化就转换一下
    }
    ~Session()
    {}
public:
    std::string _username;
    std::string _status;
    uint64_t _create_time;
    uint64_t _time_out; // 60*5
    std::string vip; // vip
    int active; // 
    std::string pos;
    //当然还可以再加任何其他信息,看你的需求
};

using session_ptr = std::shared_ptr<Session>;

class SessionManager
{
public:
    SessionManager()
    {
        srand(time(nullptr) ^ getpid());
    }
    std::string AddSession(session_ptr s)
    {
        uint32_t randomid = rand() + time(nullptr); // 随机数+时间戳,实际有形成sessionid的库,比如boost uuid库,或者其他第三方库等
        std::string sessionid = std::to_string(randomid);
        _sessions.insert(std::make_pair(sessionid, s));
        return sessionid;
    }
    session_ptr GetSession(const std::string sessionid)
    {
        if(_sessions.find(sessionid) == _sessions.end()) return nullptr;
        return _sessions[sessionid];
    }
    ~SessionManager()
    {}
private:
    std::unordered_map<std::string, session_ptr> _sessions;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值