ejet

2025-12-07 0 979

ejet – a lightweight highperformance embeded Web Server

ejet is a lightweight, high-performance, embedded Web server that implements the full-stack functions of the HTTP/1.1 protocol, including TLS/SSL, Forward/Reverse Proxy, FastCGI, Cookie, Web Cache, Access Log, HTTP Variable, HTTP Script, JSon-style configuration file, Virtual Host, HTTP Location, Directives such as Rewrite/Try_files, HTTP Tunnel, Application/Dynamic-Lib Callbacks, etc. It\’s an ideal service platform hosting for upload/download of super-large files, website, PHP, CDN, Web Cache, embedded Web, etc.

ejet 是一个轻量级、高性能、嵌入式Web服务器,实现HTTP/1.1协议全栈功能,包括TLS/SSL、正向代理、反向代理、FastCGI、Cookie、Web Cache、访问日志、HTTP变量、HTTP Script脚本程序、JSon配置文件、虚拟主机、HTTP Location、Rewrite/Try_files等指令、HTTP Tunnel、应用回调和动态库回调等,是承载超大文件上传下载、网站、PHP、CDN、Web Cache、嵌入式Web等服务的理想平台。

目录

  • 一. ejet是什么?
  • 二. ejet系统流程和工作原理
    • 2.1 Web服务器基本功能
    • 2.2 ejet Web服务器启动流程
    • 2.3 启动监听服务
    • 2.4 http_pump作为事件驱动核心入口
    • 2.5 IOE_ACCEPT事件驱动ejet创建HTTPCon连接
    • 2.6 IOE_READ事件驱动ejet读取请求数据
    • 2.7 解析HTTP请求数据
    • 2.8 创建HTTPMsg保存请求数据
    • 2.9 设置DocURI并启动HTTPMsg资源实例化
    • 2.10 Proxy代理转发
      • 2.10.1 判定是否代理转发—正向代理或反向代理
      • 2.10.2 代理请求资源是否在本地缓存里
      • 2.10.3 创建代理请求消息
      • 2.10.4 打开源服务器并建立通信连接
      • 2.10.5 发送代理请求到源服务器
    • 2.11 FastCGI转发
    • 2.12 读取并解析请求体
    • 2.13 由Handler处理HTTPMsg
      • 2.13.1 校验请求方法
      • 2.13.2 反向Proxy模式缓存内容处理
      • 2.13.3 正向Proxy模式校验
      • 2.13.4 HTTP消息应用回调和动态库回调处理
      • 2.13.5 读取并发送资源文件或路径下的缺省文件
    • 2.14 发送响应到客户端
    • 2.15 用状态机处理不完整请求
    • 2.16 设置可写通知产生IOE_WRITE事件处理网络拥塞
    • 2.17 利用定时器产生的IOE_TIMEOUT事件监管实例对象
  • 三. ejet系统基本数据结构
    • 3.1 HTTPMgmt – ejet内核
    • 3.2 HTTPMsg – 消息
    • 3.3 HTTPCon – 通信连接
    • 3.4 HTTPListen – 监听服务
    • 3.5 HTTPHost – 虚拟主机
    • 3.6 HTTPLoc – 资源位置
    • 3.7 HTTPHeader – 头信息
    • 3.8 HTTPUri – 资源地址URL
    • 3.9 HTTPVar – HTTP变量
    • 3.10 HTTPLog – 日志信息
    • 3.11 CacheInfo – 缓存信息管理
    • 3.12 HTTPForm – 表单信息
    • 3.13 HTTPScript – 脚本程序
    • 3.14 HTTPSrv – 源服务器
    • 3.15 HTTPChunk – HTTP数据块
    • 3.16 HTTPCookie – Cookie数据
    • 3.17 FcgiSrv – FastCGI服务器
    • 3.18 FcgiCon – FastCGI通信连接
    • 3.19 FcgiMsg – FastCGI消息
  • 四. ejet核心功能模块
    • 4.1 ejet资源管理架构
      • 4.1.1 三层资源定位架构
      • 4.1.2 HTTP监听服务 – HTTPListen
      • 4.1.3 HTTP虚拟主机 – HTTPHost
      • 4.1.4 HTTP资源位置 – HTTPLoc
    • 4.2 HTTP变量
      • 4.2.1 HTTP变量的定义
      • 4.2.2 HTTP变量的应用
      • 4.2.3 HTTP变量的类型和使用规则
      • 4.2.4 预定义的参数变量列表和实现原理
    • 4.3 HTTP Script脚本
      • 4.3.1 HTTP Script脚本定义
      • 4.3.2 HTTP Script脚本类型
      • 4.3.3 Script脚本嵌入位置
      • 4.3.4 Script脚本范例
      • 4.3.5 Script脚本语句
        • 4.3.5.1 条件语句
        • 4.3.5.2 赋值语句
        • 4.3.5.3 返回语句
        • 4.3.5.4 响应语句
        • 4.3.5.5 rewrite语句
        • 4.3.5.6 addReqHeader语句
        • 4.3.5.7 addResHeader语句
        • 4.3.5.8 delReqHeader语句
        • 4.3.5.9 delResHeader语句
        • 4.3.5.10 addResBody语句
        • 4.3.5.11 appendResBody语句
        • 4.3.5.12 addFile2ResBody语句
        • 4.3.5.13 appendFile2ResBody语句
        • 4.3.5.14 try_files 语句
        • 4.3.5.15 注释语句
      • 4.3.5 Script脚本解释器
    • 4.4 JSon格式的系统配置文件
      • 4.4.1 JSON语法特点
      • 4.4.2 ejet配置文件对JSON的扩展
        • 4.4.2.1 分隔符
        • 4.4.2.2 include指令
        • 4.4.2.3 单行注释和多行注释
        • 4.4.2.4 script语法
      • 4.4.3 ejet配置文件
    • 4.5 事件驱动流程 http_pump
    • 4.6 HTTP请求和响应
      • 4.6.1 HTTP请求格式
      • 4.6.2 HTTP响应格式
      • 4.6.3 头和体的解析与编码
    • 4.7 HTTPMsg的实例化流程
      • 4.7.1 什么是HTTPMsg实例化
      • 4.7.2 匹配虚拟主机和资源位置
      • 4.7.3 执行脚本程序
    • 4.8 HTTP MIME管理
    • 4.9 HTTP URI管理
    • 4.10 chunk_t数据结构
      • 4.10.1 chunk_t是什么数据结构
      • 4.10.2 chunk_t应用场景
      • 4.10.3 chunk_t接口功能
    • 4.11 HTTP请求/响应的发送流程(writev/sendfile)
    • 4.12 ejet日志系统
    • 4.13 Callback回调机制
      • 4.13.1 ejet回调机制
      • 4.13.2 ejet全局回调函数
      • 4.13.3 ejet动态库回调
      • 4.13.4 回调函数使用HTTPMsg的成员函数
    • 4.14 正则表达式的使用
    • 4.15 TLS/SSL
      • 4.15.1 TLS/SSL、OpenSSL介绍
      • 4.15.2 ejet集成OpenSSL
    • 4.16 Chunk传输编码解析
    • 4.17 反向代理和正向代理
      • 4.17.1 判断是否为代理请求
      • 4.17.2 代理请求的实时转发
      • 4.17.3 代理响应的实时转发
    • 4.18 FastCGI机制和启动PHP的流程
      • 4.18.1 FastCGI基本信息
      • 4.18.2 ejet如何启用FastCGI
      • 4.18.3 FastCGI的通信规范
      • 4.18.4 FastCGI消息的实时转发
    • 4.19 两个通信连接的串联Pipeline
      • 4.19.1 两个通信连接串联成管道
      • 4.19.2 两个连接速度不对等导致的流量拥塞
    • 4.20 HTTP Cache系统
      • 4.20.1 HTTP Cache功能设置
      • 4.20.2 ejet系统Cache存储架构
      • 4.20.3 ejet系统缓存处理流程
    • 4.21 HTTP Tunnel
    • 4.22 HTTP Cookie机制
  • 五. ejet为什么高性能
    • 5.1 事件驱动多线程ePump框架
    • 5.2 零拷贝Zero-Copy技术
    • 5.3 使用writev和sendfile提升发送效率
    • 5.4 内存池
    • 5.5 超大文件上传
    • 5.6 不连续碎片存储读写chunk_t
  • 六. ejet Web服务应用案例
    • 6.1 大型资源网站
    • 6.2 承载PHP应用
    • 6.3 充当代理服务器
    • 6.4 Web Cache服务
    • 6.5 作为CDN边缘分发
    • 6.6 应用程序集成ejet
  • 七. ejet相关的另外两个开源项目
    • adif 项目
    • ePump项目
  • 八. 关于作者 老柯 (laoke)

一. ejet是什么?

ejet Web服务器是利用 adif数据结构和算法库 和 ePump框架,用C语言开发的一个事件驱动模型、多线程、大并发连接的轻量级的高性能Web服务器,支持HTTP/1.0和HTTP/1.1协议,并支持HTTP Proxy、Tunnel等功能。

在Linux下,ejet Web服务器编译成动态库或静态库的大小约为300K,可集成嵌入到任何应用程序中,增加应用程序使用HTTP通信和服务承载的能力,使其具备像Nginx服务器一样强大的Web功能。

ejet Web服务器完全构建在ePump框架之上,利用ePump框架的多线程事件驱动模型,实现完整的HTTP请求<–>HTTP响应事务流程。ejet并没有创建进程或线程,利用ePump框架的事件驱动多线程,高效地运用服务器的CPU处理能力。

ejet接收和处理各TCP连接上的HTTP请求头和请求体,经过解析、校验、关联、实例化等处理,执行HTTP请求,或获取Web服务器特定目录下的文件,或代理客户端发起向源HTTP服务器的请求,或将HTTP请求通过FastCGI接口转发到CGI服务器,或将客户端HTTP请求交给上层设置的回调函数处理等。所有处理结果,最终以HTTP响应方式,包括HTTP响应头和响应体,通过客户端建立的TCP连接,返回给客户端。该TCP连接可以Pipe-line方式继续发送和接收多个HTTP请求和响应。

ejet服务器提供了作为Web服务器所需的其他各项功能,包括基于TLS/SSL的安全和加密传输、虚拟主机、资源位置Location的各种匹配策略、对请求URI执行动态脚本指令(包括rewrite、reply、return、try_files等)、在配置文件中使用HTTP变量、正向代理和反向代理、HTTP Proxy、FastCGI、HTTP Proxy Cache功能、HTTP Tunnel、MultiPart文件上传、动态库回调或接口函数回调机制、HTTP日志功能、CDN分发等。

ejet Web服务器采用JSon格式的配置文件,进行系统配置管理。对JSon语法做了一定的扩展,使得JSon支持include文件指令,支持嵌入Script脚本程序语言。使用扩展JSon功能的配置文件,可更加灵活、方便地扩展Web服务功能。

ejet系统大量采用了Zero-Copy、内存池、缓存等技术,来提升Web服务器处理性能和效率,加快了请求响应的处理速度,支撑更大规模的并发处理能力,支持更大规模的网络吞吐容量等。

ejet Web服务器既可以面向程序员、系统架构师提供应用程序开发接口或直接嵌入到现有系统中,也可以面向运维工程师部署完全类似Nginx Web服务器、Web Cache、CDN回源等商业服务系统,还是面向程序员提供学习、研究开发框架、通信系统等的理想平台。

开发ejet Web服务器的原则是尽可能不依赖于第三方代码和库,降低版权和复杂部署等因素带来的潜在风险。系统使用的第三方代码或库主要为:OpenSSL库、Linux系统自带的符合POSIX标准的正则表达式regex库。gzip压缩需要依赖zlib开源库,目前没有添加进来,所以ejet Web服务器暂时不提供gzip、deflate的压缩支持。

二. ejet系统流程和工作原理

2.1 Web服务器基本功能

Web服务器,常称网站服务器,将具有独立IP地址的服务器上承载的图、文、音频、视频和数据文件等资源,以URL方式来标识,以HTTP协议对外提供下载、浏览、上传、交互等服务。HTTP协议是一个请求-响应的事务型协议,Web服务器的基本工作流程基本是围绕请求应答来完成的,具体包括:

  • 建立连接 —— 接受客户端连接,或拒绝并关闭连接;
  • 接收请求 —— 从网络接口中读取 HTTP 请求头和请求体;
  • 处理请求 —— 对请求头和请求体进行解析;
  • 访问资源 —— 访问HTTP请求指定的资源;
  • 构建响应 —— 创建带有头和体的 HTTP 响应;
  • 发送响应 —— 将响应回送给客户端;
  • 记录日志 —— 与已完成事的内容记录在日志文件中。

ejet Web服务器也是按照上述流程来设计和实现的,在这些环节中,增加了各种应用程序或配置接口,更好地满足服务需求,及为提升服务性能而设计了很多功能模块。

2.2 ejet Web服务器启动流程

在启动系统时,首先为各模块分配空间和资源,解析和读取系统配置信息,初始化各模块,并构建各模块数据结构,包括:

  • 初始化SSL库模块;
  • 根据配置,URI在解码/编码时,设置需要进行转义处理的字符的位掩码;
  • 初始化HTTP请求代理,向Origin服务器发送HTTP请求时,根据配置信息,设置Proxy地址和端口
  • 初始化HTTP访问日志模块;
  • 初始化HTTP连接、HTTP消息等核心数据结构,并分配内存池;
  • 加载HTTP MIME数据,并初始化MIME管理模块;
  • 初始化HTTP Cache缓存管理模块;
  • 初始化HTTP变量和变量管理模块;
  • 设置并初始化HTTP响应状态码管理;
  • 初始化Origin服务器管理模块;
  • 构建SSL Context上下文环境;
  • 初始化HTTP Overhead流量统计模块;
  • 加载并初始化FastCGI模块;
  • 初始化HTTP Cookie模块;
  • 根据系统配置,初始化并加载HTTP Listen监听服务、虚拟主机服务、资源定位服务模块,包括配置中设定的Script脚本、配置选项等;

2.3 启动监听服务

ejet系统启动HTTP监听服务时,调用ePump框架提供的接口函数eptcp_mlisten,设置事件回调函数为http_pump,那么监听端口的所有事件都会发送到http_pump中,包括TCP连接建立请求等,http_pump是ejet系统事件驱动模型的核心。

要特别说明的是ePump框架中提供的接口函数eptcp_mlisten封装了很多操作系统细节,自动识别当前操作系统是否支持REUSEPORT,以及内核级针对多线程监听时的TCP连接事件在多线程(多进程)间的负载均衡,如果支持,就启用这个内核功能,如果不支持,就启用ePump系统实现的用户态下的TCP连接事件多线程负载均衡,最终确保高效利用CPU的并发处理能力。

2.4 http_pump作为事件驱动核心入口

ejet系统依赖ePump多线程事件驱动框架,本身不创建线程,也不创建进程,通过ePump的事件回调机制,处理所有来自ePump的事件,这是典型的事件驱动event-driven架构。

ejet系统将http_pump设置为ePump框架的事件回调入口函数,所有底层通信设施和定时器产生的任何事件,都会送到http_pump函数来处理,函数原型如下:

int http_pump (void * vmgmt, void * vobj, int event, int fdtype)

vmgmt是HTTPMgmt实例对象,vobj是产生该事件的iodev_t设备或定时器,event是事件类型,fdtype是iodev_t的文件描述类型。

2.5 IOE_ACCEPT事件驱动ejet创建HTTPCon连接

ejet系统的服务起点是http_pump模块接收到客户端的TCP连接请求,即IOE_ACCEPT事件,响应该事件的处理流程是创建HTTPCon连接,保存客户端请求的各种信息,包括iodev_t socket设备对象、四元组地址、是否为SSL连接等,启动定时器来管理当前连接的寿命,并将当前TCP连接的iodev_t设备绑定到一个负载最低的epump线程中,通过该线程监听并创建该TCP连接后续各类读、写等事件。

2.6 IOE_READ事件驱动ejet读取请求数据

客户端发起的TCP连接接受成功后,客户端开始发送HTTP Request,ejet系统http_pump模块会收到IOE_READ事件,即http_pump会被ePump的工作线程回调执行。响应该事件的处理流程是根据回调参数得到HTTPCon实例,如果是普通的TCP连接,则调用TCP非阻塞、零拷贝接口函数,读取内核中该连接上的数据存入缓冲区,如果是SSL连接,则调用SSL接口函数完成SSL握手、认证过程,并在加密连接上读取解密过的数据存入缓冲区。

2.7 解析HTTP请求数据

对HTTPCon内缓冲区的数据进行解析处理,根据HTTP协议规范约定,HTTP请求头和请求体之间有两个空行即4字节\\r\\n\\r\\n,因为请求体的内容是否存在、是什么内容格式、大小等信息都包含在HTTP请求头中。首先通过快速字符串模式匹配算法,定位出缓冲区数据中是否存在\\r\\n\\r\\n,即找到HTTP Request中头信息的结尾后,对请求行、请求头进行解析。

2.8 创建HTTPMsg保存请求数据

创建HTTPMsg实例,保存HTTP请求的各项信息,将HTTP请求头字节流存入HTTPMsg中的req_header_stream,基于这个流,解析出HTTP Method、URL、HTTP_VERSION和所有的HTTP请求头,请求头保存在hashtab中,方便快速查找。对特定的请求头做特殊解析处理,如针对Range头,需根据Range规范解析出客户端想要目标资源的部分内容,起始位置、长度等;需根据Cookie头来解析出各个Cookie信息;Connection头表示客户端希望当前TCP连接是否为Long-lived连接;Content-Type头中,如果存在multipart内容,则需以该格式解析后续的请求体;最主要的是请求体内容的传输编码格式是什么,需要Conent-Length头或Transfer-Encoding头来决定。

要强调一下的是,请求头信息都是小碎片内存开销,ejet系统采用内存池和保存偏移量等方式,降低了使用大量碎片内存分配导致的内存使用效率风险。

针对请求行、请求头信息解析成功后,构建并设置URL绝对地址,并将当前HTTPMsg实例对象加入到HTTPCon的消息队列中。校验当前请求内容是否合法,非法或不支持的请求,直接返回404或505等错误,并终止当前HTTPCon连接。

2.9 设置DocURI并启动HTTPMsg资源实例化

此刻,需启动HTTPMsg请求资源实例化流程,先设置当前请求URL地址为DocURI,得到当前HTTP请求的主机名、端口、路径、Query参数等信息。在当前监听服务HTTPListen下,根据HTTP请求的主机名,利用HTTPListen和HTTPHost管理结构,找到系统配置的虚拟主机对象HTTPHost,随后根据路径名,基于当前虚拟主机下资源位置的配置规则,将当前请求路径按照精准匹配、前缀匹配、正则表达式匹配等去匹配虚拟主机下的多个资源位置HTTPLoc。匹配成功后,分别指向HTTPListen监听服务下、虚拟主机HTTPHost下、资源位置HTTPLoc下的脚本程序,脚本程序中可能会包含rewrite、try_files等导致循环嵌套执行HTTPMsg请求资源实例化的指令。注:嵌套执行的总次数不超过16次。

完成了请求内容的解析和HTTPMsg请求资源实例化后,在接收并解析请求体之前,需要做两个判断:一是判断当前请求是否采用正向代理Forward Proxy或者反向代理Reverse Proxy进行转发;二是判断当前请求是否要使用FastCGI转发。

2.10 Proxy代理转发

2.10.1 判定是否代理转发—正向代理或反向代理

判断是否为Proxy代理转发,如果请求URI是绝对URL地址,即URL地址包含了http://domain:port,并且其URL中的地址不是当前主机,那么当前HTTP请求需要做正向代理转发处理,将当前地址作为正向代理转发地址。如果HTTPMsg中的资源位置HTTPLoc中在配置文件中设置了反向代理,获取配置中的反向代理URL地址,如果配置中是正则表达式匹配,则需对反向代理URL地址做正则匹配后的变量替换处理,如果不是正则表达式匹配,则从请求路径中删除掉匹配子串,剩余部分添加加到配置中的反向代理URL中。最后,将HTTP请求的query参数添加到反向代理URL后面,并最终确认为反向Proxy代理转发。

2.10.2 代理请求资源是否在本地缓存里

如果是Proxy代理转发,首先检查转发的请求资源是否已经保存在本地缓存了,其方法是检查当前HTTPLoc资源位置中Cache选项是否打开、CacheFile缓存文件是否设置,如果没有HTTPLoc资源位置,判断发送HTTP请求的配置项里Cache开关和CacheFile是否设置,CacheFile一般含有HTTP变量,经过变量取值处理后的文件为缓存文件。缓存文件如果存在,则资源已在本地,否则继续检查该缓存文件对应的缓存信息管理文件CacheInfo是否存在,如果存在的话,则将当前请求的资源内容区域,与CacheInfo中已经保存的缓存区域比对,如果请求区域的内容都保存在临时缓存文件中,则请求资源已经保存在本地。

2.10.3 创建代理请求消息

如果请求资源已经保存在本地缓存中,则跳过当前Proxy代理转发流程,进行下一步处理。如果不在本地缓存,则创建一个新的代理请求HTTPMsg消息,设置代理请求URL地址,将源请求HTTPMsg的请求头复制到Proxy请求的HTTPMsg中,根据CacheInfo判断,如果本地缓存中保存了一部分请求内容,那么需要从剩余的未保存内容开始,设置Range请求头,去请求未保存部分,不用重新请求已经缓存了的内容。

2.10.4 打开源服务器并建立通信连接

根据请求的目标IP地址和端口,打开或创建代表Origin服务器的HTTPSrv实例,并在HTTPSrv下创建或复用一个新的TCP连接HTTPCon,将代理消息HTTPMsg交给该TCP连接来发送,并设置当前TCP连接的任何后续读写事件,都采用当前工作线程,即跟源请求的TCP连接一起都由一个worker工作线程来处理事件,以确保处理环节的连续性pipeline。

2.10.5 发送代理请求到源服务器

在面向Origin服务器的代理TCP连接上发送代理HTTPMsg消息,过程比较复杂。不仅仅要转发请求头,还要把源HTTP请求的消息体转发给目标服务器。由于传输网络的抖动、延迟、请求消息体数据大小不一、传输编码差异等因素,请求消息体不一定都存储在本地缓冲区,也不一能来一个数据立即转发一个,需要做解析、碎片化发送等处理,具体算法和流程见后面章节。完成Proxy代理转发后,当前客户端HTTP请求的读事件处理完毕。

2.11 FastCGI转发

如果当前请求不是Proxy代理转发,则需继续判断是否为FastCGI转发。根据HTTPMsg中的资源位置HTTPLoc的配置信息,判断是否为FastCGI转发模式,如果是FastCGI转发模式,则将转发URL,即FastCGI服务器地址URL返回出来。否则跳过FastCGI转发流程。

如果是FastCGI转发,首先使用FastCGI服务器URL地址,打开或创建FastCGI主机实例FcgiSrv,随后创建FastCGI转发消息FcgiMsg,打开或创建到达FastCGI服务器的Unix Socket连接或TCP连接FcgiCon,并将该消息绑定到连接上,启动发送流程。

2.12 读取并解析请求体

如果当前HTTP请求消息既不是Proxy代理转发,也不是FastCGI转发,则根据请求头判断是否有后续的消息体需要读取、解析和存储等处理。如果请求头中包含Content-Length,并且值大于0,或者包含Transfer-Encoding头,则需把当前HTTPCon缓冲区内容拷贝到HTTPMsg的请求体缓冲区req_body_stream中,或者根据系统配置文件中receive request中的body cache开关,以及是否启动Cache缓存的消息体大小阈值,来决定请求体是否启用Cache缓存,如果启用了请求体缓存,并请求消息体大小也大于缓存阈值,则将当前请求体的内容都存入到缓存文件中。对于Chunk编码的消息体,需要解析解码处理。将请求体内容,无论是在内存中,还是在缓存文件里,都添加到不连续碎片存储管理结构chunk_t中。随后,根据请求头Content-Type值,对请求体内容进行处理,如果是multipart格式,则将消息体各部分的内容解析到http_form_t中,如果是urlencoded格式,则解析到kev-value键值对链表中,如果是JSon格式,则解析成JSon数据结构,而解析出来的这些内容的存储结构都是HTTPMsg对象的成员。

2.13 由Handler处理HTTPMsg

2.13.1 校验请求方法

处理完消息头或消息体后,封装了客户端请求所有信息的HTTPMsg会交给Handler即http_msg_handle来处理。Handler判断请求Method方法,如果是CONNECT时,建立TCP隧道,并返回隧道建立结果状态,生成HTTP响应发送给客户端。如果不是系统支持的方法,如GET、POST、DELETE、PUT、HEAD、OPTIONS、TRACE等,则直接返回405错误。

2.13.2 反向Proxy模式缓存内容处理

Handler处理这些请求方法时,首先判断当前请求内容是否是Proxy模式下的缓存内容,如果是缓存内容,则将缓存文件添加到用于存储响应体的不连续碎片存储管理数据结构res_chunk_body中,当客户端请求时只是部分区域内容,添加到响应体时会自动根据请求头的Range头规范,把指定区域的缓存内容添加到响应体,设置响应状态码和响应内容类型,生成HTTP响应,并启动HTTP响应的发送流程发送给客户端。

2.13.3 正向Proxy模式校验

如果HTTP请求的不是Proxy模式下的缓存内容,先检查一下当前请求是不是正向代理转发,并且当前HTTPListen监听的系统配置里不允许正向代理转发,返回403错误。再检查当前HTTPMsg实例中资源位置HTTPLoc是否存在,不存在则返回404错误。

2.13.4 HTTP消息应用回调和动态库回调处理

针对请求各项检查完毕后,先看看是否设置了系统级的应用回调函数,如果存在,将当前HTTP请求的HTTPMsg实例对象作为参数,执行应用级的回调函数。随后,判断该HTTPMsg是否被成功地处理并发送了HTTP响应,如果尚未发送响应,则检查当前HTTPListen是否配置了动态库回调函数,如果存在,则加载动态库并执行动态库的回调函数。

2.13.5 读取并发送资源文件或路径下的缺省文件

到这里,如果该HTTP请求的HTTPMsg还没有被处理并发送响应,则根据其资源位置HTTPLoc,获取其资源文件路径,判断该路径的资源文件是否存在,如果存在,则将该文件添加到响应体的res_chunk_body中,启动HTTP响应的发送流程。如果资源文件不存在,但资源文件路径是一个目录,根据资源位置HTTPLoc下配置的缺省文件列表,逐个查找该目录下的缺省文件是否存在,如果存在,则将该文件添加到响应体的res_body_chunk中,并发送给客户端。

最后,如果该HTTP请求的HTTPMsg还没有被各级接口成功地处理和发送响应,把404错误码发给客户端,ejet不知道客户端想要什么!

2.14 发送响应到客户端

到此为止,客户端请求数据通过IOE_READ事件,驱动ejet服务器的全部工作流程,只剩下最后环节,发送HTTP响应给客户端,ejet系统中发送HTTP响应的入口是Reply函数。当然大量的工作是检查响应头是否完整、响应数据是否一致、状态码是否呼应了响应体内容等,需要补充添加或删除响应头等操作,完成响应头数据处理后,对响应数据进行编码。编码过程中,如果发现请求时要求响应内容进行压缩,则根据客户端支持的压缩算法,对响应体内容进行压缩处理。响应头进行编码后的字节流保存在res_stream中,并添加res_body_chunk中的最前面。调用发送函数,将res_body_chunk中的内容发送给客户端,具体细节流程参见后面章节。

2.15 用状态机处理不完整请求

以上IOE_READ事件驱动流程,是比较理想的通信过程,通常情况没有那么乐观,由于网络抖动、传输延迟、大并发访问等因素,会导致一个客户端请求数据(包括请求头和请求体)并不是一次性到达,也就是http_pump收到IOE_READ事件时,很多情况下是一个不完整的HTTP请求数据包,甚至多次收到IOE_READ事件并读取数据时到缓冲区时,也是不完整的,需要以上各个处理流程和环节,记录处理状态,基于有限状态自动机FSM模型,在不同状态下完成不同的处理动作。

2.16 设置可写通知产生IOE_WRITE事件处理网络拥塞

在调用发送函数发送数据到对方时,由于网络拥塞、带宽不足、客户端处理能力有限等因素,导致Web服务器内核的TCP发送缓冲区已满,后续数据不可写入了,需要调用ePump框架的iodev_add_notify函数,对当前TCP连接设置可写通知监听(writable readiness notification)。即ePump框架监控到该TCP连接一旦可写入数据了,ePump框架就会发送IOE_WRITE事件,驱动http_pump来执行可写入处理,依然根据回调参数得到HTTPCon实例,基于HTTPCon上当前正在发送中的HTTPMsg对象,得到该请求或响应的发送信息,继续将rex_body_chunk中的内容发送到对方,具体TCP发送流程参见后面章节。

2.17 利用定时器产生的IOE_TIMEOUT事件监管实例对象

在处理请求和响应时,ejet系统会启动定时器来管理实例对象的寿命周期,如创建HTTPCon对象时,会启动一个life定时器,跟踪管理当前连接的寿命,即所有数据处理完毕且没有其他数据需要处理时,该HTTPCon连接存续10秒左右后,就需要关闭,这是基于定时器来实现的业务流程。

当定时器超时后,ePump框架产生IOE_TIMEOUT事件,回调http_pump函数。根据回调参数,获取到定时器类型、定时器绑定的对象,譬如是HTTPCon连接对象还是Origin服务器HTTPSrv对象等,再分别调用各个实例对象的定时器超时处理逻辑。ejet系统定义了4类定时器,接收客户端请求的HTTPCon连接管理定时器、主动连接Origin服务器时建立的HTTPCon连接管理定时器、主动连接远程主机时以防超时的定时器、Origin服务器HTTPSrv寿命管理定时器。

三. ejet系统基本数据结构

ejet Web服务器系统虽然使用标准C语言编写实现的,但系统数据结构的设计遵循了面向对象思想,将管理不同功能的数据属性和操作抽象出来,封装成标准的数据结构和函数实现,以实例对象方式,将实现的各级功能模块加载驻留,并被其他模块继承或复用。

根据ejet Web服务器功能目标,围绕HTTP请求-响应基本事务流程,设计了如下基本数据结构:

  • HTTPMgmt — ejet管理入口
  • HTTPMsg — 封装请求和响应信息的HTTP消息
  • HTTPCon — HTTP通信连接管理
  • HTTPListen — HTTP监听服务
  • HTTPHost — HTTP虚拟主机
  • HTTPLoc — HTTP资源位置
  • HTTPHeader — HTTP头信息
  • HTTPUri — HTTP URI管理
  • HTTPVar — HTTP变量
  • HTTPLog — HTTP日志
  • CacheInfo — HTTP缓存信息管理
  • HTTPForm — HTTP表单管理
  • HTTPScript — HTTP脚本
  • HTTPSrv — HTTP Origin服务器管理
  • HTTPChunk — HTTP Chunk数据管理
  • HTTPCookie — HTTP Cookie数据
  • FcgiSrv — FastCGI服务器管理
  • FcgiCon — FastCGI通信连接
  • FcgiMsg — 封装FastCGI请求和响应的消息

3.1 HTTPMgmt – ejet内核

整个ejet系统使用HTTPMgmt数据结构作为统一的资源管理入口,所有的配置、内存管理、监听管理、消息、缓存等实例对象都在HTTPMgmt下。

3.2 HTTPMsg – 消息

每个HTTP请求和响应的事务信息由HTTPMsg实例对象来存储和管理,HTTPMsg对象内容HTTP请求的请求头、请求体、响应头、响应体、虚拟主机、资源位置、缓存、代理等信息。成功返回HTTP响应给客户端后,HTTPMsg被回收销毁。

3.3 HTTPCon – 通信连接

客户端发起建立的TCP连接、ejet主动对外发起HTTP请求建立的TCP连接,都是由HTTPCon数据结构来管理。HTTPCon管理TCP连接四元组地址、当前正在接收处理或排队处理的HTTPMsg列表、HTTP隧道、TCP连接iodev_t设备对象等信息;还有HTTP连接的工作状态、连接寿命等管理;最主要的是维持一个可动态扩展的接收缓冲区,接收来自对方的任何数据进行解析和处理。

3.4 HTTPListen – 监听服务

HTTPListen数据结构管理HTTP监听的本地地址和本地端口,是否需要SSL连接,SSL所需的证书、私钥、CA校验证书,端口监听实例对象,每个HTTPMsg产生后需进行实例化,会执行Listen级别的Script脚本,每个HTTPListen下属多个HTTPHost主机,等等。接受的HTTPCon连接和HTTPMsg消息实例,必须关联到某个HTTPListen中。

3.5 HTTPHost – 虚拟主机

HTTPHost代表Host虚拟主机,同一个IP地址和端口下(HTTPListen)可以有很多个虚拟主机,即可以承载很多不同域名的网站。Host主机名既可以是映射到本机IP地址的域名,也可以直接是本机IP地址,HTTP请求头中的Host值就对应相应的Host主机。HTTPHost管理的数据包括主机域名或IP地址、反向代理URL地址、访问文件系统的根路径、通过SSL的SNI机制选择不同的域名下的证书、私钥,以及管理多个HTTPLoc,对HTTPMsg实例化处理时进行URI信息的精准匹配、前缀匹配、正则表达式匹配等来确定最终的HTTPLoc,还有400-520之间错误状态码页面管理等等。

3.6 HTTPLoc – 资源位置

HTTPLoc代表访问资源的最终位置,通过请求URI的文档路径来唯一标识,定位资源所在的最终位置,是客户端的连接请求是哪个监听端口,就确定属于哪个HTTPListen,根据HTTP请求头的Host值,匹配出HTTPHost虚拟主机,然后根据DocURI下的请求路径,分别采用精准匹配、前缀匹配、正则表达式匹配来找到最终的HTTPLoc资源位置。HTTPLoc信息包括:匹配路径和匹配模式、资源位置类型(Server、Proxy、FastCGI)、资源根路径、代理URL、Cache文件、执行脚本等。

3.7 HTTPHeader – 头信息

HTTP头信息是由name和value键值对构成,中间使用冒号(:)分隔,其中Name只能是字母和下划线组成。管理每条头信息的数据结构为HTTPHeader,分别包括头信息的Name和Value信息。需要注意的是,ejet系统中HTTPHeader内的name和value都是指针,指向HTTPMsg中的header_stream,并没有分配实际存储空间,这也是Zero-Copy思想的一部分。

3.8 HTTPUri – 资源地址URL

对URI进行存储和解析处理的数据结构是HTTPUri,ejet系统在接收到客户端请求后,一般在HTTPMsg中有三个HTTPUri实例对象来保存三种URI,一个是请求URI,第二个是经过资源位置实例化后的DocURI,第三个是绝对URI。设置URI后,会自动地将该URI解析分解到不同的字段中,如Host、Port、Path、Query、File等。

3.9 HTTPVar – HTTP变量

HTTPVar变量是指在ejet服务器运行期间,可通过Script脚本程序或配置文件里动态地读取访问当前HTTP请求响应相对应的HTTPMsg实例对象内特定数据的变量,一般在配置文件、访问日志等地方需要动态地配置或使用这些变量。HTTPVar变量包括全局变量、局部变量、Location变量,变量的引用必须以$开头,后跟变量名,如果变量后面还有连续紧随的其他字符串,则需用{}来包括住变量名。

3.10 HTTPLog – 日志信息

每个HTTP请求和响应的信息都要写入日志文件,方便运维和其他统计系统进行处理和分析。HTTPLog数据结构保存日志文件名、文件句柄、要写入日志文件的字段列表等信息,待写入日志文件的字段采用HTTPVar变量方式,在配置文件中设定。HTTPMsg在关闭之前,将配置文件设定的这些变量内容,从HTTPMsg实例变量以及其他实例对象中提取出来,统一写入access.log日志文件中。

3.11 CacheInfo – 缓存信息管理

在正向代理或反向代理模式下,客户端的请求都会转移到Origin源服务器,并将Origin服务器的响应转发给客户端。大量的客户端请求,将会导致转发和转收的效率不高,需要采用HTTP Cache系统,将响应内容存储在本地。HTTP Cache存储系统是由Raw缓存文件和缓存信息管理文件组成,Raw缓存文件负责存储实际的文件介质内容,缓存信息管理文件与数据结构CacheInfo一致,每个缓存文件都有一个全局唯一的CacheInfo,管理缓存的各种信息,包括缓存文件名、缓存文件的MIME类型、文件大小、实际缓存的文件大小、缓存策略、文件创建和更新时间,最主要成员是FragPack,记录Raw缓存文件里的所有已下载碎片块存储的位置和大小,确保哪个位置区域是否保存了文件内容。系统根据CacheInfo,可精准地知道缓存文件的存储信息和存储内容读写访问。

3.12 HTTPForm – 表单信息

客户端采用HTTP Post方法上传多个需要用户输入的信息内容到Web服务器,包括上传本地文件等,这种情况一般采用Content-Type为multipart/form-data的内容编码方式,各内容之间用客户端随机产生的boundary字符串分隔。ejet Web服务器设计了HTTPForm数据结构来管理multipart/form-data内容的解析和存储,一般情况下,上传内容过大会自动保存到缓存文件里,HTTPForm接口函数对缓存文件进行解析,分别解构出各个字段的名称,该字段的内容在缓存文件中的位置和大小,如果该字段是文件,则记录文件名,以及该上传文件在缓存文件中的起始位置和大小。应用程序可以使用HTTPForm来访问客户端上传的各个字段名称和字段内容,如果上传的是文件,可调用HTTPForm接口,将缓存文件中相应区域的内容提取出来,写入到应用程序指定的目录中。

3.13 HTTPScript – 脚本程序

ejet Web服务器的配置文件采用JSon格式,在http.listen(对应HTTPListen对象)、http.listen.host(对应HTTPHost对象)、http.listen.host.location(对应HTTPLoc对象)这三种对象下,通过对JSon语法进行扩展,都可以配置增加script对象,script对象的内容格式是参考C语言语法规范的脚本程序,由ejet系统在特定时刻解释并执行。当客户端发起的每个HTTP请求到达ejet Web服务器时,ejet解析请求并创建HTTPMsg对象,HTTPMsg对象实例化过程主要是根据请求信息匹配和设定HTTPMsg自己的HTTPListen、HTTPHost、HTTPLoc,在匹配到这三个对象的那一刻,ejet系统首先会调用HTTPScript接口来解释执行这三个对象下配置的Script对象中的脚本程序。像rewrite、return、reply、try_files、if、else等指令和语法都是脚本程序的基本内容。脚本程序的动态执行使得客户端的请求,以更加灵活机动的方式被处理。

3.14 HTTPSrv – 源服务器

ejet Web服务器可充当HTTP客户端向远程HTTP服务器发起HTTP请求,或充当代理模式发起HTTP请求,建立的HTTPCon连接主要是根据目标IP地址和端口来区分,具有相同目的IP地址和端口的HTTPCon请求,可以复用来发送后续相同地址的HTTP请求,这是用HTTPSrv数据结构来管理这些具有相同目的IP地址和端口的HTTPCon连接,这样,HTTPSrv就代表了HTTP Origin服务器。HTTPSrv包含一个HTTPMsg请求消息队列,当有HTTP请求发送到某个HTTP Origin服务器时,直接将该请求消息添加到该队列中,HTTPSrv会在当前连接中均衡分配一个HTTPCon或创建一个新的HTTPCon,来发送该请求。

3.15 HTTPChunk – HTTP数据块

HTTP请求和响应的消息体Body在传输过程有两种方式来标识或编码,一种是采用Content-Length,另一种是采用Transfer-Encoding为chunked的传输编码。后者是将Body数据分成一个个Chunk数据块,每个数据块前头是16进制的数据块长度和两个换行符\\r\\n,chunk数据块后面再跟两个换行符\\r\\n,最后是以长度为0的Chunk数据块来结尾。这种编码方式特别适合不知道实际长度的内容的传输,如实时压缩的内容、直播流媒体内容等。ejet系统设计HTTPChunk数据结构来解析、存储Chunk数据块内容,尤其是连续传输的Chunk数据块因为网络抖动等原因断续接收时,需要HTTPChunk结构来跟踪Chunk块状态。

3.16 HTTPCookie – Cookie数据

OSI七层协议模型中,作为Transaction事务层的HTTP协议是State-less协议,并没有保存通信双方的会话状态,而是通过Cookie机制来维持Session。Web Server通过在HTTP Response头中增加Set-Cookie头来设置Cookie信息,客户端在随后的HTTP Request请求中增加Cookie头将Cookie信息携带上。Cookie内容是由很多个name/value键值对组成,同一个Cookie下不同的name/value键值对是用分号(;)隔开,应用服务器可以根据Cookie键值对来保存会话状态,譬如id、登录会话串等。在Set-Cookie规范设置Cookie时,每个Cookie的属性中必须包含Domain、Path,指定该Cookie属于哪个域名主机的哪个路径,一般还包含max-age和expires,指定该Cookie的寿命周期。ejet系统通过HTTPCookie来解析、保存和管理这些Cookie信息。

3.17 FcgiSrv – FastCGI服务器

HTTP请求的资源如果是PHP等内容时,ejet会启动FastCGI模块,将HTTP请求内容通过FastCGI协议发送到FastCGI服务器,如php-fpm,FastCGI服务器执行脚本程序后,将响应内容通过FastCGI协议返回给ejet Web服务器,组装成HTTP响应返回给客户端。通常FastCGI服务器跟ejet Web服务器位于同一台服务器上,与FastCGI服务器之间的通信,一般采用进程间通信IPC机制,如Unix Socket或TCP协议。ejet采用FcgiSrv数据结构来标识管理FastCGI服务器,服务器的标识一般为 unix:/dev/shm/php-cgi.sock 或 fastcgi://127.0.0.1:9000。每个FcgiSrv的角色类似HTTPSrv,代表某个FastCGI服务器,一个ejet Web服务器可以部署配置多个FastCGI服务器。每个FastCGI服务器管理FcgiMsg消息队列,建立和维持多个FastCGI协议的连接FcgiCon。

3.18 FcgiCon – FastCGI通信连接

通过Unix Socket或TCP协议,来承载FastCGI协议的通信服务,用FcgiCon数据结构来管理每一个可靠通信连接。首先需要维持一个通信接收缓冲区,任何来自对方的数据,通过事件通知驱动接收接口,存储在缓冲区中用于解析和处理。其次保存该连接的iodev_t对象,以及维持连接所需的定时器,当前正在发送或接收的FcgiMsg消息,或管理处于FIFO排队队列中的消息。每个FcgiCon隶属于FcgiSrv,一个FcgiSrv下有多个FcgiCon,共同均衡地承担传输任务。

3.19 FcgiMsg – FastCGI消息

FastCGI协议定义了传输数据规范,数据是由一个到多个FastCGI Record构成,Record都是8字节对齐,每个Record包含一个8字节长的头部,最大不超过65KB大小的可变长度消息体,最后是为了补齐8字节对齐所需的padding字节。FastCGI协议Record类型共有10种,分别为BEGIN_REQUEST、ABORT_REQUEST、END_REQUEST、PARAMS、STDIN、STDOUT、STDERR、DATA、GET_VALUES、GET_VALUES_RESULT。FcgiMsg封装了FastCGI数据规范的这些信息,对FastCGI信息内容进行解析或编码,负责传输过程中消息内容状态的跟踪记录,作为FastCGI通信接口的最基本数据传输单元。

四. ejet核心功能模块

4.1 ejet资源管理架构

4.1.1 三层资源定位架构

ejet Web服务器的资源管理结构分成三层:

  • HTTP监听服务HTTPListen – 对应的是监听本地IP地址和端口后的TCP连接
  • HTTP虚拟主机 – 对应的是请求主机名称domain
  • HTTP资源位置HTTPLoc – 对应的是主机下的各个资源目录

一个ejet Web服务器可以启动一个到多个监听服务HTTPListen,一个监听服务下可以配置一个到多个HTTP虚拟主机,一个虚拟主机下可以配置多个资源位置HTTPLoc。这里的‘多个’没有数量限制,取决于系统的物理和内核资源限制。

4.1.2 HTTP监听服务 – HTTPListen

HTTP监听服务HTTPListen是指ejet Web服务器在启动时,需要绑定本地某个服务器IP地址和某个端口后,启动TCP监听服务,等候接收客户端发起TCP连接和HTTP请求数据,每个接受的HTTPCon连接一定属于某个HTTP监听服务HTTPListen。严格来说,HTTPListen负责接受HTTPCon连接,并将请求数据存储到HTTPCon的接收缓冲区,所以监听服务对应的是TC连接资源管理,即对应的是请求资源的domain和端口。

HTTP监听服务的配置信息格式参考如下:

listen = {
    local ip = *; #192.168.1.151
    port = 443;
    forward proxy = on;

    ssl = on;
    ssl certificate = cert.pem;
    ssl private key = cert.key;
    ssl ca certificate = cacert.pem;

    request process library = reqhandle.so

    script = {
        #reply 302 https://ke.test.ejetsrv.com:8443$request_uri;
        addResHeader X-Nat-IP $remote_addr;
    }

    host = {.....}
    host = {.....}
    host = {.....}
}

一台物理服务器可以安装多个网卡,每个网卡配置一个独立IP地址,HTTP监听服务可以监听某一个IP地址上的某个端口,也可以监听所有IP地址上的同一个端口。能启动监听服务的端口数量理论上是65536个,其中小于1024的端口需要有root超户权限才能监听。

HTTP监听服务HTTPListen依赖于底层ePump框架的eptcp_mlisten接口函数,通过该接口,让每一个epump监听线程都去监听指定IP地址和端口上的连接请求和数据请求服务。对于支持REUSEPORT的操作系统内核,大量客户端发起的并发连接,将会通过内核accept系统调用均衡地分摊到各epump线程处理,对于不支持REUSEPORT的操作系统,ePump框架负责大并发连接在各监听线程间的负载均衡。

HTTP监听服务HTTPListen可以设置当前监听为需要SSL的安全连接,并配置SSL握手所需的私钥、证书等。配置为SSL安全连接监听服务后,客户端发起的HTTP请求都必须是以https://开?*?**?的URL。

在HTTP监听服务HTTPListen里,可以设置Script脚本程序,执行各种针对请求数据进行预判断和预处理的指令。这些脚本程序的执行时机是在收到完整的HTTP请求头后进行的。

ejet系统提供了动态库回调机制,使用动态库回调,既可以扩展ejet Web服务器能力,也可以将小型应用系统附着在ejet Web服务器上,处理客户端发起的HTTP请求。

HTTP监听服务HTTPListen下可管理多个虚拟主机HTTPHost,采用主机名称为索引主键的hashtab来管理下属的虚拟主机表。当当前监听服务的端口收到TCP请求和数据后,根据Host请求头的主机名称,来精确匹配定位出该请求的HTTP虚拟主机HTTPHost。

4.1.3 HTTP虚拟主机 – HTTPHost

在HTTPListen监听服务下,可以配置多个虚拟主机,虚拟主机HTTPHost是ejet Web服务器资源管理体系的第二层,将HTTPCon缓冲区的数据进行解析,创建HTTPMsg来保存解析后的HTTP请求数据,HTTP协议规范中,请求头Host携带的值内容是URL中domain信息,所以HTTP虚拟主机HTTPHost,对应的就是请求域名,或者就是一个网站。一个监听服务HTTPListen下可以寄宿大量的通过虚拟主机HTTPHost来管理的网站。

HTTP虚拟主机的配置信息格式参考如下:

host = {
    host name = *; #www.ejetsrv.com
    type = server | proxy | fastcgi;
    gzip = on;

    ssl certificate = cert.pem;
    ssl private key = cert.key;
    ssl ca certificate = cacert.pem;

    script = {
        #reply 302 https://ke.test.ejetsrv.com:8443$request_uri;
        addResHeader X-Nat-IP $remote_addr;
    }

    error page = {
        400 = 400.html;
        504 = 504.html;
        root = /opt/ejet/errpage;
    }

    root = /home/hzke/sysdoc;

    location = {...}
    location = {...}
    location = {...}
}

HTTP虚拟主机的名称一般是域名格式,即多级名称体系,包含顶级域名、二级域名、三级域名等,通过DNS系统,将该域名解析到当前ejet Web服务器所在的IP地址上,如果在该IP地址上启动HTTPListen服务,那么所有使用该域名的请求都会指向到对应的HTTPHost虚拟主机。

ejet系统根据功能服务形式,对虚拟主机定义了几种类型:Server、Proxy、FastCGI等,这几种类型可以同时并存,可或在一起。

虚拟主机HTTPHost下可以设置资源的缺省目录,下属的资源位置HTTPLoc都可以复用虚拟主机的缺省目录。

如果当前虚拟主机HTTPHost的上级监听服务是建立在安全连接SSL上,那么在有多个网站即多个虚拟主机情况下,需要为每个网站配置属于该网站域名的证书、私钥等安全身份标识信息,客户端在向同一个监听服务发送请求后,采用TLS SNI机制和ejet中实现的SSL域名选择回调,来完成域名和证书的选择。

HTTPHost虚拟主机下可以设置Script脚本程序,虚拟主机下的脚本程序被执行时机是在创建HTTPMsg实例,并设置完DocURI后开始执行资源位置实例化流程,在该流程中分别执行HTTPListen的Script脚本、HTTPHost的Script脚本、HTTPLoc的Script脚本。脚本程序的执行按照上述优先级来进行,使用脚本程序的指令来预处理HTTP请求的各类数据。

一个虚拟主机HTTPHost下可以配置多个资源位置HTTPLoc,代表访问当前域名下的不同目录。虚拟主机HTTPHost采用多种方式管理下属的资源位置HTTPLoc实例,主要包括三种:

  • 精确匹配请求路径的虚拟主机表 – 以请求路径名称为索引的资源位置索引表
  • 对请求路径前缀匹配的虚拟主机表 – 以请求路径前缀名称为索引的资源位置字典树
  • 对请求路径进行正则表达式运算的虚拟主机表 – 对正则表达式字符串为索引建立的资源位置列表

进入当前虚拟主机后,到底采用哪个资源位置HTTPLoc,匹配规则和顺序是按照上述列表的排序来进行的,首先根据HTTP请求的路径名在资源位置索引表中精准匹配,如果没有,则对请求路径名的前缀在资源位置字典树中进行匹配检索,如果还没有匹配上,最后对资源位置列表中的每个HTTPLoc,利用其正则表达式字符串,去匹配当前请求路径名,如果还是没有匹配的资源位置HTTPLoc,那么使用当前虚拟主机的缺省资源位置。

4.1.4 HTTP资源位置 – HTTPLoc

HTTP资源位置HTTPLoc代表的是请求资源在某个监听服务下的某个虚拟主机里的目录位置,HTTPLoc代表的是请求路径,根据HTTPMsg中的客户端请求数据,最终基于各种资源匹配规则,找到HTTPListen、HTTPHost、HTTPLoc后,基本确定了当前请求的资源位置、处理方式等。一个网站对应的虚拟主机下,可以有多种功能和资源类别的资源位置HTTPLoc,如图像文件放置在image为根的目录下,PHP文件需要采用FastCGI转发给php-fpm解释器等。

HTTP资源位置的配置信息格式参考如下:

location = {
    type = server;
    path = [ \"\\.(h|c|apk|gif|jpg|jpeg|png|bmp|ico|swf|js|css)$\", \"~*\" ];

    root = /opt/ejet/httpdoc;
    index = [ index.html, index.htm ];
    expires = 30D;

    cache_file = <script>
           if ($request_uri ~* \'laoke\')
               return \"${host_name}_${server_port}${req_path_only}${req_file_only}\";
           else if (!-f $root$request_path) {
               return \"$root$request_path is not a regular file\";
           } else if (!-x $root$request_path) {
               return \"$root$request_path is not an executable file\";
           } else
               return \"${request_header[host]}${req_path_only}else.html\";
         </script>;
}

location = {
    path = [ \'^/view/([0-9A-Fa-f]{32})$\', \'~*\' ];
    type = proxy;
    passurl = http://cdn.*e*j*etsrv.com/view/$1;

    root = /opt/cache/;
    cache = on;
    cache file = /opt/cache/${request_header[host]}/view/$1;
}

location = {
    type = fastcgi;
    path = [ \"\\.(php|php?)$\", \'~*\'];

    passurl = fastcgi://localhost:9000;

    index = [ index.php ];
    root = /opt/ejet/php;
}

location = {
    path = [ \'/\' ];
    type = server;

    script = {
        try_files $uri $uri/ /index.php?$query_string;
    };

    index = [ index.php, index.html, index.htm ];
}

HTTP资源位置HTTPLoc是通过路径名path和匹配类型matchtype来作为其标识,路径名为配置中设置的名称,客户端请求的路径名通过匹配类型定义的匹配规则来跟设置的路径名进行匹配,如果符合匹配,则该请求使用此资源位置HTTPLoc。

匹配规则matchtype一般定义在配置文件中path数组里的第二项,分为如下几种:

  • 精准匹配,使用等于号\’=\’
  • 前缀匹配,使用\’^~\’这两个符号
  • 区分大小写的正则表达式匹配,使用\’~\’符号
  • 不区分大小写的正则表达式匹配,使用\’~*\’这两个符号
  • 通用匹配,使用\’/\’符号,如果没有其他匹配,任何请求都会匹配到

匹配的优先级顺序为:
(location =) > (location 完整路径) > (location ^~ 路径) >
(location ,* 正则顺序) > (location 部分起始路径) > (/)

ejet系统根据功能服务形式,对资源位置HTTPLoc定义了几种类型:Server、Proxy、FastCGI等,通常情况下,一个资源位置HTTPLoc只属于一种类型。

HTTP资源位置HTTPLoc都需要一个缺省的根目录,指向当前资源所在的根路径,客户端请求的路径都是相对于当前HTTPLoc下的root跟目录来定位文件资源的。对于Proxy模式,根目录一般充当缓存文件的根目录,即需要对Proxy代理请求回来的内容缓存时,都保存在当前HTTPLoc下的root目录中。

每个HTTPLoc下都会有缺省文件选项,可以配置多个缺省文件,一般设置为index.html等。使用缺省文件的情形是客户端发起的请求只有目录形式,如http://www.*x*x*x.com/,这时该请求访问的是HTTPLoc的根目录,ejet系统会自动地依次寻找当前根目录下的各个缺省文件是否存在,如果存在就返回缺省文件给客户端。不过需要注意的是,ejet系统中这个流程是在设置DocURI时处理的。

HTTP资源位置如果是Proxy类型或FastCGI类型,则必须配置转发地址passurl,转发地址passurl一般都为绝对URL地址,含有指向其他服务器的domain域名,passurl的形式取决HTTPLoc资源类型。

反向代理(Reverse Proxy)就是将HTTPLoc的资源类型设置为Proxy模式,通过设置passurl指向要代理的远程服务器URL地址,来实现反向代理功能。在反向代理模式下,passurl可以是含有匹配结果变量的URL地址,这个地址指向的是待转发的下一个Origin服务器,匹配变量如果为$1、$2等数字变量,即表示基于正则表达式匹配路径时,把第一个或第二个匹配字符串作为passurl的一部分。当然passurl可以包含任何全局变量或配置变量,使用这些变量可以更灵活方便地处理转发数据。

在反向代理模式下,HTTPLoc资源位置下有一个cache开关,如果设置cache=on即打开Cache功能,则需要在当前HTTPLoc下设置cachefile缓存文件名。对于不同的请求地址,cachefile必须随着请求路径或参数的变化而变化,所以cachefile的取值设置需要采用HTTP变量,或者使用Script脚本来动态计算cachefile的取值。

HTTPLoc下一般都会部署Script脚本程序,包括rewrite、reply、try_files等,根据请求路径、请求参数、请求头、源地址等信息,决定当前资源位置是否需要重写、是否需要转移到其他地址处理等。

4.2 HTTP变量

下载源码

通过命令行克隆项目:

git clone https://github.com/kehengzhong/ejet.git

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 开发教程 ejet https://www.zuozi.net/31928.html

Masuit.MyBlogs
上一篇: Masuit.MyBlogs
aspnet core mvc
下一篇: aspnet core mvc
常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务