代碼已經託管在 https://git.oschina.net/augustus/TinyWS.githtml
能夠用git clone下來。因爲我可能會偶爾作一些修改,不能保證git 庫上的代碼與blog裏的徹底一致(實際上也不可能把全部的代碼都貼在這裏)。另外,TinyWS是基於linux寫的(ubuntu 14.10 + eclipse luna,eclipse工程我也push到了git庫),故在Windows上可能沒法正常編譯(主要是系統調用 部分可能會不一樣)。linux
前面的內容可參考上一篇 http://www.cnblogs.com/cuiluo/p/4217205.html
git
接上一篇,這個類就是處理GET方法的強求的。它繼承自Request,須要override doExecute方法。它的功能是解析出請求的URI,並將其傳給Response類的對象進一步處理。目前的實現中,使用CGI的方式處理動態請求(所謂動態請求,就是瀏覽器的請求中指明要運行服務器端的某個程序,並獲得返回結果),可是這部分代碼實際上是沒有測試過的。本類中,後面三個方法都是處理動態請求的uri和參數的。ubuntu
// GetRequest.h
class GetRequest : public Request { virtual void doExecute(); bool parseUri(std::string& filename, std::string& cgiargs); bool parseStaticContentUri(std::string& filename); bool parseDynamicContentUri(std::string& filename, std::string& cgiargs); void assignCigArgs(std::string& cgiargs); void doAssignCigArgs(std::string::size_type pos, std::string& cgiargs); };
在具體實現中,靜態網頁的存儲路徑是"test-files",這個主要是爲了測試,若是請求沒有攜帶指定的文件名,那麼就返回此路徑下的"index.html"文件。服務於動態請求的程序都存放在"cgi-bin"目錄下,固然,目前此路徑下也沒有程序。在doExecute方法中,解析了URI後,就直接建立一個Response的對象作進一步處理。瀏覽器
// GetRequest.cpp void GetRequest::doExecute() { std::string filename, cgiargs; bool isStatic = parseUri(filename, cgiargs); Response(getFileDescriptor(), filename, cgiargs, isStatic).respond(); } bool GetRequest::parseUri(std::string& filename, std::string& cgiargs) { if (getUri().find("cgi-bin") == std::string::npos) return parseStaticContentUri(filename); else return parseDynamicContentUri(filename, cgiargs); } bool GetRequest::parseStaticContentUri(std::string& filename) { std::string uri = getUri(); filename = "test-files" + uri; if (uri[uri.length() - 1] == '/') filename += "index.html"; return true; } bool GetRequest::parseDynamicContentUri(std::string& filename, std::string& cgiargs) { assignCigArgs(cgiargs); filename = "." + getUri(); return false; } void GetRequest::assignCigArgs(std::string& cgiargs) { std::string uri = getUri(); std::string::size_type pos = uri.find_first_of("?"); doAssignCigArgs(pos, cgiargs); } void GetRequest::doAssignCigArgs(std::string::size_type pos, std::string& cgiargs) { if (pos != std::string::npos) cgiargs = getUri().substr(pos, getUri().length() - 1); else cgiargs.clear(); }
Response類的做用是,根據請求的uri,返回被請求的文件,若是是動態請求,則執行相關的程序。服務器
Response的設計很很差。首先,他的構造函數須要一個bool值標識是否爲靜態網頁,這會引發內部處理的不少if ... else分支,這實際上是我十分痛恨的;其次它的一個成員是struct stat 類型,此爲linux系統調用提供的一個標識文件屬性的結構,它本不該該屬於這個層級,Response應該是專一業務的,和操做系統打交道的工做應該交給別人。eclipse
因此這裏是後面我重構的重點,或許可使用工廠或狀態模式等方法處理一下,不過對於一個只有兩種選擇的狀態,用工廠就顯得太興師動衆了。目前打算使用狀態模式,已經加了一個 enum State { STATIC, DYNAMIC},不過尚未繼續。後續若是修改了代碼,會提到git庫,這裏就不會再同步修改了。ide
class Response { public: Response(int fd, std::string name, std::string cgiargs, bool isStc); void respond(); private: const std::string getFiletype(); void preResond(); void respondOK(); void respondStatic(); void respondDynamic(); const void execveCgiProgram(); const void doExecveCgiProgram(); const std::string buildRespond0KHeaders(); const std::string buildRespondStaticHeaders(); const std::string buildForbiddenMsg(); const std::string buildRespondErrorHeaders(const std::string errNum, const std::string shortMsg); const std::string buildRespondErrorBody(const std::string errNum, const std::string shortMsg, const std::string longMsg); void respondError(const std::string errNum, const std::string shortMsg, const std::string longMsg); private: enum State { STATIC, DYNAMIC}; struct stat sbuf; int fileDescriptor; std::string fileName; std::string cgiArgs; bool isStatic; };
如今,關於TinyWS上層業務相關的部分就介紹完了。後面的部分會涉及到Socket和IO操做相關的內容,留做下次再說。函數