為Boost ASIO的http server4例子新增Cookie和Post/Get引數提取支援
Cookie和Post/Get引數支援
Author: kagula
Date: 2016-12-28
Cookie的支援
如何呼叫
第一步:所有的TCP請求加入下面的程式碼
若session不存在,會返回一個新的sessionid
std::string sessionid = kagula::SessionSupport::Instance().parseReq(req);
第二步:往session裡讀寫資料
只要拿到下面的物件,就可以讀寫session了。
boost::shared_ptr<kagula::SessionInfo> pSI = kagula::SessionSupport::Instance().getSessionInfo(sessionid);
最後一步:修改http返回的程式碼段,使之能把sessionid寫到http客戶端裡
rep.status = reply::ok; rep.headers.resize(3); rep.headers[0].name = "Content-Length"; rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size()); rep.headers[1].name = "Content-Type"; rep.headers[1].value = mime_types::extension_to_type(extension); rep.headers[2].name = kagula::reply_set_cookie; rep.headers[2].value = kagula::SessionSupport::Instance().getSetCookieValue(sessionid);
依賴的程式碼
SessionSupport.h
#ifndef _SESSION_SUPPORT_H_ #define _SESSION_SUPPORT_H_ #include <boost/shared_ptr.hpp> #include <boost/thread.hpp> #include <string> #include <map> namespace http { namespace server4 { struct request; } } /* 功能:提供Cookie支援,通過Cookie中存放Session ID識別每個不同的使用者,維護每個session對應的具體使用者狀態。 依賴:Boost::ASIO庫 示例程式碼 http::server4中的程式碼段. */ namespace kagula { const char req_cookie[] = "Cookie"; const char reply_set_cookie[] = "Set-Cookie"; //服務端只需要在客戶端Cookie中存放session id的值,這個session id對應的具體資料 //由服務端維護。 const char session_key[] = "CSessionID"; struct SessionInfo { SessionInfo(std::string sessionID):count(0),sessionid(sessionID){} //存放使用者名稱稱、使用者角色型別等使用者資訊。 //這樣後面的程式碼就知道當前session的使用者資訊。 std::map<std::string, std::string> session; //存放最後一次XX.html請求,GET Method方式後面跟的請求引數。 //應該在每次執行具體請求處理前呼叫。 std::map<std::string, std::map<std::string, std::string>> sessionPage; // const std::string sessionid; unsigned count;//每秒+1,如果超過3600s,當前Session被清除。 }; class SessionSupport { public: ~SessionSupport(); static SessionSupport& Instance() { static SessionSupport ss; return ss; } //響應客戶端請求的時候呼叫,返回session id的具體值, //如果客戶端沒有在Cookie中上傳session id就分配一個新的sessionid. //後面的程式碼可以通過這個session id維護當前session的使用者資訊。 std::string parseReq(const http::server4::request &req); //供處理使用者請求的時候呼叫。 boost::shared_ptr<SessionInfo> getSessionInfo(const std::string &sessionID); //返回資訊給客戶端之前呼叫,用來告訴瀏覽器本服務端的Cookie值設定。 std::string getSetCookieValue(const std::string &sessionID); void removeSession(const std::string &sessionID); private: //key是sessionID std::map<std::string, boost::shared_ptr<SessionInfo>> m_si; // Other non - static member functions private: SessionSupport(); // Private constructor SessionSupport(const SessionSupport&); // Prevent copy-construction SessionSupport& operator=(const SessionSupport&); // Prevent assignment void checkTimeout(); boost::shared_mutex m_mutexRW; boost::thread m_tCheck; }; } #endif
SessionSupport.cpp
#include "SessionSupport.h"
#include "request.hpp"
#include <boost/smart_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
namespace kagula
{
boost::mutex m_mutexNewSessionID;
std::string NewSessionID()
{
boost::lock_guard<boost::mutex> lock(m_mutexNewSessionID);
static unsigned sessionIndex = 0;
return boost::lexical_cast<std::string>(++sessionIndex);
}
SessionSupport::SessionSupport()
{
boost::thread t(boost::bind(&SessionSupport::checkTimeout,this));
m_tCheck = boost::move(t);
}
SessionSupport::~SessionSupport()
{
m_tCheck.interrupt();
m_tCheck.join();
}
void SessionSupport::checkTimeout()
{
while (true)
{
{
boost::unique_lock<boost::shared_mutex> lock(m_mutexRW, boost::defer_lock);
if (lock.try_lock())
{
std::map<std::string, boost::shared_ptr<SessionInfo>>::iterator iter = m_si.begin();
while (iter!=m_si.end())
{
iter->second->count++;
//if a session have not accessed by a client exceed an hour, erase it.
if (iter->second->count>3600)
{
m_si.erase(iter++);
continue;
}//if
iter++;
}//while
lock.unlock();
}//if
}//check session
boost::this_thread::sleep(boost::posix_time::seconds(1));
}//while
}//function
std::string SessionSupport::parseReq(const http::server4::request &req)
{
std::string cookieValue;
for (unsigned index = 0; index < req.headers.size();index++)
{
if (req.headers[index].name == req_cookie)
{
cookieValue = req.headers[index].value;
break;
}
}
std::string sessionID;
//extract session id from cookie value.
if (cookieValue.empty() == false)
{
std::vector<std::string> vecRec;
boost::split(vecRec, cookieValue, boost::is_any_of(";"));
for (unsigned i = 0; i < vecRec.size();i++)
{
std::vector<std::string> vecSub;
boost::split(vecSub, vecRec[i], boost::is_any_of("="));
if (vecSub.size()==2)
{
boost::trim_if(vecSub[0], boost::is_any_of("\" \n\r\t'"));
boost::trim_if(vecSub[1], boost::is_any_of("\" \n\r\t'"));
if (vecSub[0] == session_key)
{
sessionID = vecSub[1];
break;
}
}//end if
}//end for
}//end if
//
if (cookieValue.empty()||sessionID.empty()||
m_si.find(sessionID) == m_si.end())//由於瀏覽器非正常關閉,沒有即時清除sessionID,把上次“已經無效的”session id也傳上來了。
{
boost::lock_guard<boost::shared_mutex> lock(m_mutexRW);
do
{
sessionID = NewSessionID();
} while (m_si.find(sessionID)!=m_si.end());
boost::shared_ptr<SessionInfo> pSI(new SessionInfo(sessionID));
m_si[sessionID] = pSI;
}
return sessionID;
}
//供處理使用者請求的時候呼叫。
boost::shared_ptr<SessionInfo> SessionSupport::getSessionInfo(const std::string &sessionID)
{
boost::lock_guard<boost::shared_mutex> lock(m_mutexRW);
m_si[sessionID]->count = 0;
return m_si[sessionID];
}
void SessionSupport::removeSession(const std::string &sessionID)
{
boost::lock_guard<boost::shared_mutex> lock(m_mutexRW);
m_si.erase(sessionID);
}
//返回資訊給客戶端之前呼叫,用來告訴瀏覽器本服務端的Cookie值設定。
std::string SessionSupport::getSetCookieValue(const std::string &sessionID)
{
std::string strCookieValue;
strCookieValue = session_key;
strCookieValue.append("=");
strCookieValue.append(sessionID);
return strCookieValue;
}
}
Post/Get的支援
程式碼段如下
//
std::string &command = vecRec[1];//url地址中的字尾部份,比如http://localhost/XX.do?a=b中的XX.do
std::map<std::string, std::string> mapReq;//Get/Post方法傳上來的引數會放到這個容器中。
//下面三條是GET方式Parse測試用例
//std::string command = "my.do";
//std::string command = "my.do?";
//std::string command = "my.do?key=value&key2=value2&key3=value3";
if (req.method=="GET")
{
//解析Get方式發上來的資料
[&command, &mapReq](){
std::string headPart;
std::string tailPart;
if (command.find_last_of('?') > 0)
{
headPart = command.substr(0, command.find_last_of('?'));
tailPart = command.substr(command.find_last_of('?') + 1,
command.size() - command.find_last_of('?') - 1);
command = headPart;
[](std::string &tailPart, std::map<std::string, std::string> &mapReq){
std::vector<std::string> vecSegment;
boost::split(vecSegment, tailPart, boost::is_any_of("&"));
for (unsigned i = 0; i < vecSegment.size(); i++)
{
std::vector<std::string> vecKV;
boost::split(vecKV, vecSegment[i], boost::is_any_of("="));
if (vecKV.size() == 2)
{
mapReq[vecKV[0]] = vecKV[1];
}
}
}(tailPart,mapReq);
}
else
{
return;
}
}();
}
else if (req.method == "POST")
{
//
if (command.find_last_of('?') > 0)
{
command = command.substr(0, command.find_last_of('?'));
}
//
const std::string &req_content = req.content;
const std::string x_www_form_urlencoded = "application/x-www-form-urlencoded";
const std::string form_data = "multipart/form-data";
if (contentType == x_www_form_urlencoded)
{
[](const std::string &tailPart, std::map<std::string, std::string> &mapReq){
std::vector<std::string> vecSegment;
boost::split(vecSegment, tailPart, boost::is_any_of("&"));
for (unsigned i = 0; i < vecSegment.size(); i++)
{
std::vector<std::string> vecKV;
boost::split(vecKV, vecSegment[i], boost::is_any_of("="));
if (vecKV.size() == 2)
{
mapReq[vecKV[0]] = URLDecode(vecKV[1]);
}
}
}(req_content, mapReq);
}
else if (contentType.size() >= form_data.size() &&
contentType.substr(0, form_data.size()) == form_data)
{
//解析Post方式發上來的form-data格式資料
[&req_content, &mapReq](){
//Step1: find separator
//也可以從 contentType 裡取separator
std::string bound = [req_content]()->std::string{
unsigned end = req_content.find('\r');
if (req_content[end + 1] != '\n')
{
std::stringstream ss;
ss << "[" << __FILE__ << "][" << __LINE__ << "] Http資料請求格式非法。";
kagula::utility::LoggerError(ss.str());
return "";//invalid format
}
return req_content.substr(0, end);
}();
if (bound.empty())
{
return;
}
//Step2: extract block
std::vector<std::string> vecBlock;
[req_content, bound, &vecBlock](){
unsigned pos = 0;
std::list<unsigned> listPos;
std::list<unsigned>::iterator iterPos;
while ((pos = req_content.find(bound, pos)) != std::string::npos)
{
listPos.push_back(pos);
pos += bound.size();
if (listPos.size() == 2)
{
iterPos = listPos.begin();
unsigned begin = *iterPos + bound.size(); iterPos++;
unsigned end = *iterPos;
std::string block = req_content.substr(begin, end - begin);
boost::trim_if(block, boost::is_any_of("\" \n\r\t'"));
vecBlock.push_back(block);
listPos.pop_front();
}
}
}();
//Step3:parse block
[vecBlock, &mapReq](){
for (std::vector<std::string>::const_iterator iter = vecBlock.begin();
iter != vecBlock.end(); iter++)
{
const unsigned posSemicolon = iter->find_first_of(';');
if (posSemicolon == std::string::npos)
{
std::stringstream ss;
ss << "[" << __FILE__ << "][" << __LINE__ << "] Http資料請求格式非法。";
kagula::utility::LoggerError(ss.str());
continue;//invalid format
}
//extract content disposition
std::string contentDisposition = iter->substr(0, posSemicolon);
std::vector<std::string> vecRec;
boost::split(vecRec, contentDisposition, boost::is_any_of(":"));
if (vecRec.size() != 2)
{
std::stringstream ss;
ss << "[" << __FILE__ << "][" << __LINE__ << "] Http資料請求格式非法。";
kagula::utility::LoggerError(ss.str());
continue;//invalid format
}
boost::trim_if(vecRec[0], boost::is_any_of("\" \n\r\t'"));
boost::trim_if(vecRec[1], boost::is_any_of("\" \n\r\t'"));
if (vecRec[0] != "Content-Disposition")
{
continue;//invalid format.
}
if (vecRec[1] != "form-data")
{
std::stringstream ss;
ss << "[" << __FILE__ << "][" << __LINE__ << "] Http資料請求格式非法。";
kagula::utility::LoggerError(ss.str());
continue;
}
//extract name
const unsigned posRNRN = iter->find("\r\n\r\n");
if (posRNRN == std::string::npos)
{
std::stringstream ss;
ss << "[" << __FILE__ << "][" << __LINE__ << "] Http資料請求格式非法。";
kagula::utility::LoggerError(ss.str());
continue;//invalid format.
}
std::string namePart = iter->substr(posSemicolon + 1, posRNRN - posSemicolon);//+1 for exclude semicolon
boost::split(vecRec, namePart, boost::is_any_of("="));
if (vecRec.size() != 2)
{
std::stringstream ss;
ss << "[" << __FILE__ << "][" << __LINE__ << "] Http資料請求格式非法。";
kagula::utility::LoggerError(ss.str());
continue;//invalid format
}
boost::trim_if(vecRec[0], boost::is_any_of("\" \n\r\t'"));
boost::trim_if(vecRec[1], boost::is_any_of("\" \n\r\t'\""));
if (vecRec[0] != "name")
{
std::stringstream ss;
ss << "[" << __FILE__ << "][" << __LINE__ << "] Http資料請求格式非法。";
kagula::utility::LoggerError(ss.str());
continue;//invalid format.
}
std::string strKey = vecRec[1];
//extract value
mapReq[strKey] = iter->substr(posRNRN + 4, iter->size() - (posRNRN + 4));
}
}();
}();
}
else
{
std::stringstream ss;
ss << "[" << __FILE__ << "][" << __LINE__ << "] 不支援的Http資料請求格式。";
kagula::utility::LoggerError(ss.str());
}
}
else
{
return false;
}
if (mapReq.find("data") == mapReq.end())
{
//具體的請求引數以JSON形式放在data欄位中,所以data欄位不可能為空。
return false;
}
相關推薦
為Boost ASIO的http server4例子新增Cookie和Post/Get引數提取支援
Cookie和Post/Get引數支援 Author: kagula Date: 2016-12-28 Cookie的支援 如何呼叫 第一步:所有的TCP請求加入下面的程式碼 若session不存在,會返回一個新的sessionid std::string sessio
ab傳送cookie和post請求的方法
ab是apache自帶的壓力測試工具,近期需要壓測一個介面,涉及使用post請求,並在其中帶cookie。方法總結如下: 1. 傳送cookie 方法1 -C key1=value1;key2=value2... 例: ab -n 1
Cookie和Post模擬登陸
藉助Chrome直接獲取到Cookie,不過這種方式有侷限性,並不如Fiddler通用,有一些網站使用這種方式獲取的Cookie無法登入。 經過測試,這種方式對知乎有效。 在已經登入知乎的情況下,開啟開發者工具,定位到 Network 選顯示卡,然後重新整理
java獲取GET和POST請求引數
一 獲取請求方式 request.getMethod(); get和post都可用, 二 獲取請求型別 request.getContentType(); get和post都可用,示例值:application/json ,multipart/form-data, application/x
關於get請求引數的長度和post請求引數的長度的區別
最近在做一個上傳圖片的功能,用到了post請求,通過網上查閱資料,對post請求的引數長度有了進一步的理解,所以索性就把以前學過的get和post 請求引數的異同點,進行總結,以備記憶! 一、 Get方法長度限制 首先Http Get方法提交的資料大小長度並沒有
Express Get和Post獲取引數
Project Directory npm install express npm install body-parser Note:post提交需要這個body-parser來獲取引數 index.html <!DOCTYPE html> <html lang="en"&
Spring mvc 針對get 和 post 請求引數的不同接收方式
GET 請求 針對與於get請求spring mvc控制層主要接收方式如下 1.直接在controller接收引數,引數名稱要與getURl中傳遞的引數一致 get url : https://localhost:8080/demo/login?&userName=
nginx+lua實現post請求引數提取為一致性雜湊引數
nginx的ip_hash,要求nginx一定是最前端伺服器,否則nginx得不到正確的ip值。若nginx不是最前端伺服器,且沒有實現session共享機制,使用ip_hash演算法,會導致nginx使用的ip不是客戶端ip,達不到預期效果。假定每個訪問的客戶端都會攜帶固定
IOS開發學習筆記十五 為UITableView控制元件新增Header和Footer
效果圖:專案地址 新增圖片素材,新增plist檔案,新增名為CZGoods的module檔案 @implementation CZGoods - (instancetype)initWithDict:(NSDictionary *)dict { if
為TexWorks新增字典和拼寫檢查器
[TOC] 1 介紹 任何一個用於書寫文件的軟體,拼寫檢查一直以來是一個重要的功能。人在書寫各種資料時難免會書寫一些拼寫錯誤的單詞,靠人工檢查拼寫錯誤是費時費力的,同時也容易漏判,因此為軟體配置拼寫檢查功能非常重要。 MS Word、OpenOffice等軟體都配備有拼寫檢查
js為Object物件動態新增屬性和值
為Object物件動態新增屬性和值 方式1: var obj = {}; //或者 var obj=new Object(); var key = "name"; var value = "張三丰" obj[key] = value; console.info(obj); 方式2,
為窗體新增背景和為元件新增圖片
為窗體新增背景 public class LoginPanel extends JPanel{ private Image img;// 登入面板的背景圖片 public LoginPanel() {// 登入面板的構造方法 super();
淘淘商城27_購物車_01新增購物車和將購物車儲存到cookie
一、需求分析 購物車大概分兩種:京東樣式的和淘寶樣式的 京東:在使用者不用登入的情況下也可以將商品放入到購物車 淘寶:使用者必須登入成功後才能將商品放入到購物車 我們要做的類似京東:購物車在使用者不登陸的情況下也可以使用購物車。需要把購物車的商品資訊寫入cookie
Unity3D——使用UGUI為角色新增名字和血條
轉載http://www.jianshu.com/p/a9fd13594f18 學習Unity3D有一段時間了,龐大的U3D系統可能只學了一些皮毛。最近做東西時又要為怪物製作血條,但一時間竟然忘記了該怎麼弄,於是翻翻以前的專案,同時在這裡記下來,作為學習參考。
為GET和POST請求新增請求引數和請求頭
我們平常瀏覽各個網站時,不免有時候就需要填寫一些資訊,比如註冊時,登入時,這些資訊一般都是通過GET請求或者POST(敏感資訊一般使用POST,資料隱藏,相對來說更安全)請求提交到後臺,經過後臺的一系列處理,再返回給前臺結果,前臺進行處理。 GET
伺服器8080埠改為80埠(直接訪問網站不用新增埠和專案名。)
新手教材。很多人購買伺服器和域名後,全部弄好後,只能通過 ip:8080/專案名 (或者 域名:8080/專案名 )的方式進行訪問。 這裡講解的就是如何去掉埠和專案名,直接用 ip (或者
為RecyclerView新增頭部和腳部的UI控制元件:Bookends
Bookends會封裝傳遞給它的adapter。 其工作原理是在 getItemViewType()的返回值中新增額外的view item型別,將addHeader() 和 addFooter()提供的view對映為頭部和腳部。 使用這個類有如下的限制: 只對單列的列表有效(比如使用Linear
Java Web如何操作Cookie的新增修改和刪除
一、Cookie是什麼 Cookie是伺服器存放在客戶端瀏覽器上的一些小資料,可以使用Cookie完成與伺服器的一些互動動作。伺服器可以通過HTTP響應頭將Cookie傳送給瀏覽器,而瀏覽器如果支援儲存Cookie,則將HTTP響應頭資訊中的Cookie內容存放到瀏覽器中。
Volley學習(二)簡封Volley、為Volley請求頭體新增Json引數、Gson和JsonObject生成Json資料、Gson解析返回的Json
上一篇簡單瞭解了Volley的原始碼 Volley原始碼簡讀 ps一下:那個封裝頭體的請求,url我修改了埠(因為是上線專案,避免外來請求,不過有log日誌可以參考) 今天我來簡封Volley、為Volley請求頭體新增Json引數、Gson和JsonObject生成J
為EditPlus工具欄新增編譯和執行選項
哈,新增編譯和執行項之後,我們就能更方便的除錯和執行程式碼了。而不僅僅把它當成高階記事本用了。 方法有些愚笨,偶是菜鳥嘛,還請大大們指點簡便方法。 一,加入編譯器(以c#為例) 這個最簡單,就是新增你的編譯器所在地址。點選【工具】——【配置使用者工具】,點選【新增工具】選【