net/fastcgi
A FastCGI client: run PHP or any FastCGI program behind a Salam server.
import net.fastcgi
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:
Executetakes the body as a pointer and a length. With no body, passnulland0; an empty string is not a pointer.Addcopies the name and the value, so a temporary string can be freed as soon asAddreturns, andFreereleases every copy.- The body of the answer belongs to the response: call
FreeResponsewhen 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 leastSCRIPT_FILENAMEandREQUEST_METHODto 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 ParamREQUEST_METHOD and its value.| Name | Type | Description |
|---|---|---|
name | str = "" | The variable name, as PHP reads it from $_SERVER. |
value | str = "" | The variable value. |
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
endParams
struct ParamsAdd appends to it and Free releases it.| Name | Type | Description |
|---|---|---|
items | Vector | The variables, in the order they were added. |
The example for this item is shown with the ones above: net-fastcgi-Params.salam
Header
struct HeaderSet-Cookie headers arrive.| Name | Type | Description |
|---|---|---|
name | str = "" | The header name. |
value | str = "" | The header value, without the name and the colon. |
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
endResponse
struct Response| Name | Type | Description |
|---|---|---|
status | int = 0 | The HTTP status from the Status: line, or 200 when the backend sends no such line. |
headers | Vector | The headers in the order the backend sent them, without Status. |
body | void* | The body bytes, owned by the response. Pass it to FreeResponse when done. |
len | int = 0 | The number of body bytes; zero when the body is empty and body is null. |
ok | bool = false | True when a complete answer was read, whatever its status. A 404 from PHP is still ok. |
error | str = "" | 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(): ParamsThe example for this item is shown with the ones above: net-fastcgi-Params.salam
Add
func Add(p &: Params, name: str, value: str)The example for this item is shown with the ones above: net-fastcgi-Params.salam
Free
func Free(p &: Params)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.Conntcp.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): ResponseFreeResponse.The example for this item is shown with the ones above: net-fastcgi-Connect.salam
FreeResponse
func FreeResponse(r &: Response)The example for this item is shown with the ones above: net-fastcgi-Connect.salam