1. 程式人生 > >為Boost ASIO的http server4例子新增Cookie和Post/Get引數提取支援

為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例子新增CookiePost/Get引數提取支援

Cookie和Post/Get引數支援 Author: kagula Date: 2016-12-28 Cookie的支援 如何呼叫 第一步:所有的TCP請求加入下面的程式碼 若session不存在,會返回一個新的sessionid std::string sessio

ab傳送cookiepost請求的方法

ab是apache自帶的壓力測試工具,近期需要壓測一個介面,涉及使用post請求,並在其中帶cookie。方法總結如下: 1. 傳送cookie 方法1 -C key1=value1;key2=value2... 例: ab -n 1

CookiePost模擬登陸

藉助Chrome直接獲取到Cookie,不過這種方式有侷限性,並不如Fiddler通用,有一些網站使用這種方式獲取的Cookie無法登入。 經過測試,這種方式對知乎有效。 在已經登入知乎的情況下,開啟開發者工具,定位到 Network 選顯示卡,然後重新整理

java獲取GETPOST請求引數

一 獲取請求方式 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 GetPost獲取引數

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控制元件新增HeaderFooter

效果圖:專案地址 新增圖片素材,新增plist檔案,新增名為CZGoods的module檔案 @implementation CZGoods - (instancetype)initWithDict:(NSDictionary *)dict { if

TexWorks新增字典拼寫檢查器

[TOC] 1 介紹 任何一個用於書寫文件的軟體,拼寫檢查一直以來是一個重要的功能。人在書寫各種資料時難免會書寫一些拼寫錯誤的單詞,靠人工檢查拼寫錯誤是費時費力的,同時也容易漏判,因此為軟體配置拼寫檢查功能非常重要。 MS Word、OpenOffice等軟體都配備有拼寫檢查

jsObject物件動態新增屬性

為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系統可能只學了一些皮毛。最近做東西時又要為怪物製作血條,但一時間竟然忘記了該怎麼弄,於是翻翻以前的專案,同時在這裡記下來,作為學習參考。

GETPOST請求新增請求引數請求頭

  我們平常瀏覽各個網站時,不免有時候就需要填寫一些資訊,比如註冊時,登入時,這些資訊一般都是通過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引數、GsonJsonObject生成Json資料、Gson解析返回的Json

上一篇簡單瞭解了Volley的原始碼 Volley原始碼簡讀 ps一下:那個封裝頭體的請求,url我修改了埠(因為是上線專案,避免外來請求,不過有log日誌可以參考) 今天我來簡封Volley、為Volley請求頭體新增Json引數、Gson和JsonObject生成J

EditPlus工具欄新增編譯執行選項

哈,新增編譯和執行項之後,我們就能更方便的除錯和執行程式碼了。而不僅僅把它當成高階記事本用了。 方法有些愚笨,偶是菜鳥嘛,還請大大們指點簡便方法。 一,加入編譯器(以c#為例) 這個最簡單,就是新增你的編譯器所在地址。點選【工具】——【配置使用者工具】,點選【新增工具】選【