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.hclass 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

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

时间: 2024-12-28 14:25:26

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

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

写在前面 每个码农可能都会偶尔有自己做一个常用软件的想法,比如操作系统,编译器,邮件服务器/客户端,文字编辑器等等.这里面有些很难,比如操作系统,做一个最简单的也要付出很大的努力,可是大部分常用工具都是可以比较容易的做一个简易版本(当然也是只能玩玩而已).于是我做了一个非常简陋的WEB服务器 —— TinyWS.这里主要是记录下自己整个过程中的一些想法. TinyWS是用C++”从头开始“做的,也就是说,除了C/C++的标准库和操作系统的系统调用,并没有使用第三方库.我并不喜欢C++(甚至有些厌

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上可能无法正常编译(主要是系统调用 部分可能会不同). 前面的内容可参考上一篇

Socket 初识 用Socket建立一个简易Web服务器

摘自<Asp.Net 本质论>作者:郝冠军 //在.Net中.system.Net命名空间提供了网络编程的大多数数据据类型以及常用操作,其中常用的类型如下:/*IPAddress 类表示一个IP地址* IPEndPoint类用来表示一个IP地址和一个端口号的组合,成为网络的端点.* System.Net.Sockets命名空间中提供了基于Socked编程的数据类型.* Socket类封装了Socked的操作.* 常见的操作:* Listen:设置基于连接通信的Socket进入监听状态,并设置等

自己写浏览器和web服务器的分析!

自己写浏览器和web服务器 在android写一个浏览器 editText:输入网址ip:port/login.html,提交 把域名解析成ip 产生请求行 get login.html /r/n 产生请求头 user-agent:/r/n host: new socket(ip,port).println(行,头) ip[tcp[行,头]]在网络上传输到达服务器了, 自己写一个web服务器 new serverSocket(8080) 我程序拿到是tcp中的数据 get login.html

写一个简易web服务器、ASP.NET核心知识(4)

前言 昨天尝试了,基于对http协议的探究,我们用控制台写了一个简单的浏览器.尽管浏览器很low,但是对于http协议有个更好的理解. 说了上面这一段,诸位猜到我要干嘛了吗?(其实不用猜哈,标题里都有,又都不瞎...我就是调侃一下,说些没营养的笑话.我认为这样能不那么枯燥,尽管不好笑吧,但这不重要!) 没错,今天要尝试的东西,是自己写一个web服务器.初衷依旧和昨天一样,旨在理解一些东西,而不是真的写出一个多牛的东西. 第一次尝试(V1.0) 1.理论支持 其实关于http协议的理论方面我在<写

[js高手之路]node js系列课程-创建简易web服务器与文件读写

web服务器至少有以下几个特点: 1.24小时不停止的工作,也就是说这个进程要常驻在内存中 2.24小时在某一端口监听,如: http://localhost:8080, www服务器默认端口80 3.要能够处理基本的请求:如get, post 在node js中创建一台服务器非常的简单,因为node自带http模块,该模块可以帮助我们非常快速搭建一台web服务器,来处理一个简单的请求. 1 const http = require("http"); 2 var server = ht

简易Web服务器实现

手痒就自己实现了一下简易的web服务器,由于只是简易的web服务器,所以并没有什么特别高深的技术含量. 1.      TCP通信(socket) 2.      IO流 3.      线程池技术 服务器架构也简单: Request类主要是实现解析URL的功能,以获取html文件的路径. Response类实现读取html文件并且向浏览器输出html文件内容. Server类整合Request类和Response类,实现线程的run方法. Test类实现线程池,同时也是服务启动类. 下图是浏览

一个基于webrick 的简单web服务器

使用ruby 自带的webrick 可以非常方便地实现一个web服务器. webrick.rb 基本代码如下: #!/usr/bin/env ruby require 'webrick' root = File.expand_path 'html' server = WEBrick::HTTPServer.new :Port => 8000, :DocumentRoot => root trap 'INT' do server.shutdown end server.start 使用命令rub

python简易web服务器学习笔记(三)

import sys, os, BaseHTTPServer #------------------------------------------------------------------------------- class ServerException(Exception): '''For internal error reporting.''' pass #--------------------------------------------------------------