├── .gitignore ├── FastCGI.groupproj ├── LICENSE ├── NGINX ├── NGINX.FCGIApplication.pas ├── NGINX.FCGIConstants.pas ├── NGINX.FCGIRecord.pas ├── NGINX.FCGIRequest.pas ├── NGINX.FCGIResponse.pas ├── NGINX.FCGIStream.pas ├── NGINX.FastCGI.dpr ├── NGINX.FastCGI.dproj ├── NGINX.FastCGI.res ├── NGINX.WebModule.pas └── ServerConst1.pas └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | # 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | #*.res 7 | # 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | #*.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | # Delphi compiler-generated binaries (safe to delete) 30 | *.exe 31 | *.dll 32 | *.bpl 33 | *.bpi 34 | *.dcp 35 | *.so 36 | *.apk 37 | *.drc 38 | *.map 39 | *.dres 40 | *.rsm 41 | *.tds 42 | *.dcu 43 | *.lib 44 | *.a 45 | *.o 46 | *.ocx 47 | 48 | # Delphi autogenerated files (duplicated info) 49 | *.cfg 50 | *.hpp 51 | *Resource.rc 52 | 53 | # Delphi local files (user-specific info) 54 | *.local 55 | *.identcache 56 | *.projdata 57 | *.tvsconfig 58 | *.dsk 59 | 60 | # Delphi history and backups 61 | __history/ 62 | __recovery/ 63 | *.~* 64 | 65 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 66 | *.stat 67 | 68 | # Boss dependency manager vendor folder https://github.com/HashLoad/boss 69 | modules/ 70 | -------------------------------------------------------------------------------- /FastCGI.groupproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {C0C5E5EC-B9E0-40D7-A567-96FBE60BB5F7} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Default.Personality.12 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 kylixfans 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NGINX/NGINX.FCGIApplication.pas: -------------------------------------------------------------------------------- 1 | unit NGINX.FCGIApplication; 2 | 3 | /// 4 | /// Main FastCGI listener class. 5 | /// 6 | /// 7 | /// 8 | /// This class manages a connection to a webserver by listening on a given port on localhost and receiving FastCGI 9 | /// requests by webserver nginx. 10 | /// 11 | /// In FastCGI terms, this class implements the responder role. Refer to section 6.2 of the FastCGI specification 12 | /// for details. 13 | /// 14 | /// Use to get notified of received requests. You can call to 15 | /// enter an infinite loopand let the app handle everything. 16 | /// Alternatively, if you want to control the execution flow by yourself, call to start 17 | /// accepting connections. Then repeatedly call to handle incoming requests. 18 | /// 19 | /// If you want to manage the socket connection details by yourself, or for testing purposes, 20 | /// you can also call instead of any of the above methods. 21 | /// 22 | /// See the below example to learn how to accept requests. 23 | /// For more detailed information, have a look at the class. 24 | /// 25 | /// If you need to fiddle with the FastCGI packets itself, see the class and read the 26 | /// [FastCGI specification](http://www.fastcgi.com/devkit/doc/fcgi-spec.html). 27 | /// 28 | 29 | interface 30 | 31 | uses 32 | System.SysUtils, System.Generics.Collections, System.Classes, 33 | NGINX.FCGIRequest, NGINX.FCGIStream, NGINX.FCGIRecord, 34 | NGINX.FCGIConstants, 35 | IdBaseComponent, IdComponent, IdCustomTCPServer, IdSocksServer, IdTCPServer, 36 | IdContext, IdGlobal, NGINX.FCGIResponse; 37 | 38 | const 39 | _Timeout = 5000; 40 | 41 | type 42 | TFCGIRequestIncoming = procedure(const sender: TObject; 43 | const Request: TFCGIRequest) of object; 44 | TFCGIRequestReceived = procedure(const sender: TObject; 45 | const Request: TFCGIRequest; Response: TFCGIResponse) of object; 46 | 47 | {$M+} 48 | 49 | TFCGIApplication = class 50 | private 51 | FOnRequestReceived: TFCGIRequestReceived; 52 | FOnRequestIncoming: TFCGIRequestIncoming; 53 | FTimeout: Integer; 54 | FListeningSocket: TIdTCPServer; 55 | /// 56 | /// A dictionary of all open TFCGIRequest, indexed by the FastCGI request id. 57 | /// 58 | Requests: TDictionary; 59 | FDefaultPort: Integer; 60 | function GetConnected: Boolean; 61 | procedure SetTimeout(const Value: Integer); 62 | function GetTimeout: Integer; 63 | procedure StopListening; 64 | procedure FCGIExecute(AContext: TIdContext); 65 | procedure SetDefaultPort(const Value: Integer); 66 | procedure SetActive(const Value: Boolean); 67 | function GetVersion: string; 68 | protected 69 | property ListeningSocket: TIdTCPServer read FListeningSocket; 70 | published 71 | property Active: Boolean read GetConnected write SetActive; 72 | property Timeout: Integer read GetTimeout write SetTimeout; 73 | property DefaultPort: Integer read FDefaultPort write SetDefaultPort; 74 | property Version: string read GetVersion; 75 | /// 76 | /// Will be called when a request has been fully received. 77 | /// 78 | property OnRequestReceived: TFCGIRequestReceived read FOnRequestReceived 79 | write FOnRequestReceived; 80 | /// 81 | /// Will be called when a new request is incoming, before it has been fully received. 82 | /// 83 | property OnRequestIncoming: TFCGIRequestIncoming read FOnRequestIncoming 84 | write FOnRequestIncoming; 85 | public 86 | constructor Create; 87 | destructor Destory; 88 | procedure Run(port: Integer); 89 | end; 90 | 91 | implementation 92 | 93 | { TFCGIApplication } 94 | 95 | function TFCGIApplication.GetConnected: Boolean; 96 | begin 97 | Result := FListeningSocket.Active; 98 | end; 99 | 100 | /// 101 | /// The read/write timeouts in miliseconds for the listening socket, the connections, and the streams. 102 | /// 103 | /// Zero or -1 mean infinite timeout. 104 | function TFCGIApplication.GetTimeout: Integer; 105 | begin 106 | if FTimeout = 0 then 107 | begin 108 | FTimeout := _Timeout; 109 | end; 110 | Result := FTimeout; 111 | end; 112 | 113 | function TFCGIApplication.GetVersion: string; 114 | begin 115 | Result := FListeningSocket.Version; 116 | end; 117 | 118 | /// 119 | /// Starts listening for connections on the given port. 120 | /// 121 | /// 122 | /// Will only accept connections from localhost. 123 | /// 124 | procedure TFCGIApplication.Run(port: Integer); 125 | begin 126 | FListeningSocket.DefaultPort := port; 127 | FListeningSocket.Active := True; 128 | end; 129 | 130 | constructor TFCGIApplication.Create; 131 | begin 132 | Requests := TDictionary.Create; 133 | FListeningSocket := TIdTCPServer.Create(nil); 134 | FListeningSocket.OnExecute := FCGIExecute; 135 | end; 136 | 137 | destructor TFCGIApplication.Destory; 138 | begin 139 | StopListening; 140 | Requests.Free; 141 | FListeningSocket.Free; 142 | end; 143 | 144 | /// 145 | /// Tries to read and handle a from inputStream and writes responses to outputStream. 146 | /// 147 | procedure TFCGIApplication.FCGIExecute(AContext: TIdContext); 148 | var 149 | req: TFCGIRequest; 150 | Response: TFCGIResponse; 151 | r: TFCGIRecord; 152 | role: Integer; 153 | flags: Byte; 154 | keepAlive: Boolean; 155 | ValuesResultStream: TFCGIStream; 156 | begin 157 | with AContext.Connection do 158 | begin 159 | IOHandler.ReadTimeout := GetTimeout; 160 | // if not IOHandler.Readable(0) then 161 | // begin 162 | // Exit; 163 | // end; 164 | r := TFCGIRecord.Create(IOHandler); 165 | end; 166 | try 167 | r.ReadRecord; 168 | if r.RecordType = TRecordType.BeginRequest then 169 | begin 170 | if Requests.ContainsKey(r.RequestId) then 171 | begin 172 | Requests.Remove(r.RequestId); 173 | end; 174 | BeginRequestData(r.ContentData, role, flags); 175 | if (flags and FCGI_KEEP_CONN) <> 0 then 176 | keepAlive := True 177 | else 178 | keepAlive := False; 179 | 180 | req := TFCGIRequest.Create(r.RequestId, AContext.Connection.IOHandler, 181 | keepAlive); 182 | Requests.Add(r.RequestId, req); 183 | if Assigned(FOnRequestIncoming) then 184 | FOnRequestIncoming(Self, req); 185 | end 186 | else if (r.RecordType = TRecordType.AbortRequest) or 187 | (r.RecordType = TRecordType.EndRequest) then 188 | begin 189 | Requests.Remove(r.RequestId); 190 | end 191 | else if r.RecordType = TRecordType.GetValues then 192 | begin 193 | ValuesResultStream := TFCGIStream.Create(0); 194 | try 195 | ValuesResultStream.ValuesResult(1, 1, False); 196 | ValuesResultStream.stream.Position := 0; 197 | AContext.Connection.IOHandler.Write(ValuesResultStream.stream, 198 | ValuesResultStream.StreamLength); 199 | AContext.Connection.IOHandler.Close; 200 | finally 201 | ValuesResultStream.Free; 202 | end; 203 | end 204 | else 205 | begin 206 | if Requests.ContainsKey(r.RequestId) then 207 | begin 208 | req := Requests[r.RequestId]; 209 | if req.HandleRecord(r) then 210 | begin 211 | if Assigned(FOnRequestReceived) then 212 | begin 213 | Response := TFCGIResponse.Create(r.RequestId, 214 | AContext.Connection.IOHandler); 215 | try 216 | FOnRequestReceived(Self, req, Response); 217 | if not Response.IsClose then 218 | begin 219 | Response.Flush; 220 | end; 221 | finally 222 | Response.Free; 223 | end; 224 | end; 225 | with AContext.Connection do 226 | begin 227 | IOHandler.Close; 228 | if not req.keepAlive then 229 | begin 230 | AContext.Connection.Disconnect; 231 | end; 232 | end; 233 | Requests.Remove(r.RequestId); 234 | end; 235 | end; 236 | end; 237 | finally 238 | r.Free; 239 | end; 240 | end; 241 | 242 | /// 243 | /// True iff this application is currently connected to a webserver. 244 | /// 245 | procedure TFCGIApplication.SetActive(const Value: Boolean); 246 | begin 247 | FListeningSocket.Active := Value; 248 | end; 249 | 250 | procedure TFCGIApplication.SetDefaultPort(const Value: Integer); 251 | begin 252 | FDefaultPort := Value; 253 | FListeningSocket.DefaultPort := FDefaultPort; 254 | end; 255 | 256 | procedure TFCGIApplication.SetTimeout(const Value: Integer); 257 | begin 258 | FTimeout := Value; 259 | end; 260 | 261 | /// 262 | /// Stops listening for incoming connections. 263 | /// 264 | procedure TFCGIApplication.StopListening; 265 | begin 266 | if FListeningSocket <> nil then 267 | begin 268 | FListeningSocket.Active := False; 269 | end; 270 | end; 271 | 272 | end. 273 | -------------------------------------------------------------------------------- /NGINX/NGINX.FCGIConstants.pas: -------------------------------------------------------------------------------- 1 | unit NGINX.FCGIConstants; 2 | 3 | /// 4 | /// Constants defined in the FastCGI spec. 5 | /// 6 | 7 | interface 8 | 9 | const 10 | /// 11 | /// Listening socket file number 12 | /// 13 | FCGI_LISTENSOCK_FILENO = 0; 14 | 15 | /// 16 | /// Number of bytes in a FCGI_Header. 17 | /// Future versions of the protocol will not reduce this number. 18 | /// 19 | FCGI_HEADER_LEN = 8; 20 | 21 | /// 22 | /// Value for version component of FCGI_Header 23 | /// 24 | FCGI_VERSION_1 = 1; 25 | 26 | /// 27 | /// Values for type component of FCGI_Header 28 | /// 29 | FCGI_BEGIN_REQUEST = 1; 30 | /// 31 | /// Values for type component of FCGI_Header 32 | /// 33 | FCGI_ABORT_REQUEST = 2; 34 | /// 35 | /// Values for type component of FCGI_Header 36 | /// 37 | FCGI_END_REQUEST = 3; 38 | /// 39 | /// Values for type component of FCGI_Header 40 | /// 41 | FCGI_PARAMS = 4; 42 | /// 43 | /// Values for type component of FCGI_Header 44 | /// 45 | FCGI_STDIN = 5; 46 | /// 47 | /// Values for type component of FCGI_Header 48 | /// 49 | FCGI_STDOUT = 6; 50 | /// 51 | /// Values for type component of FCGI_Header 52 | /// 53 | FCGI_STDERR = 7; 54 | /// 55 | /// Values for type component of FCGI_Header 56 | /// 57 | FCGI_DATA = 8; 58 | /// 59 | /// Values for type component of FCGI_Header 60 | /// 61 | FCGI_GET_VALUES = 9; 62 | /// 63 | /// Values for type component of FCGI_Header 64 | /// 65 | FCGI_GET_VALUES_RESULT = 10; 66 | /// 67 | /// Values for type component of FCGI_Header 68 | /// 69 | FCGI_UNKNOWN_TYPE = 11; 70 | /// 71 | /// Values for type component of FCGI_Header 72 | /// 73 | FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE; 74 | 75 | /// 76 | /// Value for requestId component of FCGI_Header 77 | /// 78 | FCGI_NULL_REQUEST_ID = 0; 79 | 80 | /// 81 | /// Mask for flags component of FCGI_BeginRequestBody 82 | /// 83 | FCGI_KEEP_CONN = 1; 84 | 85 | /// 86 | /// Values for role component of FCGI_BeginRequestBody 87 | /// 88 | FCGI_RESPONDER = 1; 89 | /// 90 | /// Values for role component of FCGI_BeginRequestBody 91 | /// 92 | FCGI_AUTHORIZER = 2; 93 | /// 94 | /// Values for role component of FCGI_BeginRequestBody 95 | /// 96 | FCGI_FILTER = 3; 97 | 98 | /// 99 | /// Values for protocolStatus component of FCGI_EndRequestBody 100 | /// 101 | FCGI_REQUEST_COMPLETE = 0; 102 | /// 103 | /// Values for protocolStatus component of FCGI_EndRequestBody 104 | /// 105 | FCGI_CANT_MPX_CONN = 1; 106 | /// 107 | /// Values for protocolStatus component of FCGI_EndRequestBody 108 | /// 109 | FCGI_OVERLOADED = 2; 110 | /// 111 | /// Values for protocolStatus component of FCGI_EndRequestBody 112 | /// 113 | FCGI_UNKNOWN_ROLE = 3; 114 | 115 | /// 116 | /// Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records 117 | /// 118 | FCGI_MAX_CONNS = 'FCGI_MAX_CONNS'; 119 | /// 120 | /// Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records 121 | /// 122 | FCGI_MAX_REQS = 'FCGI_MAX_REQS'; 123 | /// 124 | /// Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records 125 | /// 126 | FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS'; 127 | 128 | implementation 129 | 130 | end. 131 | -------------------------------------------------------------------------------- /NGINX/NGINX.FCGIRecord.pas: -------------------------------------------------------------------------------- 1 | unit NGINX.FCGIRecord; 2 | 3 | /// 4 | /// A FastCGI Record. 5 | /// 6 | /// 7 | /// See section 3.3 of the FastCGI Specification for details. 8 | /// 9 | /// 10 | interface 11 | 12 | uses 13 | System.SysUtils, System.Classes, NGINX.FCGIConstants, IdIOHandler, IdGlobal, 14 | System.Generics.Collections, System.StrUtils; 15 | 16 | type 17 | /// 18 | /// Record types, used in the 'type' field of Record. 19 | /// 20 | /// 21 | /// Described in the FastCGI Specification section 8. 22 | /// 23 | TRecordType = (BeginRequest = FCGI_BEGIN_REQUEST, AbortRequest, EndRequest, 24 | Params, Stdin, Stdout, Stderr, Data, GetValues, GetValuesResult, 25 | UnknownType = FCGI_UNKNOWN_TYPE, MaxType = FCGI_MAXTYPE); 26 | /// 27 | /// Protocol status used for requests. 28 | /// Described in the FastCGI Specification section 8. 29 | /// 30 | TProtocolStatus = (RequestComplete = FCGI_REQUEST_COMPLETE, 31 | CantMpxConn = FCGI_CANT_MPX_CONN, Overloaded = FCGI_OVERLOADED, 32 | UnknownRole = FCGI_UNKNOWN_ROLE); 33 | 34 | {$M+} 35 | 36 | TFCGIRecord = class 37 | private 38 | FIO: TIdIOHandler; 39 | FVersion: Byte; 40 | FRecordType: TRecordType; 41 | FRequestId: Integer; 42 | FContentLength: Integer; 43 | FContentData: TMemoryStream; 44 | procedure ReadBytes(len: Integer); 45 | function ReadInt16: UInt16; 46 | function ReadByte: Byte; 47 | procedure ReadContent(len: Integer); 48 | published 49 | /// 50 | /// The version byte. Should always equal . 51 | /// 52 | property Version: Byte read FVersion; 53 | /// 54 | /// The of this record. 55 | /// 56 | property RecordType: TRecordType read FRecordType; 57 | /// 58 | /// The request id associated with this record. 59 | /// 60 | property RequestId: Integer read FRequestId; 61 | /// 62 | /// The length of . 63 | /// 64 | property ContentLength: Integer read FContentLength; 65 | /// 66 | /// The data contained in this record. 67 | /// 68 | property ContentData: TMemoryStream read FContentData; 69 | public 70 | constructor Create(io: TIdIOHandler); 71 | procedure ReadRecord; 72 | end; 73 | 74 | implementation 75 | 76 | { TFCGIRecord } 77 | 78 | constructor TFCGIRecord.Create(io: TIdIOHandler); 79 | begin 80 | FIO := io; 81 | end; 82 | 83 | /// 84 | /// Reads a single byte from the given stream. 85 | /// 86 | function TFCGIRecord.ReadByte: Byte; 87 | begin 88 | Result := FIO.ReadByte; 89 | end; 90 | 91 | procedure TFCGIRecord.ReadBytes(len: Integer); 92 | var 93 | buf: TIdBytes; 94 | begin 95 | SetLength(buf, len); 96 | FIO.ReadBytes(buf, len); 97 | end; 98 | 99 | procedure TFCGIRecord.ReadContent(len: Integer); 100 | begin 101 | FContentData := TMemoryStream.Create; 102 | FIO.ReadStream(FContentData, len); 103 | end; 104 | 105 | /// 106 | /// Reads a 16-bit integer from the given stream. 107 | /// 108 | function TFCGIRecord.ReadInt16: UInt16; 109 | var 110 | h, l: Byte; 111 | begin 112 | h := ReadByte; 113 | l := ReadByte; 114 | Result := h * 256 + l; 115 | end; 116 | 117 | /// 118 | /// Reads a single Record from the given stream. 119 | /// 120 | /// 121 | /// Returns the retreived record or null if no record could be read. 122 | /// Will block if a partial record is on the stream, until the full record has arrived or a timeout occurs. 123 | /// 124 | procedure TFCGIRecord.ReadRecord; 125 | var 126 | paddingLength: Byte; 127 | begin 128 | FVersion := ReadByte; 129 | if FVersion <> FCGI_VERSION_1 then 130 | begin 131 | raise Exception.Create 132 | ('Invalid version number in FastCGI record header. Possibly corrupted data.'); 133 | end; 134 | case ReadByte of 135 | 1: 136 | FRecordType := BeginRequest; 137 | 2: 138 | FRecordType := AbortRequest; 139 | 3: 140 | FRecordType := EndRequest; 141 | 4: 142 | FRecordType := Params; 143 | 5: 144 | FRecordType := Stdin; 145 | 6: 146 | FRecordType := Stdout; 147 | 7: 148 | FRecordType := Stderr; 149 | 8: 150 | FRecordType := Data; 151 | 9: 152 | FRecordType := GetValues; 153 | 10: 154 | FRecordType := GetValuesResult; 155 | 11: 156 | FRecordType := UnknownType; 157 | 12: 158 | FRecordType := MaxType; 159 | end; 160 | FRequestId := ReadInt16; 161 | FContentLength := ReadInt16; 162 | 163 | paddingLength := ReadByte; 164 | ReadByte; 165 | 166 | if FContentLength > 0 then 167 | begin 168 | ReadContent(FContentLength); 169 | end; 170 | if paddingLength > 0 then 171 | begin 172 | ReadBytes(paddingLength); 173 | end; 174 | end; 175 | 176 | end. 177 | -------------------------------------------------------------------------------- /NGINX/NGINX.FCGIRequest.pas: -------------------------------------------------------------------------------- 1 | unit NGINX.FCGIRequest; 2 | 3 | /// 4 | /// A FastCGI request. 5 | /// 6 | /// 7 | /// A request usually corresponds to a HTTP request that has been received by the webserver (see the [FastCGI specification](http://www.fastcgi.com/devkit/doc/fcgi-spec.html) for details). 8 | /// 9 | interface 10 | 11 | uses 12 | System.SysUtils, System.Classes, NGINX.FCGIRecord, IdIOHandler, 13 | System.Generics.Collections, NGINX.FCGIStream; 14 | 15 | type 16 | {$M+} 17 | TFCGIRequest = class 18 | private 19 | FRequestId: Integer; 20 | FIO: TIdIOHandler; 21 | FKeepAlive: Boolean; 22 | FIsOpen: Boolean; 23 | /// 24 | /// Incoming parameter records are stored here, until the parameter stream is closed by the webserver by sending an empty param record. 25 | /// 26 | FParamStream: TMemoryStream; 27 | FParameters: TDictionary; 28 | FRequestBodyStream: TMemoryStream; 29 | procedure Param_ReadNameValuePairs; 30 | protected 31 | published 32 | /// 33 | /// The id for this request, issued by the webserver 34 | /// 35 | property RequestId: Integer read FRequestId; 36 | /// 37 | /// True iff the webserver set the KeepAlive flag for this request 38 | /// 39 | /// 40 | /// This indicates that the socket used for this request should be left open. 41 | /// This is used internally by . 42 | /// 43 | property KeepAlive: Boolean read FKeepAlive; 44 | /// 45 | /// The FastCGI parameters received by the webserver, in raw byte arrays. 46 | /// 47 | property Parameters: TDictionary read FParameters; 48 | /// 49 | /// A stream providing the request body. 50 | /// 51 | /// 52 | /// For POST requests, this will contain the POST variables. For GET requests, this will be empty. 53 | /// 54 | property RequestBody: TMemoryStream read FRequestBodyStream; 55 | public 56 | constructor Create(RequestId: Integer; responseHandler: TIdIOHandler; 57 | KeepAlive: Boolean); 58 | function HandleRecord(r: TFCGIRecord): Boolean; 59 | end; 60 | 61 | implementation 62 | 63 | { TFCGIRequest } 64 | 65 | /// 66 | /// Creates a new request. Usually, you don't need to call this. 67 | /// 68 | /// Records are created by when a new request has been received. 69 | constructor TFCGIRequest.Create(RequestId: Integer; 70 | responseHandler: TIdIOHandler; KeepAlive: Boolean); 71 | begin 72 | FRequestId := RequestId; 73 | FIO := responseHandler; 74 | FKeepAlive := KeepAlive; 75 | FIsOpen := true; 76 | FParameters := TDictionary.Create; 77 | FRequestBodyStream := TMemoryStream.Create; 78 | end; 79 | 80 | /// 81 | /// Used internally. Feeds a TFCGIRecord to this request for processing. 82 | /// 83 | /// The record to feed. 84 | /// Returns true iff the request is completely received. 85 | function TFCGIRequest.HandleRecord(r: TFCGIRecord): Boolean; 86 | var 87 | oldPos: Integer; 88 | begin 89 | Result := false; 90 | case r.RecordType of 91 | TRecordType.Params: 92 | begin 93 | if not Assigned(FParamStream) then 94 | FParamStream := TMemoryStream.Create; 95 | if r.ContentLength = 0 then 96 | begin 97 | // An empty parameter record specifies that all parameters have been transmitted 98 | FParamStream.Position := 0; 99 | Param_ReadNameValuePairs; 100 | end 101 | else 102 | begin 103 | // If the params are not yet finished, write the contents to the ParamStream. 104 | r.ContentData.Position := 0; 105 | FParamStream.CopyFrom(r.ContentData, r.ContentLength); 106 | end; 107 | end; 108 | TRecordType.Stdin: 109 | begin 110 | if r.ContentLength = 0 then 111 | begin 112 | // Finished requests are indicated by an empty stdin record 113 | Result := true; 114 | end 115 | else 116 | begin 117 | oldPos := FRequestBodyStream.Position; 118 | FRequestBodyStream.Seek(0, TSeekOrigin.soEnd); 119 | r.ContentData.Position := 0; 120 | FRequestBodyStream.CopyFrom(r.ContentData, r.ContentLength); 121 | FRequestBodyStream.Position := oldPos; 122 | end; 123 | end; 124 | end; 125 | end; 126 | 127 | /// 128 | /// Tries to read a dictionary of name-value pairs from the given stream 129 | /// 130 | /// 131 | /// This method does not make any attempt to make sure whether this record actually contains a set of name-value pairs. 132 | /// It will return nonsense or throw an EndOfStreamException if the record content does not contain valid name-value pairs. 133 | /// 134 | procedure TFCGIRequest.Param_ReadNameValuePairs; 135 | /// 136 | /// Reads a length from the given stream, which is encoded in one or four bytes. 137 | /// 138 | /// 139 | /// See section 3.4 of the FastCGI specification for details. 140 | /// 141 | function ReadVarLength: UInt16; 142 | var 143 | firstByte: Byte; 144 | b0, b1, b2: Byte; 145 | begin 146 | FParamStream.Read(firstByte, 1); 147 | if firstByte <= 127 then 148 | begin 149 | Result := firstByte; 150 | end 151 | else 152 | begin 153 | FParamStream.Read(b2, 1); 154 | FParamStream.Read(b1, 1); 155 | FParamStream.Read(b0, 1); 156 | Result := UInt16((16777216 * ($7F and firstByte) + 65536 * b2 + 256 * 157 | b1 + b0)); 158 | end; 159 | end; 160 | 161 | var 162 | nameLength: UInt16; 163 | valueLength: UInt16; 164 | name: TBytes; 165 | Value: TBytes; 166 | n: string; 167 | begin 168 | if not Assigned(FParameters) then 169 | FParameters := TDictionary.Create; 170 | while FParamStream.Position < FParamStream.Size do 171 | begin 172 | nameLength := ReadVarLength; 173 | valueLength := ReadVarLength; 174 | // x32 does not allow objects larger than 2GB 175 | // We do not make the effort to workaround this, 176 | // but simply throw an error if we encounter sizes beyond this limit. 177 | if (nameLength >= Integer.MaxValue) or (valueLength >= Integer.MaxValue) 178 | then 179 | begin 180 | raise Exception.Create('Cannot process values larger than 2GB.'); 181 | end; 182 | SetLength(name, nameLength); 183 | FParamStream.Read(name, nameLength); 184 | n := TEncoding.ASCII.GetString(name); 185 | SetLength(Value, valueLength); 186 | FParamStream.Read(Value, valueLength); 187 | 188 | if FParameters.ContainsKey(n) then 189 | FParameters[n] := Value 190 | else 191 | FParameters.Add(n, Value); 192 | end; 193 | end; 194 | 195 | end. 196 | -------------------------------------------------------------------------------- /NGINX/NGINX.FCGIResponse.pas: -------------------------------------------------------------------------------- 1 | unit NGINX.FCGIResponse; 2 | 3 | /// 4 | /// A FastCGI response. 5 | /// 6 | /// 7 | /// You will probably want to use or its helper methods to output a response and then call . Use to be notified of new requests. 8 | /// 9 | /// Remember to call when you wrote the complete response. 10 | /// 11 | /// 12 | interface 13 | 14 | uses 15 | System.SysUtils, System.Classes, NGINX.FCGIRecord, IdIOHandler, 16 | System.Generics.Collections, NGINX.FCGIStream; 17 | 18 | type 19 | {$M+} 20 | TFCGIResponse = class 21 | private 22 | FRequestId: Integer; 23 | FIO: TIdIOHandler; 24 | FIsClose: Boolean; 25 | FHeader: TDictionary; 26 | FHttpStatusCode: Integer; 27 | FHttpVersion: string; 28 | FContent: string; 29 | FCharset: string; 30 | FContentType: string; 31 | procedure SendContent; 32 | procedure SetContent(const Value: string); 33 | procedure SetHeader(const Value: TDictionary); 34 | procedure SetHttpStatusCode(const Value: Integer); 35 | procedure SetHttpVersion(const Value: string); 36 | procedure SetCharset(const Value: string); 37 | procedure SetContentType(const Value: string); 38 | protected 39 | published 40 | property HttpVersion: string read FHttpVersion write SetHttpVersion; 41 | property HttpStatusCode: Integer read FHttpStatusCode 42 | write SetHttpStatusCode; 43 | property Header: TDictionary read FHeader write SetHeader; 44 | property Content: string read FContent write SetContent; 45 | property IsClose: Boolean read FIsClose; 46 | property ContentType: string read FContentType write SetContentType; 47 | property Charset: string read FCharset write SetCharset; 48 | public 49 | constructor Create(requestId: Integer; responseHandler: TIdIOHandler); 50 | procedure Send(const data: TBytes); overload; 51 | procedure Send(const data: TStream); overload; 52 | procedure Send(const data: string); overload; 53 | procedure SendRaw(const rawdata: TBytes); 54 | procedure Flush; 55 | end; 56 | 57 | implementation 58 | 59 | { TFCGIResponse } 60 | 61 | constructor TFCGIResponse.Create(requestId: Integer; 62 | responseHandler: TIdIOHandler); 63 | begin 64 | FRequestId := requestId; 65 | FIO := responseHandler; 66 | FHttpVersion := 'HTTP/1.1'; 67 | FHttpStatusCode := 200; 68 | FHeader := TDictionary.Create; 69 | ContentType := 'text/html'; 70 | Charset := 'utf-8'; 71 | 72 | FHeader.Add('X-Powered-By', 'MVCXE.NGINX.FCGI'); 73 | FIsClose := false; 74 | end; 75 | 76 | procedure TFCGIResponse.Flush; 77 | begin 78 | if not FIsClose then 79 | SendContent; 80 | FIsClose := true; 81 | FIO.Close; 82 | end; 83 | 84 | procedure TFCGIResponse.Send(const data: TStream); 85 | var 86 | s: string; 87 | item: TPair; 88 | bs: TBytesStream; 89 | begin 90 | s := FHttpVersion + ' ' + IntToStr(FHttpStatusCode) + ' OK'#10; 91 | for item in FHeader do 92 | begin 93 | s := s + item.Key + ':' + item.Value + #10; 94 | end; 95 | s := s + #10; 96 | bs := TBytesStream.Create(TEncoding.Default.GetBytes(s)); 97 | bs.Write(data, data.Size); 98 | SendRaw(bs.Bytes); 99 | end; 100 | 101 | /// 102 | /// Appends data to the response body. 103 | /// 104 | /// 105 | /// The given data will be sent immediately to the webserver as a single stdout record. 106 | /// 107 | /// The data to append. 108 | procedure TFCGIResponse.Send(const data: TBytes); 109 | var 110 | s: string; 111 | item: TPair; 112 | bs: TBytesStream; 113 | begin 114 | s := FHttpVersion + ' ' + IntToStr(FHttpStatusCode) + ' OK'#10; 115 | for item in FHeader do 116 | begin 117 | s := s + item.Key + ':' + item.Value + #10; 118 | end; 119 | s := s + #10; 120 | bs := TBytesStream.Create(TEncoding.Default.GetBytes(s)); 121 | bs.Write(data, Length(data)); 122 | SendRaw(bs.Bytes); 123 | end; 124 | 125 | /// 126 | /// Appends an ASCII string to the response body. 127 | /// 128 | /// 129 | /// This is a helper function, it converts the given string to ASCII bytes and feeds it to . 130 | /// 131 | /// The string to append, encoded in Charset. 132 | procedure TFCGIResponse.Send(const data: string); 133 | var 134 | s: string; 135 | item: TPair; 136 | begin 137 | s := FHttpVersion + ' ' + IntToStr(FHttpStatusCode) + ' OK'#10; 138 | for item in FHeader do 139 | begin 140 | s := s + item.Key + ':' + item.Value + #10; 141 | end; 142 | s := s + #10 + data; 143 | // todo: Encoding 144 | if SameText(FCharset, 'utf8') or SameText(FCharset, 'utf-8') then 145 | begin 146 | SendRaw(TEncoding.UTF8.GetBytes(s)); 147 | end 148 | else 149 | begin 150 | SendRaw(TEncoding.Default.GetBytes(s)); 151 | end; 152 | end; 153 | 154 | procedure TFCGIResponse.SendContent; 155 | begin 156 | Send(Content); 157 | end; 158 | 159 | /// 160 | /// Used internally. Writes the record to the given stream. Used for sending records to the webserver. 161 | /// 162 | procedure TFCGIResponse.SendRaw(const rawdata: TBytes); 163 | var 164 | remainingLength: Integer; 165 | response: TFCGIStream; 166 | buf64kb: TBytes; 167 | offset: Integer; 168 | begin 169 | response := TFCGIStream.Create(FRequestId); 170 | try 171 | remainingLength := Length(rawdata); 172 | if remainingLength <= 65535 then 173 | begin 174 | response.response(rawdata); 175 | response.stream.Position := 0; 176 | FIO.Write(response.stream, response.StreamLength); 177 | end 178 | else 179 | begin 180 | SetLength(buf64kb, 65535); 181 | offset := 0; 182 | while remainingLength > 65535 do 183 | begin 184 | buf64kb := Copy(rawdata, offset, 65535); 185 | response.response(buf64kb); 186 | response.stream.Position := 0; 187 | FIO.Write(response.stream, response.StreamLength); 188 | offset := offset + 65535; 189 | remainingLength := remainingLength - 65535; 190 | end; 191 | SetLength(buf64kb, remainingLength); 192 | buf64kb := Copy(rawdata, offset, remainingLength); 193 | response.response(buf64kb); 194 | response.stream.Position := 0; 195 | FIO.Write(response.stream, response.StreamLength); 196 | SetLength(buf64kb, 0); 197 | end; 198 | // 0 199 | SetLength(buf64kb, 0); 200 | response.response(buf64kb); 201 | response.stream.Position := 0; 202 | FIO.Write(response.stream, response.StreamLength); 203 | response.EndRequest; 204 | response.stream.Position := 0; 205 | FIO.Write(response.stream, response.StreamLength); 206 | FIsClose := true; 207 | finally 208 | response.Free; 209 | end; 210 | end; 211 | 212 | procedure TFCGIResponse.SetCharset(const Value: string); 213 | var 214 | s: string; 215 | begin 216 | FCharset := Value; 217 | if FCharset <> '' then 218 | begin 219 | s := '; charset=' + FCharset; 220 | if FHeader.ContainsKey('Content-Type') then 221 | FHeader['Content-Type'] := FContentType + s 222 | else 223 | FHeader.Add('Content-Type', FContentType + s); 224 | end; 225 | end; 226 | 227 | procedure TFCGIResponse.SetContentType(const Value: string); 228 | var 229 | s: string; 230 | begin 231 | FContentType := Value; 232 | if FCharset <> '' then 233 | begin 234 | s := '; charset=' + FCharset; 235 | end; 236 | if FHeader.ContainsKey('Content-Type') then 237 | FHeader['Content-Type'] := FContentType + s 238 | else 239 | FHeader.Add('Content-Type', FContentType + s); 240 | end; 241 | 242 | procedure TFCGIResponse.SetContent(const Value: string); 243 | begin 244 | FContent := Value; 245 | end; 246 | 247 | procedure TFCGIResponse.SetHeader(const Value: TDictionary); 248 | begin 249 | FHeader := Value; 250 | end; 251 | 252 | procedure TFCGIResponse.SetHttpStatusCode(const Value: Integer); 253 | begin 254 | FHttpStatusCode := Value; 255 | end; 256 | 257 | procedure TFCGIResponse.SetHttpVersion(const Value: string); 258 | begin 259 | FHttpVersion := Value; 260 | end; 261 | 262 | end. 263 | -------------------------------------------------------------------------------- /NGINX/NGINX.FCGIStream.pas: -------------------------------------------------------------------------------- 1 | unit NGINX.FCGIStream; 2 | 3 | /// 4 | /// The stream where responses to this request should be written to. 5 | /// Only write FastCGI records here, not the raw response body. Use for sending response data. 6 | /// 7 | 8 | interface 9 | 10 | uses 11 | System.SysUtils, System.Classes, NGINX.FCGIConstants, NGINX.FCGIRecord, 12 | System.Generics.Collections, System.StrUtils; 13 | 14 | type 15 | {$M+} 16 | TFCGIStream = class 17 | private 18 | FRequestId: Integer; 19 | FVersion: Integer; 20 | Fstream: TBytesStream; 21 | FContentLength: Integer; 22 | FStreamLength: Integer; 23 | FRecordType: TRecordType; 24 | procedure WriteInt16(v: UInt16); 25 | procedure CreateResponse(data: TBytes); 26 | published 27 | property StreamLength: Integer read FStreamLength; 28 | property stream: TBytesStream read Fstream write Fstream; 29 | public 30 | constructor Create(requestId: Integer); 31 | procedure Response(data: TBytes); 32 | procedure EndRequest; 33 | procedure ValuesResult(maxConnections: Integer; maxRequests: Integer; 34 | multiplexing: Boolean); 35 | end; 36 | 37 | procedure BeginRequestData(stream: TStream; var role: Integer; var flags: Byte); 38 | 39 | implementation 40 | 41 | procedure BeginRequestData(stream: TStream; var role: Integer; var flags: Byte); 42 | var 43 | h, l: Byte; 44 | begin 45 | stream.Position := 0; 46 | // ReadInt16 47 | // stream.Read(h, 1); 48 | // stream.Read(l, 1); 49 | // role := h * 256 + l; 50 | stream.Read(role, 1); 51 | // ReadByte 52 | stream.Read(flags, 1); 53 | end; 54 | 55 | { TFCGIStream } 56 | 57 | constructor TFCGIStream.Create(requestId: Integer); 58 | begin 59 | FRequestId := requestId; 60 | FVersion := FCGI_VERSION_1; 61 | Fstream := TBytesStream.Create; 62 | end; 63 | 64 | /// 65 | /// Writes this record to the given stream. 66 | /// 67 | /// Returns the number of bytes written. 68 | procedure TFCGIStream.CreateResponse(data: TBytes); 69 | var 70 | b: Byte; 71 | begin 72 | if FContentLength > 65535 then 73 | begin 74 | raise Exception.Create('Cannot send a record with more that 65535 bytes.'); 75 | end; 76 | Fstream.Clear; 77 | Fstream.Write(FVersion, 1); 78 | 79 | Fstream.Write(FRecordType, 1); 80 | WriteInt16(FRequestId); 81 | WriteInt16(FContentLength); 82 | // No padding 83 | b := 0; 84 | Fstream.Write(b, 1); 85 | // Reserved byte 86 | Fstream.Write(b, 1); 87 | if FContentLength > 0 then 88 | begin 89 | Fstream.Write(data, FContentLength); 90 | end; 91 | FStreamLength := FCGI_HEADER_LEN + FContentLength; 92 | end; 93 | 94 | /// 95 | /// Creates a EndRequest record with the given request id 96 | /// 97 | procedure TFCGIStream.EndRequest; 98 | var 99 | content: TBytes; 100 | begin 101 | FContentLength := 8; 102 | SetLength(content, FContentLength); 103 | content[0] := 0; 104 | content[1] := 0; 105 | content[2] := 0; 106 | content[3] := 0; 107 | 108 | // protocolStatus 109 | content[4] := Byte(TProtocolStatus.RequestComplete); 110 | 111 | // reserved bytes 112 | content[5] := 0; 113 | content[6] := 0; 114 | content[7] := 0; 115 | FRecordType := TRecordType.EndRequest; 116 | CreateResponse(content); 117 | end; 118 | 119 | /// 120 | /// Creates a GetValuesResult record from the given config values. 121 | /// 122 | procedure TFCGIStream.ValuesResult(maxConnections, maxRequests: Integer; 123 | multiplexing: Boolean); 124 | var 125 | nameValuePairs: TDictionary; 126 | 127 | vstream: TBytesStream; 128 | nameValuePair: TPair; 129 | name: string; 130 | nameBuf, value: TBytes; 131 | /// 132 | /// Writes a length from the given stream, which is encoded in one or four bytes. 133 | /// 134 | /// 135 | /// See section 3.4 of the FastCGI specification for details. 136 | /// 137 | procedure WriteVarLength(len: UInt32); 138 | var 139 | b: Byte; 140 | begin 141 | if len <= 127 then 142 | stream.Write(Byte(len), 1) 143 | else 144 | begin 145 | b := Byte($80 or len div 16777216); 146 | stream.Write(b, 1); 147 | b := Byte(len div 65536); 148 | stream.Write(b, 1); 149 | b := Byte(len div 256); 150 | stream.Write(b, 1); 151 | stream.Write(Byte(len), 1); 152 | end; 153 | end; 154 | 155 | begin 156 | nameValuePairs := TDictionary.Create(); 157 | try 158 | nameValuePairs.Add(FCGI_MAX_CONNS, 159 | TEncoding.ASCII.GetBytes(IntToStr(maxConnections))); 160 | nameValuePairs.Add(FCGI_MAX_REQS, 161 | TEncoding.ASCII.GetBytes(IntToStr(maxRequests))); 162 | nameValuePairs.Add(FCGI_MPXS_CONNS, 163 | TEncoding.ASCII.GetBytes(IfThen(multiplexing, '1', '0'))); 164 | FRecordType := TRecordType.GetValuesResult; 165 | 166 | vstream := TBytesStream.Create; 167 | try 168 | for nameValuePair in nameValuePairs do 169 | begin 170 | name := nameValuePair.Key; 171 | nameBuf := TEncoding.ASCII.GetBytes(name); 172 | value := nameValuePair.value; 173 | WriteVarLength(Length(nameBuf)); 174 | WriteVarLength(Length(value)); 175 | stream.Write(nameBuf, 0, Length(nameBuf)); 176 | stream.Write(value, 0, Length(value)); 177 | end; 178 | FContentLength := stream.Size; 179 | CreateResponse(stream.Bytes); 180 | finally 181 | vstream.Free; 182 | end; 183 | finally 184 | nameValuePairs.Free; 185 | end; 186 | end; 187 | 188 | /// 189 | /// Creates a Stdout record from the given data and request id 190 | /// 191 | procedure TFCGIStream.Response(data: TBytes); 192 | begin 193 | FContentLength := Length(data); 194 | FRecordType := TRecordType.Stdout; 195 | CreateResponse(data); 196 | end; 197 | 198 | /// 199 | /// Writes a 16-bit integer to the given stream. 200 | /// 201 | procedure TFCGIStream.WriteInt16(v: UInt16); 202 | var 203 | b1, b2: Byte; 204 | begin 205 | b1 := Byte(v div 256); 206 | b2 := Byte(v); 207 | Fstream.Write(b1, 1); 208 | Fstream.Write(b2, 1); 209 | end; 210 | 211 | end. 212 | -------------------------------------------------------------------------------- /NGINX/NGINX.FastCGI.dpr: -------------------------------------------------------------------------------- 1 | program NGINX.FastCGI; 2 | 3 | {$APPTYPE CONSOLE} 4 | {$R *.res} 5 | 6 | uses 7 | System.SysUtils, 8 | System.Types, 9 | IPPeerServer, 10 | IPPeerAPI, 11 | NGINX.FCGIApplication in 'NGINX.FCGIApplication.pas', 12 | NGINX.FCGIRequest in 'NGINX.FCGIRequest.pas', 13 | NGINX.FCGIStream in 'NGINX.FCGIStream.pas', 14 | NGINX.FCGIRecord in 'NGINX.FCGIRecord.pas', 15 | NGINX.FCGIConstants in 'NGINX.FCGIConstants.pas', 16 | NGINX.FCGIResponse in 'NGINX.FCGIResponse.pas', 17 | ServerConst1 in 'ServerConst1.pas', 18 | NGINX.WebModule in 'NGINX.WebModule.pas'; 19 | 20 | function BindPort(APort: Integer): Boolean; 21 | var 22 | LTestServer: IIPTestServer; 23 | begin 24 | Result := True; 25 | try 26 | LTestServer := PeerFactory.CreatePeer('', IIPTestServer) as IIPTestServer; 27 | LTestServer.TestOpenPort(APort, nil); 28 | except 29 | Result := False; 30 | end; 31 | end; 32 | 33 | function CheckPort(APort: Integer): Integer; 34 | begin 35 | if BindPort(APort) then 36 | Result := APort 37 | else 38 | Result := 0; 39 | end; 40 | 41 | procedure SetPort(const AServer: TFCGIApplication; APort: String); 42 | begin 43 | if not AServer.Active then 44 | begin 45 | APort := APort.Replace(cCommandSetPort, '').Trim; 46 | if CheckPort(APort.ToInteger) > 0 then 47 | begin 48 | AServer.DefaultPort := APort.ToInteger; 49 | Writeln(Format(sPortSet, [APort])); 50 | end 51 | else 52 | Writeln(Format(sPortInUse, [APort])); 53 | end 54 | else 55 | Writeln(sServerRunning); 56 | Write(cArrow); 57 | end; 58 | 59 | procedure StartServer(const AServer: TFCGIApplication); 60 | begin 61 | if not AServer.Active then 62 | begin 63 | if CheckPort(AServer.DefaultPort) > 0 then 64 | begin 65 | Writeln(Format(sStartingServer, [AServer.DefaultPort])); 66 | AServer.Active := True; 67 | end 68 | else 69 | Writeln(Format(sPortInUse, [AServer.DefaultPort.ToString])); 70 | end 71 | else 72 | Writeln(sServerRunning); 73 | Write(cArrow); 74 | end; 75 | 76 | procedure WriteStatus(const AServer: TFCGIApplication); 77 | begin 78 | Writeln(sIndyVersion + AServer.Version); 79 | Writeln(sActive + AServer.Active.ToString(TUseBoolStrs.True)); 80 | Writeln(sPort + AServer.DefaultPort.ToString); 81 | Write(cArrow); 82 | end; 83 | 84 | procedure StopServer(const AServer: TFCGIApplication); 85 | begin 86 | if AServer.Active then 87 | begin 88 | Writeln(sStoppingServer); 89 | AServer.Active := False; 90 | Writeln(sServerStopped); 91 | end 92 | else 93 | Writeln(sServerNotRunning); 94 | Write(cArrow); 95 | end; 96 | 97 | procedure WriteCommands; 98 | begin 99 | Writeln(sCommands); 100 | Write(cArrow); 101 | end; 102 | 103 | procedure RunServer(APort: Integer); 104 | var 105 | LServer: TFCGIApplication; 106 | LResponse: string; 107 | begin 108 | WriteCommands; 109 | LServer := TFCGIApplication.Create; 110 | try 111 | LServer.DefaultPort := APort; 112 | LServer.OnRequestIncoming := WebModule.FCGIRequestIncoming; 113 | LServer.OnRequestReceived := WebModule.FCGIRequestReceived; 114 | while True do 115 | begin 116 | Readln(LResponse); 117 | LResponse := LowerCase(LResponse); 118 | if LResponse.StartsWith(cCommandSetPort) then 119 | SetPort(LServer, LResponse) 120 | else if sametext(LResponse, cCommandStart) then 121 | StartServer(LServer) 122 | else if sametext(LResponse, cCommandStatus) then 123 | WriteStatus(LServer) 124 | else if sametext(LResponse, cCommandStop) then 125 | StopServer(LServer) 126 | else if sametext(LResponse, cCommandHelp) then 127 | WriteCommands 128 | else if sametext(LResponse, cCommandExit) then 129 | if LServer.Active then 130 | begin 131 | StopServer(LServer); 132 | break 133 | end 134 | else 135 | break 136 | else 137 | begin 138 | Writeln(sInvalidCommand); 139 | Write(cArrow); 140 | end; 141 | end; 142 | finally 143 | LServer.Free; 144 | end; 145 | end; 146 | 147 | begin 148 | try 149 | { TODO -oUser -cConsole Main : Insert code here } 150 | WebModule := TWebModule.Create; 151 | RunServer(8044); 152 | { app := TFCGIApplication.Create; 153 | try 154 | app.Run(8044); 155 | app.OnRequestIncoming := m.AppRequestIncoming; 156 | app.OnRequestReceived := m.AppRequestReceived; 157 | ReadLn; 158 | finally 159 | app.Free; 160 | end; } 161 | except 162 | on E: Exception do 163 | Writeln(E.ClassName, ': ', E.Message); 164 | end; 165 | 166 | end. 167 | -------------------------------------------------------------------------------- /NGINX/NGINX.FastCGI.dproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | {8E51664B-16C8-437F-923B-60D771751C97} 4 | 18.8 5 | None 6 | NGINX.FastCGI.dpr 7 | True 8 | Debug 9 | Win32 10 | 1 11 | Console 12 | 13 | 14 | true 15 | 16 | 17 | true 18 | Base 19 | true 20 | 21 | 22 | true 23 | Base 24 | true 25 | 26 | 27 | true 28 | Base 29 | true 30 | 31 | 32 | true 33 | Base 34 | true 35 | 36 | 37 | true 38 | Base 39 | true 40 | 41 | 42 | true 43 | Base 44 | true 45 | 46 | 47 | true 48 | Cfg_1 49 | true 50 | true 51 | 52 | 53 | true 54 | Base 55 | true 56 | 57 | 58 | .\$(Platform)\$(Config) 59 | .\$(Platform)\$(Config) 60 | false 61 | false 62 | false 63 | false 64 | false 65 | System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) 66 | NGINX_FastCGI 67 | 68 | 69 | RESTComponents;DataSnapServerMidas;emsclientfiredac;DataSnapFireDAC;FireDACADSDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;inetdb;emsedge;FireDACIBDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;DataSnapConnectors;soapserver;bindengine;CloudService;FireDACOracleDriver;FireDACMySQLDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;IndySystem;FireDACDb2Driver;FireDACInfxDriver;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;FireDACASADriver;FireDACTDataDriver;soaprtl;DbxCommonDriver;DataSnapServer;xmlrtl;DataSnapNativeClient;soapmidas;rtl;emsserverresource;DbxClientDriver;CustomIPTransport;bindcomp;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;dsnapxml;dbrtl;FireDACMongoDBDriver;IndyProtocols;$(DCC_UsePackage) 70 | 71 | 72 | DBXSqliteDriver;RESTComponents;DataSnapServerMidas;DBXInterBaseDriver;emsclientfiredac;DataSnapFireDAC;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;FireDACIBDriver;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;DataSnapNativeClient;ibxbindings;fmxobj;FireDACDSDriver;soapmidas;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;DataSnapProviderClient;dsnapxml;dbrtl;FireDACMongoDBDriver;IndyProtocols;inetdbxpress;fmxase;$(DCC_UsePackage) 73 | true 74 | 75 | 76 | DBXSqliteDriver;RESTComponents;DataSnapServerMidas;DBXInterBaseDriver;emsclientfiredac;DataSnapFireDAC;tethering;FireDACMSSQLDriver;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;FireDACIBDriver;fmx;fmxdae;FireDACDBXDriver;dbexpress;IndyCore;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;soapserver;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;IndyIPServer;IndySystem;fmxFireDAC;FireDAC;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;ibxpress;DataSnapServer;xmlrtl;DataSnapNativeClient;ibxbindings;fmxobj;FireDACDSDriver;soapmidas;rtl;DbxClientDriver;DBXSybaseASADriver;CustomIPTransport;bindcomp;DBXInformixDriver;IndyIPClient;dbxcds;FireDACODBCDriver;DataSnapIndy10ServerTransport;DataSnapProviderClient;dsnapxml;dbrtl;FireDACMongoDBDriver;IndyProtocols;inetdbxpress;fmxase;$(DCC_UsePackage) 77 | true 78 | 79 | 80 | DBXSqliteDriver;RESTComponents;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;emsclientfiredac;DataSnapFireDAC;tethering;svnui;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;svn;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;FireDACIBDriver;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;FireDACInfxDriver;fmxFireDAC;vclimg;TeeDB;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;ibxbindings;fmxobj;FireDACDSDriver;soapmidas;rtl;emsserverresource;DbxClientDriver;vclwinx;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;DataSnapProviderClient;dsnapxml;dbrtl;FireDACMongoDBDriver;IndyProtocols;inetdbxpress;fmxase;$(DCC_UsePackage) 81 | Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) 82 | Debug 83 | CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= 84 | 1033 85 | true 86 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png 87 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png 88 | 89 | 90 | DBXSqliteDriver;RESTComponents;DataSnapServerMidas;DBXDb2Driver;DBXInterBaseDriver;vclactnband;vclFireDAC;emsclientfiredac;DataSnapFireDAC;tethering;FireDACADSDriver;DBXMSSQLDriver;DatasnapConnectorsFreePascal;FireDACMSSQLDriver;vcltouch;vcldb;bindcompfmx;DBXOracleDriver;inetdb;FmxTeeUI;emsedge;FireDACIBDriver;fmx;fmxdae;vclib;FireDACDBXDriver;dbexpress;IndyCore;vclx;dsnap;DataSnapCommon;emsclient;FireDACCommon;RESTBackendComponents;DataSnapConnectors;VCLRESTComponents;soapserver;vclie;bindengine;DBXMySQLDriver;CloudService;FireDACOracleDriver;FireDACMySQLDriver;DBXFirebirdDriver;FireDACCommonODBC;FireDACCommonDriver;DataSnapClient;inet;bindcompdbx;IndyIPCommon;vcl;DBXSybaseASEDriver;IndyIPServer;IndySystem;FireDACDb2Driver;dsnapcon;FireDACMSAccDriver;FireDACInfxDriver;fmxFireDAC;vclimg;TeeDB;FireDAC;emshosting;FireDACSqliteDriver;FireDACPgDriver;ibmonitor;FireDACASADriver;DBXOdbcDriver;FireDACTDataDriver;FMXTee;soaprtl;DbxCommonDriver;ibxpress;Tee;DataSnapServer;xmlrtl;DataSnapNativeClient;ibxbindings;fmxobj;FireDACDSDriver;soapmidas;rtl;emsserverresource;DbxClientDriver;vclwinx;DBXSybaseASADriver;CustomIPTransport;vcldsnap;bindcomp;appanalytics;DBXInformixDriver;IndyIPClient;bindcompvcl;TeeUI;dbxcds;VclSmp;adortl;FireDACODBCDriver;DataSnapIndy10ServerTransport;DataSnapProviderClient;dsnapxml;dbrtl;FireDACMongoDBDriver;IndyProtocols;inetdbxpress;fmxase;$(DCC_UsePackage) 91 | true 92 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png 93 | $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png 94 | 95 | 96 | DEBUG;$(DCC_Define) 97 | true 98 | false 99 | true 100 | true 101 | true 102 | 103 | 104 | false 105 | 106 | 107 | false 108 | RELEASE;$(DCC_Define) 109 | 0 110 | 0 111 | 112 | 113 | 114 | MainSource 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | Cfg_2 126 | Base 127 | 128 | 129 | Base 130 | 131 | 132 | Cfg_1 133 | Base 134 | 135 | 136 | 137 | Delphi.Personality.12 138 | Application 139 | 140 | 141 | 142 | NGINX.FastCGI.dpr 143 | 144 | 145 | 146 | 147 | 148 | true 149 | 150 | 151 | 152 | 153 | true 154 | 155 | 156 | 157 | 158 | true 159 | 160 | 161 | 162 | 163 | NGINX_FastCGI.exe 164 | true 165 | 166 | 167 | 168 | 169 | 1 170 | 171 | 172 | Contents\MacOS 173 | 1 174 | 175 | 176 | 0 177 | 178 | 179 | 180 | 181 | classes 182 | 1 183 | 184 | 185 | classes 186 | 1 187 | 188 | 189 | 190 | 191 | res\xml 192 | 1 193 | 194 | 195 | res\xml 196 | 1 197 | 198 | 199 | 200 | 201 | library\lib\armeabi-v7a 202 | 1 203 | 204 | 205 | 206 | 207 | library\lib\armeabi 208 | 1 209 | 210 | 211 | library\lib\armeabi 212 | 1 213 | 214 | 215 | 216 | 217 | library\lib\armeabi-v7a 218 | 1 219 | 220 | 221 | 222 | 223 | library\lib\mips 224 | 1 225 | 226 | 227 | library\lib\mips 228 | 1 229 | 230 | 231 | 232 | 233 | library\lib\armeabi-v7a 234 | 1 235 | 236 | 237 | library\lib\arm64-v8a 238 | 1 239 | 240 | 241 | 242 | 243 | library\lib\armeabi-v7a 244 | 1 245 | 246 | 247 | 248 | 249 | res\drawable 250 | 1 251 | 252 | 253 | res\drawable 254 | 1 255 | 256 | 257 | 258 | 259 | res\values 260 | 1 261 | 262 | 263 | res\values 264 | 1 265 | 266 | 267 | 268 | 269 | res\values-v21 270 | 1 271 | 272 | 273 | res\values-v21 274 | 1 275 | 276 | 277 | 278 | 279 | res\values 280 | 1 281 | 282 | 283 | res\values 284 | 1 285 | 286 | 287 | 288 | 289 | res\drawable 290 | 1 291 | 292 | 293 | res\drawable 294 | 1 295 | 296 | 297 | 298 | 299 | res\drawable-xxhdpi 300 | 1 301 | 302 | 303 | res\drawable-xxhdpi 304 | 1 305 | 306 | 307 | 308 | 309 | res\drawable-ldpi 310 | 1 311 | 312 | 313 | res\drawable-ldpi 314 | 1 315 | 316 | 317 | 318 | 319 | res\drawable-mdpi 320 | 1 321 | 322 | 323 | res\drawable-mdpi 324 | 1 325 | 326 | 327 | 328 | 329 | res\drawable-hdpi 330 | 1 331 | 332 | 333 | res\drawable-hdpi 334 | 1 335 | 336 | 337 | 338 | 339 | res\drawable-xhdpi 340 | 1 341 | 342 | 343 | res\drawable-xhdpi 344 | 1 345 | 346 | 347 | 348 | 349 | res\drawable-mdpi 350 | 1 351 | 352 | 353 | res\drawable-mdpi 354 | 1 355 | 356 | 357 | 358 | 359 | res\drawable-hdpi 360 | 1 361 | 362 | 363 | res\drawable-hdpi 364 | 1 365 | 366 | 367 | 368 | 369 | res\drawable-xhdpi 370 | 1 371 | 372 | 373 | res\drawable-xhdpi 374 | 1 375 | 376 | 377 | 378 | 379 | res\drawable-xxhdpi 380 | 1 381 | 382 | 383 | res\drawable-xxhdpi 384 | 1 385 | 386 | 387 | 388 | 389 | res\drawable-xxxhdpi 390 | 1 391 | 392 | 393 | res\drawable-xxxhdpi 394 | 1 395 | 396 | 397 | 398 | 399 | res\drawable-small 400 | 1 401 | 402 | 403 | res\drawable-small 404 | 1 405 | 406 | 407 | 408 | 409 | res\drawable-normal 410 | 1 411 | 412 | 413 | res\drawable-normal 414 | 1 415 | 416 | 417 | 418 | 419 | res\drawable-large 420 | 1 421 | 422 | 423 | res\drawable-large 424 | 1 425 | 426 | 427 | 428 | 429 | res\drawable-xlarge 430 | 1 431 | 432 | 433 | res\drawable-xlarge 434 | 1 435 | 436 | 437 | 438 | 439 | res\values 440 | 1 441 | 442 | 443 | res\values 444 | 1 445 | 446 | 447 | 448 | 449 | 1 450 | 451 | 452 | Contents\MacOS 453 | 1 454 | 455 | 456 | 0 457 | 458 | 459 | 460 | 461 | Contents\MacOS 462 | 1 463 | .framework 464 | 465 | 466 | Contents\MacOS 467 | 1 468 | .framework 469 | 470 | 471 | 0 472 | 473 | 474 | 475 | 476 | 1 477 | .dylib 478 | 479 | 480 | 1 481 | .dylib 482 | 483 | 484 | 1 485 | .dylib 486 | 487 | 488 | Contents\MacOS 489 | 1 490 | .dylib 491 | 492 | 493 | Contents\MacOS 494 | 1 495 | .dylib 496 | 497 | 498 | 0 499 | .dll;.bpl 500 | 501 | 502 | 503 | 504 | 1 505 | .dylib 506 | 507 | 508 | 1 509 | .dylib 510 | 511 | 512 | 1 513 | .dylib 514 | 515 | 516 | Contents\MacOS 517 | 1 518 | .dylib 519 | 520 | 521 | Contents\MacOS 522 | 1 523 | .dylib 524 | 525 | 526 | 0 527 | .bpl 528 | 529 | 530 | 531 | 532 | 0 533 | 534 | 535 | 0 536 | 537 | 538 | 0 539 | 540 | 541 | 0 542 | 543 | 544 | 0 545 | 546 | 547 | Contents\Resources\StartUp\ 548 | 0 549 | 550 | 551 | Contents\Resources\StartUp\ 552 | 0 553 | 554 | 555 | 0 556 | 557 | 558 | 559 | 560 | 1 561 | 562 | 563 | 1 564 | 565 | 566 | 1 567 | 568 | 569 | 570 | 571 | 1 572 | 573 | 574 | 1 575 | 576 | 577 | 1 578 | 579 | 580 | 581 | 582 | 1 583 | 584 | 585 | 1 586 | 587 | 588 | 1 589 | 590 | 591 | 592 | 593 | 1 594 | 595 | 596 | 1 597 | 598 | 599 | 1 600 | 601 | 602 | 603 | 604 | 1 605 | 606 | 607 | 1 608 | 609 | 610 | 1 611 | 612 | 613 | 614 | 615 | 1 616 | 617 | 618 | 1 619 | 620 | 621 | 1 622 | 623 | 624 | 625 | 626 | 1 627 | 628 | 629 | 1 630 | 631 | 632 | 1 633 | 634 | 635 | 636 | 637 | 1 638 | 639 | 640 | 1 641 | 642 | 643 | 1 644 | 645 | 646 | 647 | 648 | 1 649 | 650 | 651 | 1 652 | 653 | 654 | 1 655 | 656 | 657 | 658 | 659 | 1 660 | 661 | 662 | 1 663 | 664 | 665 | 1 666 | 667 | 668 | 669 | 670 | 1 671 | 672 | 673 | 1 674 | 675 | 676 | 1 677 | 678 | 679 | 680 | 681 | 1 682 | 683 | 684 | 1 685 | 686 | 687 | 1 688 | 689 | 690 | 691 | 692 | 1 693 | 694 | 695 | 1 696 | 697 | 698 | 1 699 | 700 | 701 | 702 | 703 | 1 704 | 705 | 706 | 1 707 | 708 | 709 | 1 710 | 711 | 712 | 713 | 714 | 1 715 | 716 | 717 | 1 718 | 719 | 720 | 1 721 | 722 | 723 | 724 | 725 | 1 726 | 727 | 728 | 1 729 | 730 | 731 | 1 732 | 733 | 734 | 735 | 736 | 1 737 | 738 | 739 | 1 740 | 741 | 742 | 1 743 | 744 | 745 | 746 | 747 | 1 748 | 749 | 750 | 1 751 | 752 | 753 | 1 754 | 755 | 756 | 757 | 758 | 1 759 | 760 | 761 | 1 762 | 763 | 764 | 1 765 | 766 | 767 | 768 | 769 | 1 770 | 771 | 772 | 1 773 | 774 | 775 | 1 776 | 777 | 778 | 779 | 780 | 1 781 | 782 | 783 | 1 784 | 785 | 786 | 1 787 | 788 | 789 | 790 | 791 | 1 792 | 793 | 794 | 1 795 | 796 | 797 | 1 798 | 799 | 800 | 801 | 802 | 1 803 | 804 | 805 | 1 806 | 807 | 808 | 1 809 | 810 | 811 | 812 | 813 | 1 814 | 815 | 816 | 1 817 | 818 | 819 | 1 820 | 821 | 822 | 823 | 824 | 1 825 | 826 | 827 | 1 828 | 829 | 830 | 831 | 832 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 833 | 1 834 | 835 | 836 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 837 | 1 838 | 839 | 840 | 841 | 842 | 1 843 | 844 | 845 | 1 846 | 847 | 848 | 849 | 850 | ..\ 851 | 1 852 | 853 | 854 | ..\ 855 | 1 856 | 857 | 858 | 859 | 860 | 1 861 | 862 | 863 | 1 864 | 865 | 866 | 1 867 | 868 | 869 | 870 | 871 | 1 872 | 873 | 874 | 1 875 | 876 | 877 | 1 878 | 879 | 880 | 881 | 882 | ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF 883 | 1 884 | 885 | 886 | 887 | 888 | ..\ 889 | 1 890 | 891 | 892 | ..\ 893 | 1 894 | 895 | 896 | 897 | 898 | Contents 899 | 1 900 | 901 | 902 | Contents 903 | 1 904 | 905 | 906 | 907 | 908 | Contents\Resources 909 | 1 910 | 911 | 912 | Contents\Resources 913 | 1 914 | 915 | 916 | 917 | 918 | library\lib\armeabi-v7a 919 | 1 920 | 921 | 922 | library\lib\arm64-v8a 923 | 1 924 | 925 | 926 | 1 927 | 928 | 929 | 1 930 | 931 | 932 | 1 933 | 934 | 935 | 1 936 | 937 | 938 | Contents\MacOS 939 | 1 940 | 941 | 942 | Contents\MacOS 943 | 1 944 | 945 | 946 | 0 947 | 948 | 949 | 950 | 951 | library\lib\armeabi-v7a 952 | 1 953 | 954 | 955 | 956 | 957 | 1 958 | 959 | 960 | 1 961 | 962 | 963 | 964 | 965 | Assets 966 | 1 967 | 968 | 969 | Assets 970 | 1 971 | 972 | 973 | 974 | 975 | Assets 976 | 1 977 | 978 | 979 | Assets 980 | 1 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | False 996 | False 997 | False 998 | True 999 | False 1000 | 1001 | False 1002 | 1003 | 12 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | -------------------------------------------------------------------------------- /NGINX/NGINX.FastCGI.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kylixfans/FastCGI/7d3dde5eb69ca911d121c3d387e3a6414921ef64/NGINX/NGINX.FastCGI.res -------------------------------------------------------------------------------- /NGINX/NGINX.WebModule.pas: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kylixfans/FastCGI/7d3dde5eb69ca911d121c3d387e3a6414921ef64/NGINX/NGINX.WebModule.pas -------------------------------------------------------------------------------- /NGINX/ServerConst1.pas: -------------------------------------------------------------------------------- 1 | unit ServerConst1; 2 | 3 | interface 4 | 5 | resourcestring 6 | sPortInUse = '- Error: Port %s already in use'; 7 | sPortSet = '- Port set to %s'; 8 | sServerRunning = '- The Server is already running'; 9 | sStartingServer = '- Starting HTTP Server on port %d'; 10 | sStoppingServer = '- Stopping Server'; 11 | sServerStopped = '- Server Stopped'; 12 | sServerNotRunning = '- The Server is not running'; 13 | sInvalidCommand = '- Error: Invalid Command'; 14 | sIndyVersion = '- Indy Version: '; 15 | sActive = '- Active: '; 16 | sPort = '- Port: '; 17 | sSessionID = '- Session ID CookieName: '; 18 | sCommands = 'Enter a Command: ' + slineBreak + 19 | ' - "start" to start the server' + slineBreak + 20 | ' - "stop" to stop the server' + slineBreak + 21 | ' - "set port" to change the default port' + slineBreak + 22 | ' - "status" for Server status' + slineBreak + 23 | ' - "help" to show commands' + slineBreak + 24 | ' - "exit" to close the application'; 25 | 26 | const 27 | cArrow = '->'; 28 | cCommandStart = 'start'; 29 | cCommandStop = 'stop'; 30 | cCommandStatus = 'status'; 31 | cCommandHelp = 'help'; 32 | cCommandSetPort = 'set port'; 33 | cCommandExit = 'exit'; 34 | 35 | implementation 36 | 37 | end. 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastCGI for Delphi 2 | 3 | This is an implementation of [FastCGI](http://www.fastcgi.com/devkit/doc/fcgi-spec.html) for Delphi, written in Pascal. It implements the parts of the protocol that are necessary to build a simple web application using Delphi. 4 | 5 | This means that you can write web applications in Pascal that serve dynamic content. 6 | 7 | ## License and contributing 8 | 9 | This software is distributed under the terms of the MIT license. You can use it for your own projects for free under the conditions specified in LICENSE. 10 | 11 | If you have questions, feel free to contact me. Visit [www.mvcxe.com](https://www.mvcxe.com) for my contact details. 12 | 13 | If you think you found a bug, you can open an Issue on Github. If you make changes to this library, I would be happy about a pull request. 14 | 15 | ## Basic usage 16 | 17 | The most common usage scenario is to use this library together with a web server nginx. The web server will serve static content and forward HTTP requests for dynamic content to your application. 18 | 19 | Have a look at the NGINX.FCGIApplication.TFCGIApplication class for usage examples and more information. 20 | 21 | This code example shows how to create a FastCGI application and receive requests: 22 | 23 | ```pascal 24 | // TFCGIApplication Object, will accept FastCGI requests 25 | var 26 | app: TFCGIApplication; 27 | 28 | // Handle requests by responding with a 'Hello World' message 29 | procedure TWebModule.FCGIRequestReceived(const sender: TObject; 30 | const Request: TFCGIRequest; Response: TFCGIResponse); 31 | var 32 | Content: string; 33 | item: TPair; 34 | begin 35 | Content := 'Hello World!
这是中文内容
'; 36 | for item in Request.Parameters do 37 | begin 38 | Content := Content + ' ' + item.Key + '=' + TEncoding.UTF8.GetString 39 | (item.Value) + '
'; 40 | end; 41 | Response.Content := Content; 42 | end; 43 | 44 | // Create a new FCGIApplication 45 | app := TFCGIApplication.Create; 46 | app.OnRequestReceived := WebModule.FCGIRequestReceived; 47 | // Start listening on port 19000 48 | app.Run(19000); 49 | 50 | ``` 51 | 52 | ## Web server configuration 53 | 54 | For nginx, use `fastcgi_pass` to pass requests to your FastCGI application: 55 | 56 | location / { 57 | fastcgi_pass 127.0.0.1:19000; # Pass all requests to port 19000 via FastCGI. 58 | include fastcgi_params; # (Optional): Set several FastCGI parameters like the remote IP and other metadata. 59 | } 60 | 61 | For more details, refer to your web server documentation for configuration details: 62 | 63 | * [nginx documentation](http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html) 64 | --------------------------------------------------------------------------------