Pyjo.Server.FastCGI - FastCGI connections of the servers

import Pyjo.Server.Daemon

daemon = Pyjo.Server.Daemon.new(listen=['fcgi://127.0.0.1:9000'])


@daemon.unsubscribe('request').on('request')
def request(daemon, tx):
    # Request
    method = tx.req.method
    path = tx.req.url.path

    # Response
    tx.res.code = 200
    tx.res.headers.content_type = 'text/plain'
    tx.res.body = f'{method} request for {path}!'.encode()

    # Resume transaction
    tx.resume()


daemon.run()

Pyjo.Server.FastCGI speaks FastCGI 1.0 on the connections of the listen locations fcgi:// and fcgi+unix:// of Pyjo.Server.Daemon, and so of the servers built on it, Pyjo.Server.Prefork, Pyjo.Server.Hot and Pyjo.Server.Reload, with the same applications, timeouts, signals and graceful shutdown as HTTP.

$ ./myapp.py daemon -l fcgi://127.0.0.1:9000
$ ./myapp.py prefork -l fcgi+unix://%2Ftmp%2Fmyapp.sock

A web server in front of the application speaks HTTP to the clients and FastCGI to the application, which gets the request as the environment of RFC 3875, as Pyjo.Server.CGI does, but in a process that keeps running. A connection takes one request at a time, in the role of a responder, the only one that it supports, and stays open after it when the web server asks for it with FCGI_KEEP_CONN. The content of the response can come later, as with Pyjo.Server.Daemon, and goes to the web server as soon as it is written. WebSockets are not available, as FastCGI has no protocol for them.

The application can also be started by the web server, which gives it its listen socket as the file descriptor 0, as Apache with mod_fcgid and lighttpd do; the command Pyjoyment.Command.fastcgi serves it, and is detected by Pyjoyment.Commands, so the script of the application needs no command.

Environment

The request is read from the parameters of FastCGI, as the environment of CGI that Pyjo.Message.Request.Pyjo_Message_Request.parse() takes, with a few changes for what the web servers send:

  • A path in SCRIPT_NAME without PATH_INFO is the path of the request, as nginx with its fastcgi_params and Caddy send the whole path in SCRIPT_NAME. An application deployed under a path prefix gets it in SCRIPT_NAME and the rest of the path in PATH_INFO.

  • The bytes of the paths beyond ASCII are percent-encoded, as the path is escaped, as with Pyjo.Server.WSGI.

  • A request body without CONTENT_LENGTH, of a chunked request whose framing the web server removed, is read whole before the request is parsed.

Web servers

  • Caddy, with transport fastcgi: it sends the response to the client with the Status header next to its status line.

    example.com {
      reverse_proxy 127.0.0.1:9000 {
        transport fastcgi
      }
    }
    
  • Apache with mod_proxy_fcgi:

    ProxyPass / fcgi://127.0.0.1:9000/ enablereuse=on
    
  • Apache with mod_fcgid, which starts the application:

    <VirtualHost *:80>
      ServerName localhost
      ScriptAlias / /home/sri/myapp/myapp.py/
      <Directory /home/sri/myapp>
        SetHandler fcgid-script
        Options +ExecCGI
        Require all granted
      </Directory>
    </VirtualHost>
    
  • nginx:

    location / {
      include fastcgi_params;
      fastcgi_pass 127.0.0.1:9000;
      fastcgi_keep_conn on;
    }
    

See also

Pyjoyment.Command.fastcgi, Pyjo.Server.Daemon, Pyjoyment, https://mojolicious.org.

It has no counterpart in Mojolicious; Pyjo.Server.CGI is the counterpart of Mojo::Server::CGI.

Classes

class Pyjo.Server.FastCGI.Pyjo_Server_FastCGI(daemon: Pyjo_Server_Daemon, id: str)[source]

Pyjo.Server.FastCGI is the FastCGI side of a connection of Pyjo.Server.Daemon, which makes it for the connections of its listen locations fcgi:// and fcgi+unix://.

closed() → None[source]
fcgi.closed()

The connection has been closed, which finishes the transaction of the request in progress.

id
id = fcgi.id

Id of the connection in the event loop of the daemon.

read(chunk: bytes) → None[source]
fcgi.read(chunk)

Read the records of a chunk of data from the web server.