Standard library · Network

net/fastcgi

A FastCGI client: run PHP or any FastCGI program behind a Salam server.

import net.fastcgi
On this page
  1. Types
  2. Functions

FastCGI is the protocol php-fpm and php-cgi -b speak, and this package is a client for it. The web server in net/httpd uses it for the fastcgi_pass setting; you can also use it directly when a program has to hand one request to a PHP script.

A request is a list of CGI variables and a body. NewParams makes the list, Add fills it, Connect opens a TCP connection to the backend, and Execute sends everything and waits for the answer. The answer holds the status, every header in the order the backend sent them, and the body as a pointer with a length, so an image or a zip comes back intact.

// lang: en
package main

import net.fastcgi
import net.tcp

func main:
    p := fastcgi.NewParams()
    defer fastcgi.Free(p)
    fastcgi.Add(p, "GATEWAY_INTERFACE", "CGI/1.1")
    fastcgi.Add(p, "REQUEST_METHOD", "GET")
    fastcgi.Add(p, "SCRIPT_NAME", "/index.php")
    fastcgi.Add(p, "SCRIPT_FILENAME", "/srv/app/index.php")
    fastcgi.Add(p, "QUERY_STRING", "a=1")

    c := fastcgi.Connect("127.0.0.1", 9000, 5000)
    if not tcp.ConnOk(c):
        println "cannot reach the FastCGI backend"
        ret 1
    end
    defer tcp.Close(c)

    r := fastcgi.Execute(c, p, null, 0, 5000)
    if not r.ok:
        println "the request failed: ", r.error
        ret 1
    end
    println r.status, " ", r.len, " bytes"
    fastcgi.FreeResponse(r)
    ret 0
end

One connection carries one request, which is what php-fpm expects from a simple front end. The timeout is in milliseconds and covers the whole exchange, dialling included; zero means 30 seconds. A backend that closes the connection early, a response without a header block, or a protocol error comes back as ok = false with a message in error, never as a crash.

The things people get wrong:

  • Execute takes the body as a pointer and a length. With no body, pass null and 0; an empty string is not a pointer.
  • Add copies the name and the value, so a temporary string can be freed as soon as Add returns, and Free releases every copy.
  • The body of the answer belongs to the response: call FreeResponse when done with it, or the buffer stays allocated.
  • The address is TCP. php-fpm has to listen on a port such as 127.0.0.1:9000, not on a unix socket, and the CGI variables are yours to fill: a backend needs at least SCRIPT_FILENAME and REQUEST_METHOD to answer.
  • The backend's stderr is not an error. A PHP warning on stderr does not fail the request; only a broken or missing response does.

Types

Param

struct Param
One CGI variable: a name such as REQUEST_METHOD and its value.
NameTypeDescription
namestr = ""The variable name, as PHP reads it from $_SERVER.
valuestr = ""The variable value.
net-fastcgi-Params.salam
import net.fastcgi

func main:
    p := fastcgi.NewParams()
    defer fastcgi.Free(p)

    fastcgi.Add(p, "REQUEST_METHOD", "GET")
    fastcgi.Add(p, "SCRIPT_NAME", "/index.php")

    each item in p.items:
        println item.name + "=" + item.value
    end
end
OutputREQUEST_METHOD=GET SCRIPT_NAME=/index.php

Params

struct Params
The list of CGI variables of one request. Add appends to it and Free releases it.
NameTypeDescription
itemsVectorThe variables, in the order they were added.

The example for this item is shown with the ones above: net-fastcgi-Params.salam

struct Header
One response header, as the backend sent it. The same name may appear more than once, which is how several Set-Cookie headers arrive.
NameTypeDescription
namestr = ""The header name.
valuestr = ""The header value, without the name and the colon.
net-fastcgi-Connect.salam
import net.fastcgi
import net.tcp

func main:
    p := fastcgi.NewParams()
    defer fastcgi.Free(p)
    fastcgi.Add(p, "REQUEST_METHOD", "GET")
    fastcgi.Add(p, "SCRIPT_NAME", "/index.php")
    fastcgi.Add(p, "SCRIPT_FILENAME", "/srv/app/index.php")

    c := fastcgi.Connect("127.0.0.1", 9000, 1000)
    if not tcp.ConnOk(c):
        println "no FastCGI backend on 127.0.0.1:9000"
        ret 1
    end
    defer tcp.Close(c)

    r := fastcgi.Execute(c, p, null, 0, 5000)
    if not r.ok:
        println r.error
        ret 1
    end
    println r.status, r.len
    fastcgi.FreeResponse(r)
    ret 0
end

Response

struct Response
The answer of a backend: the status, the headers, and the body as a pointer with its length.
NameTypeDescription
statusint = 0The HTTP status from the Status: line, or 200 when the backend sends no such line.
headersVector
The headers in the order the backend sent them, without Status.
bodyvoid*The body bytes, owned by the response. Pass it to FreeResponse when done.
lenint = 0The number of body bytes; zero when the body is empty and body is null.
okbool = falseTrue when a complete answer was read, whatever its status. A 404 from PHP is still ok.
errorstr = ""Why the request failed, when ok is false: no connection, a cut response, a protocol error, or the stderr text when no header block arrived.

The example for this item is shown with the ones above: net-fastcgi-Connect.salam

Functions

NewParams

func NewParams(): Params
Makes an empty list of CGI variables.

The example for this item is shown with the ones above: net-fastcgi-Params.salam

Add

func Add(p &: Params, name: str, value: str)
Appends one CGI variable, copying the name and the value.

The example for this item is shown with the ones above: net-fastcgi-Params.salam

Free

func Free(p &: Params)
Releases the list and every name and value in it.

The example for this item is shown with the ones above: net-fastcgi-Params.salam

Connect

func Connect(host: str, port: int, timeout_ms: int): tcp.Conn
Opens a TCP connection to a FastCGI backend. The timeout is in milliseconds; zero means 30 seconds. Check it with tcp.ConnOk.

The example for this item is shown with the ones above: net-fastcgi-Connect.salam

Execute

func Execute(c: tcp.Conn, params: Params, data: void*, n: int, timeout_ms: int): Response
Sends the parameters and the body, then reads the answer. The body is a pointer and a length, and the timeout covers the whole exchange. The returned response must be released with FreeResponse.

The example for this item is shown with the ones above: net-fastcgi-Connect.salam

FreeResponse

func FreeResponse(r &: Response)
Releases the body and the header strings of a response.

The example for this item is shown with the ones above: net-fastcgi-Connect.salam