首页 > 代码库 > TinyWS —— 一个C++写的简易WEB服务器(二)

TinyWS —— 一个C++写的简易WEB服务器(二)

写在前面

代码已经托管在 https://git.oschina.net/augustus/TinyWS.git

可以用git clone下来。由于我可能会偶尔做一些修改,不能保证git 库上的代码与blog里的完全一致(实际上也不可能把所有的代码都贴在这里)。另外,TinyWS是基于linux写的(ubuntu 14.10 + eclipse luna,eclipse工程我也push到了git库),故在Windows上可能无法正常编译(主要是系统调用 部分可能会不同)。

前面的内容可参考上一篇  http://www.cnblogs.com/cuiluo/p/4217205.html

GetRequest

接上一篇,这个类就是处理GET方法的强求的。它继承自Request,需要override doExecute方法。它的功能是解析出请求的URI,并将其传给Response类的对象进一步处理。目前的实现中,使用CGI的方式处理动态请求(所谓动态请求,就是浏览器的请求中指明要运行服务器端的某个程序,并得到返回结果),但是这部分代码其实是没有测试过的。本类中,后面三个方法都是处理动态请求的uri和参数的。

// 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.cppvoid 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

Response类的作用是,根据请求的uri,返回被请求的文件,如果是动态请求,则执行相关的程序。

Response的设计很不好。首先,他的构造函数需要一个bool值标识是否为静态网页,这会引起内部处理的很多if ... else分支,这其实是我十分痛恨的;其次它的一个成员是struct stat 类型,此为linux系统调用提供的一个标识文件属性的结构,它本不应该属于这个层级,Response应该是专注业务的,和操作系统打交道的工作应该交给别人。

所以这里是后面我重构的重点,或许可以使用工厂或状态模式等方法处理一下,不过对于一个只有两种选择的状态,用工厂就显得太兴师动众了。目前打算使用状态模式,已经加了一个 enum State { STATIC, DYNAMIC},不过还没有继续。后续如果修改了代码,会提到git库,这里就不会再同步修改了。

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操作相关的内容,留作下次再说。

TinyWS —— 一个C++写的简易WEB服务器(二)