FastCgiNet
用C#编写的FastCGI库。 FASTCGI是一项协议,允许传统的CGI应用程序或控制台应用程序与Web服务器并肩无效(无需更改其代码),从而响应用户的HTTP请求。它也可用于运行其他类型的Web应用程序(PHP,ROR和OWIN应用程序),并且出于多种原因是一个很好的托管选择(请参阅官方网站)。应该注意的是, FastCgiNet不打算在没有任何代码更改的情况下启用Console .NET应用程序运行: FastCgiNet不会重定向stdin,stdout或stderr。它为应用程序提供了其他机制,可以用作CGI应用程序。
API和安装
您可以克隆并使用Monodevelops或Visual Studio构建,也可以通过Nuget下载FastCgiNet 。提防API可能会在不久的将来发生变化,尽管并不是很糟糕。也就是说,这里有一些有关如何使用此库的文档:
请求API
FastCgiNet中有两个API:记录API和请求API。请求API是推荐的API,应满足大多数用户的需求。本节描述了它。
Web服务器的观点:
- 当浏览器请求页面/URL时,Web服务器必须从应用程序请求答案。
- 然后,应用程序将编写HTTP响应状态,HTTP响应标头和标准输出(也可以写入标准错误输出),Web服务器最终将其发送给实际访问者。
因此,如果您正在编写Web服务器,则可能需要每个访问者的请求使用WeberverSocketRequest Request,例如:
using FastCgiNet ;
using FastCgiNet . Streams ;
using FastCgiNet . Requests ;
.. .
// Let's simulate a GET request to http://gi*thub.**com/mzabani/FastCgiNet
var requestedUrl = new Uri ( "http://gi*thub.**com/mzabani/FastCgiNet" ) ;
string requestMethod = "GET" ;
// Suppose the FastCgi application is listening on 127.0.0.1, port 9000
var sock = new Socket ( AddressFamily . InterNetwork , SocketType . Stream , ProtocolType . Tcp ) ;
sock . Connect ( new IPEndPoint ( IPAddress . Loopback , 9000 ) ) ;
// There must be no two concurrent requests with the same requestid, even if in different sockets. For simplicity, this request will have request id equal to 1
ushort requestId = 1 ;
using ( var request = new WebServerSocketRequest ( sock , requestId ) )
{
// The BeginRequest Record defines how the application should respond. To know more read FastCgi's docs.
request . SendBeginRequest ( Role . Responder , true ) ;
// The Request Headers are sent with Params Records. You don't have to worry about the mechanisms, though: just write to the Params stream.
using ( var nvpWriter = new NvpWriter ( request . Params ) )
{
// The WriteParamsFromUri is a helper method that writes the following Name-Value Pairs:
// HTTP_HOST, HTTPS, SCRIPT_NAME, DOCUMENT_URI, REQUEST_METHOD, SERVER_NAME, QUERY_STRING, REQUEST_URI, SERVER_PROTOCOL, GATEWAY_INTERFACE
nvpWriter . WriteParamsFromUri ( requestedUrl , requestMethod ) ;
// The other http request headers, e.g. User-Agent
nvpWriter . Write ( "HTTP_USER_AGENT" , "Super cool Browser v1.0" ) ;
}
// If there is any request body, send it through the Stdin stream. If there is nothing to send, send an End-Of-Request Record (an empty record)
request . SendEmptyStdin ( ) ;
// At this point, the application is processing the request and cooking up a response for us, so let's welcome the incoming data until the response is over
int bytesRead ;
byte [ ] buf = new byte [ 4096 ] ;
while ( ! request . ResponseComplete )
{
bytesRead = sock . Receive ( buf , SocketFlags . None ) ;
request . FeedBytes ( buf , 0 , bytesRead ) ;
}
// All the application's response will be in the Stdout and/or Stderr streams
// Don't forget that the very first line of the output is ASCII encoded text with the response status, such as "Status: 200 OK"
using ( var reader = new StreamReader ( request . Stdout ) )
{
Console . Write ( reader . ReadToEnd ( ) ) ;
}
}
// The socket and all other resources are automatically disposed at this point.
// This implies that WebServerSocketRequest still doesn't multiplex requests
// (not for long, hopefully - also, you can inherit from this class and make
// Dispose() not call CloseSocket() if you want to multiplex requests)
上面的代码示例很好地证明了使用FastCgiNet容易性!但是,不要忘记处理各种错误。例如,该应用程序可以随时突然关闭插座,如果邪恶的应用程序永远不会发送EndRequest记录,则邪恶的应用程序可能会使您进入无限循环。现在,让我们从应用程序的角度看一下它,并使用applicationsocketRequest类:
Hello FastCgiNet !
The requested path was {0}", requestedPath); } // Our application status and end of request. The FastCgi Standard defines that returning 0 indicates there were no errors request.SendEndRequest(0, ProtocolStatus.RequestComplete); } // The connection socket and all other resources (except for the // listen socket) are automatically disposed at this point. This // implies that ApplicationSocketRequest still doesn't multiplex // requests (not for long, hopefully - also, you can inherit from // this class and make Dispose() not call CloseSocket() if // you want to multiplex requests). } "> using FastCgiNet ;
using FastCgiNet . Streams ;
using FastCgiNet . Requests ;
.. .
using ( var listenSock = new Socket ( AddressFamily . InterNetwork , SocketType . Stream , ProtocolType . Tcp ) )
{
listenSock . Bind ( new IPEndPoint ( IPAddress . Loopback , 9000 ) ) ;
listenSock . Listen ( 1 ) ;
// For simplicity, let's accept only one connection
var sock = listenSock . Accept ( ) ;
using ( var request = new ApplicationSocketRequest ( sock ) )
{
// Now let's wait until we have received the Params and Stdin streams completely
int bytesRead ;
byte [ ] buf = new byte [ 4096 ] ;
while ( ! request . Params . IsComplete || ! request . Stdin . IsComplete )
{
bytesRead = sock . Receive ( buf , SocketFlags . None ) ;
request . FeedBytes ( buf , 0 , bytesRead ) ;
}
// Let's look for the requested path and ignore everything else
string requestedPath = null ;
using ( var nvpReader = new NvpReader ( request . Params ) )
{
NameValuePair nvp ;
while ( ( nvp = nvpReader . Read ( ) ) != null )
{
if ( nvp . Name == "DOCUMENT_URI" )
requestedPath = nvp . Value ;
}
}
// Let's write a classic response
using ( var writer = new StreamWriter ( request . Stdout ) )
{
// The headers first
writer . NewLine = " r n " ;
writer . Write ( "Status: 200 OK" ) ;
writer . WriteLine ( "Content-Type: text/html" ) ;
writer . WriteLine ( ) ;
// Now the body
writer . Write ( "Hello World Hello FastCgiNet !
The requested path was {0}" , requestedPath ) ;
}
// Our application status and end of request. The FastCgi Standard defines that returning 0 indicates there were no errors
request . SendEndRequest ( 0 , ProtocolStatus . RequestComplete ) ;
}
// The connection socket and all other resources (except for the
// listen socket) are automatically disposed at this point. This
// implies that ApplicationSocketRequest still doesn't multiplex
// requests (not for long, hopefully - also, you can inherit from
// this class and make Dispose() not call CloseSocket() if
// you want to multiplex requests).
}
再一次,不要忘记处理各种插座和邪恶的Web服务器错误。
大量要求和内存消耗(应用程序侧)
如果您使用的是请求API,则可以轻松地将大型请求存储在磁盘上而不是在内存中。实际上,如果您只是复制并粘贴应用程序代码示例, FastCgiNet将自动为您存储大于2KB的请求。可以通过RecordFactory类中的构造函数轻松地指定此尺寸限制,该构造器可以提供给您的FastCgiRequest S(请注意,这在v0.1中未实现;它仅在Master上可用,并且可以在V0.11中可用)。
大量要求和内存消耗(WebServer侧)
托多
记录API
这是一个较低级别的API,可让您构建记录并亲自发送记录。强烈建议您不使用此API,原因有几个:
- 记录最多保存着65535个字节的内容。这意味着您必须将数据分解为多个记录以通过它,或者事情可能会错误地错误。
- 此API中没有请求的概念。您必须自己处理。
- 大量要求 - 想象文件上传 - 是记忆的巨大浪费。请求API可以在指定的限制后自动将记录内容的存储存储到磁盘上;直接处理记录时,执行此操作并不容易。
- 请求API进行了几项理智检查,使其非常有帮助。
该API是公开的,因为与请求API结合使用,电源用户可能会充分利用它(尽管我自己很少看到用例)。因此,我不会在记录此API方面付出太多努力,因为我将记录请求API。该代码的记录和直观非常完美,因此,如果需要,只需探索API,就可以了。
版本控制
该库仍在略有变化。作者非常努力地保持较高的向后兼容性,尽管某些行为发生了变化,甚至可以预期更改API,直到V0.2。达到v0.2后,我们将输入严格的版本控制方案,该方案将在此处进行详细记录。
托多
- 多路复用请求
- 类型GetValues,GetValuesResult,Abortrequest,Data和Unknowntype的记录
- 更加关注角色过滤器和授权者
通过命令行克隆项目: