FCGI- DENO的FASTCGI协议实现
文档索引
该库允许以下内容:
- 要在具有FASTCGI的Web服务器(例如Apache或Nginx)后面创建DENO后端应用程序。
- 要查询FastCGI服务,例如PHP(如Web服务器)。
- 要创建2个DENO应用程序,并通过FastCGI协议在它们之间进行通信。
FastCGI是一个简单的协议,旨在转发HTTP请求。通常,它用于将不同的HTTP请求转发到从单主/端口上听的一台HTTP服务器的不同应用程序。拥有主HTTP服务器很方便。它允许在一个地方控制所有www动作。
后端应用程序示例(FastCGI服务器)
// File: backend.ts import { fcgi } from \'https://d*e*n*o.land/x/fcgi@v2.1.1/mod.ts\' ; const listener = fcgi . listen ( \'localhost:9988\' , // FastCGI service will listen on this address \'\' , // Handle all URL paths async req => { // Handle the request console . log ( req . url ) ; req . responseHeaders . set ( \'Content-Type\' , \'text/html\' ) ; await req . respond ( { body : \'Hello world!\' } ) ; } ) ; console . log ( `Started on ${ listener . addr . transport == \'tcp\' ? listener . addr . hostname + \':\' + listener . addr . port : listener . addr . transport } ` ) ;
您可以将Web服务器设置为将HTTP请求转发到localhost:9988 ,并且该应用程序将获取它们。
前端应用程序示例(FastCGI客户端)
或者,您可以使用DENO创建Web服务器,并向某些FastCGI服务器提出请求。这可以是php-fpm,也可以是上面显示的DENO后端应用程序。
在下面的示例中,我将使用X/Oak在DeNo中创建HTTP服务器:
// File: frontend.ts import { Application } from \'https://den*o.lan**d/x/oak@v9.0.1/mod.ts\' ; import { fcgi } from \'https://d*e*n*o.land/x/fcgi@v2.1.1/mod.ts\' ; const app = new Application ; app . use ( async ctx => { const resp = await fcgi . fetch ( { addr : \'localhost:9988\' , scriptFilename : \'\' , // PHP requires this parameter to be set to path of an existing *.php file } , new Request ( ctx . request . url . href , { method : ctx . request . method , headers : ctx . request . headers , body : ctx . request . hasBody ? ctx . request . body ( { type : \'stream\' } ) . value : undefined , } ) ) ; ctx . response . status = resp . status ; ctx . response . headers = resp . headers ; ctx . response . body = resp . body ; } ) ; app . listen ( \'localhost:8123\' ) ; console . log ( `Started on http://*localho**st:8123` ) ;
这是运行前两个示例的方法:
deno run --allow-net backend.ts & deno run --allow-net frontend.ts
现在http://l*ocal*ho*st:8123将转发到fcgi://localhost:9988 ,并通过后端申请进行处理。
设置示例:
-
Nginx → Deno:如何设置NGINX HTTP服务器。 -
Apache → Deno:如何设置Apache HTTP服务器。 -
Deno → PHP:如何设置PHP-FPM并转发请求。 -
Deno → spawn-fcgi + Perl:如何设置Perl Fastcgi并向它提出请求。
使用API
该库提供一流的对象,您可以通过该对象进行所有支持的FastCGI操作:启动FastCGI服务器,并对FastCGI服务进行查询。
此对象称为FCGI。
import { fcgi } from \'https://d*e*n*o.land/x/fcgi@v2.1.1/mod.ts\' ;
方法:
听()
⚙fcgi.listen(addr_or_listener:fcgiaddr |侦听器,path_pattern:pathpattern:pathpattern,callback:callback):侦听器
在指定的网络地址上注册FastCGI服务器。该地址可以:
- 端口号(
8000), - 带有可选端口的主机名(
localhost:80000.0.0.0:8000,[::1]:8000,::1), - 一个Unix-Domain插座文件名(
/run/deno-fcgi-server.sock), -
Deno.Addr({transport: \'tcp\', hostname: \'127.0.0.1\', port: 8000}), - 或者也可以给出一个现成的
Deno.Listener对象。
该功能可以多次使用相同或不同的地址调用。带有相同地址的呼叫添加了另一个处理程序回调,该回调将尝试处理匹配请求。使用不同地址的呼叫会创建另一台FastCGI服务器。
第二个参数允许过滤到达请求。它使用X/path_to_regexp库,就像X/Oak一样。
第三个参数是带有签名的回调函数(request: ServerRequest, params: any) => Promise<unknown>该>将被要求用于匹配过滤器的传入请求。 params包含来自路径滤波器的REGEXP组。
“回调”可以处理请求并调用其req.respond()方法(等待结果),或者它可以决定不处理此请求,而无需致电req.respond()返回,因此其他处理程序(在FCGI.LISTEN()注册)将有机会处理此请求。如果没有处理,将返回404响应。
例子:
fcgi . listen ( 9988 , \'/page-1.html\' , async req => { await req . respond ( { body : \'Hello world\' } ) ; } ) ; fcgi . listen ( 9988 , \'/catalog/:item\' , async ( req , params ) => { await req . respond ( { body : `Item ${ params . item } ` } ) ; } ) ; fcgi . listen ( 9988 , \'\' , // match all paths async req => { await req . respond ( { body : \'Something else\' } ) ; } ) ;
unlisten()
⚙fcgi.unlisten(addr?:fcgiaddr):
void
在指定地址或所有地址上停止服务请求(如果ADDR参数未定义)。删除所有听众将触发“结束”事件。
onerror()
⚙fcgi.onerror(呼叫?:(错误:错误)=>
unknown):Promisevoid
捕获FastCGI服务器错误。可以添加多个事件处理程序。
onend()
⚙fcgi.onend(callback?:()=>
unknown):Promisevoid
或await onEnd() – 捕获FastCGI服务器停止接受连接的时刻(当所有侦听器删除和正在进行的请求完成时)。
OffError()
⚙fcgi.offerror(呼叫?:(错误:错误)=>
unknown):void
删除通过onError(callback)添加的回调。
fcgi.offError() – 删除所有回调。
得罪()
⚙fcgi.offend(呼叫?:()=>
unknown):void
删除通过onEnd(callback) 。
fcgi.offEnd() – 删除所有回调。
选项()
⚙fcgi.options(选项?:服务器和客户端):ServerOptions&ClientOptions
允许修改服务器和/或Client选项。未指定的选项将保留其先前的值。即使在服务器开始运行后,也可以随时调用此功能,并且在可能的情况下将生效新的选项值。此功能在修改后返回所有选项。
console . log ( `maxConns= ${ fcgi . options ( ) . maxConns } ` ) ; fcgi . options ( { maxConns : 123 } ) ; console . log ( `Now maxConns= ${ fcgi . options ( ) . maxConns } ` ) ;
拿来()
⚙fcgi.fetch(request_options:requestOptions,输入:request | url |
string,init?:requestInit):Promise <Response WithWithCookies>
像Apache和Nginx一样,将请求发送到FASTCGI服务,例如PHP。
第一个参数( request_options )指定如何连接到服务以及发送到哪些参数。 2最重要的参数是request_options.addr (服务套接字地址)和request_options.scriptFilename (服务必须执行的脚本文件的路径)。
第二个( input )和第三( init )参数与内置fetch()函数中的参数相同。
返回的响应对象通过添加包含所有Set-Cookie标头的cookies属性来扩展内置Response (定期fetch()返回)。也response.body对象通过添加Deno.Reader实现来扩展常规的ReadableStream<Uint8Array> 。
在指定的request_options.timeout期间通过之前,必须明确阅读响应主体。在此期间之后,连接将被迫关闭。每个未封闭的连接都计入clientoptions.maxconns。 response.body后。doby读取到结尾,连接返回到池,可以重复使用(除了将现有Deno.Conn提供给request_options.addr的情况外 – 在这种情况下,此对象的创建者决定该对象如何处理该对象)。
request_options.keepAliveTimeout milliseconds以及request_options.keepAliveMax times使用的时间。
fetchcapabilities()
⚙fcgi.fetchcapabilities(addr:fcgiaddr | conn):promise <{fcgi_max_conns?:
numbernumberfcgi_max_reqsnumber:fcgi_mpxs_conns?
向FASTCGI服务(例如PHP)询问其协议功能。这不是那么有用的信息。仅适用于那些好奇的人。众所周知,Apache和Nginx在协议使用过程中甚至都不要求此。
canfetch()
⚙fcgi.canfetch():
boolean
当正在进行的请求的数量大于配置的值(MaxConns)时,Fetch()和FetchCapabilities()将等待。 canFetch()检查是否有免费插槽,如果是的,则返回true。建议不要将fetch() untill canFetch()授予绿灯。例子:
if ( ! fcgi . canFetch ( ) ) { await fcgi . waitCanFetch ( ) ; } await fcgi . fetch ( ... ) ;
waitcanfetch()
⚙fcgi.waitcanfetch():Promise
void
Closeidle()
⚙fcgi.closeidle():
void
如果keepAliveTimeout选项为> 0,则FCGI.Fetch()将重复使用连接。每次获取后,连接将等待指定的毫秒数以进行下一个获取。空闲连接不允许Deno应用自然退出。您可以致电fcgi.closeIdle()关闭所有空闲连接。
使用低级API
提到的FCGI对象只是围绕低级功能和类的包装器。可以直接使用它们。
import { Server } from \'https://d*e*n*o.land/x/fcgi@v2.1.1/mod.ts\' ; const listener = Deno . listen ( { hostname : \"::1\" , port : 9988 } ) ; const server = new Server ( listener ) ; console . log ( `Started on ${ listener . addr . transport == \'tcp\' ? listener . addr . hostname + \':\' + listener . addr . port : listener . addr } ` ) ; for await ( let req of server ) { queueMicrotask ( async ( ) => { console . log ( req . url ) ; req . responseHeaders . set ( \'Content-Type\' , \'text/html\' ) ; await req . respond ( { body : \'Your cookies: \' + JSON . stringify ( [ ... req . cookies . entries ( ) ] ) } ) ; } ) ; }
ServerRequest对象
给出的fcgi.listen()的回调接收到传入的请求作为serverRequest对象。服务器上的异步迭代也会产生此类对象。 ServerRequest包含从FastCGI服务器发送的信息。
-
url(string) – 请求的request_uri,例如\’/path/index.html?a=1\’ -
method(字符串) – 喜欢GET。 -
proto(字符串) – 例如HTTP/1.1或HTTP/2。 -
protoMinor(数字) -
protoMajor(数字) -
params(标题) – 从FastCGI Frontend发送的环境参数。这通常包括“ request_uri”,“ script_uri”,“ script_filename”,“ document_root”,可以包含“ context_document_root”(如果使用apache multiviews),等等。 -
headers(标题) – 请求HTTP标头。 -
get(MAP) – 查询字符串参数。 -
post(地图) – 发布参数,可以包含上传的文件。要初始化此属性,请致电await req.post.parse()。 -
cookies(地图) – 请求cookie。添加并删除它们添加了相应的响应HTTP标头。 -
body(Deno.Reader) – 如果未调用req.post.parse()则允许阅读原始的帖子主体。也可以从ServerRequest对象本身中读取身体,因为它实现了Deno.Reader(req.body == req)。 -
responseStatus(number) – 在调用respond()之前将其设置为HTTP状态代码。但是,respond()如果给出)给出的状态覆盖了这一状态。 -
responseHeaders(标题) – 在调用响应之前,将响应HTTP标头设置respond()标头,然后/或将它们传递给respond()(后者具有优先级)。 -
headersSent(布尔值) – 表示已经发送响应标头。如果您将数据写入serverRequest对象(它实现Deno.Writer),则它们将通过respond()或更早发送。
要响应请求,您需要调用req.respond()方法,该方法将所有未决数据发送到FastCGI服务器,并终止请求,释放所有资源,并删除所有上传的文件(您需要将它们移动到其他位置以保持它们)。调用respond() 。
如果使用服务器对象,则在完成此请求后respond()是您的责任。 FCGI.LISTEN()API如果您不在任何注册的请求处理程序中调用它,将自动拨打respond()为404状态。
可以在调用respond()之前设置响应标头和数据,也可以将其发送给response() 。可以给出响应主体以respond() ,也可以将其写入ServerRequest对象。
// test like this: curl --data \'INPUT DATA\' http://*deno-se*rve*r.loc/test.ts import { fcgi } from \'https://d*e*n*o.land/x/fcgi@v2.1.1/mod.ts\' ; console . log ( `Started on [::1]:9988` ) ; fcgi . listen ( \'[::1]:9988\' , \'\' , async req => { console . log ( req . url ) ; // read raw POST input const raw_input = await req . readable . uint8Array ( ) ; // write response req . responseHeaders . set ( \'Content-Type\' , \'text/plain\' ) ; await req . writable . write ( \'The POST input was:\\n\' ) ; await req . writable . write ( raw_input ) ; await req . respond ( ) ; } ) ;
