Commit 09a1f029 authored by Sebastian Schmittner's avatar Sebastian Schmittner Committed by Matt Holt

persistent fastcgi connections (#1087)

* keep fastcgi connection open

* poor mans serialisation to make up for the lack of demuxing

* pointing includes to echse's repo

* Revert "pointing includes to echse's repo"

This reverts commit 281daad8d437ba88fadd65fae5af2da99d449bea.

* switch for persistent fcgi connections on/off added

* fixing ineffectual assignments

* camel case instead of _

* only activate persistent sockets on windows (and some naming conventions/cleanup)

* gitfm import sorting

* Revert "fixing ineffectual assignments"

This reverts commit 79760344e7b1231e59a2867246e7689b05e92e18.

# Conflicts:
#	caddyhttp/staticfiles/fileserver.go

* added another mutex and deleting map entries. thx to mholts QA comments!

* thinking about it, this RW lock was not a good idea here

* thread safety

* I keep learning about mutexs in go

* some cosmetics
parent 8e54d5ce
...@@ -12,10 +12,29 @@ import ( ...@@ -12,10 +12,29 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/mholt/caddy/caddyhttp/httpserver" "github.com/mholt/caddy/caddyhttp/httpserver"
) )
// persistent fastcgi connections
type serialClient struct {
// for read/write serialisation
sync.Mutex
backend *FCGIClient
}
type concurrentPersistentConnectionsMap struct {
// for thread safe acces to the map
sync.Mutex
clientMap map[string]*serialClient
}
var persistentConnections = &(concurrentPersistentConnectionsMap{clientMap: make(map[string]*serialClient)})
// UsePersistentFcgiConnections TODO: add an option in Caddyfile and pass it down to here
var UsePersistentFcgiConnections = true
// Handler is a middleware type that can handle requests as a FastCGI client. // Handler is a middleware type that can handle requests as a FastCGI client.
type Handler struct { type Handler struct {
Next httpserver.Handler Next httpserver.Handler
...@@ -73,10 +92,26 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) ...@@ -73,10 +92,26 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
// Connect to FastCGI gateway // Connect to FastCGI gateway
network, address := rule.parseAddress() network, address := rule.parseAddress()
fcgiBackend, err := Dial(network, address)
var fcgiBackend *FCGIClient
var client *serialClient
reuse := false
if UsePersistentFcgiConnections {
persistentConnections.Lock()
client, reuse = persistentConnections.clientMap[network+address]
persistentConnections.Unlock()
}
if reuse {
client.Lock()
defer client.Unlock()
fcgiBackend = client.backend
} else {
fcgiBackend, err = Dial(network, address)
if err != nil { if err != nil {
return http.StatusBadGateway, err return http.StatusBadGateway, err
} }
}
var resp *http.Response var resp *http.Response
contentLength, _ := strconv.Atoi(r.Header.Get("Content-Length")) contentLength, _ := strconv.Atoi(r.Header.Get("Content-Length"))
...@@ -91,14 +126,18 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) ...@@ -91,14 +126,18 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
resp, err = fcgiBackend.Post(env, r.Method, r.Header.Get("Content-Type"), r.Body, contentLength) resp, err = fcgiBackend.Post(env, r.Method, r.Header.Get("Content-Type"), r.Body, contentLength)
} }
if resp.Body != nil {
defer resp.Body.Close()
}
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return http.StatusBadGateway, err return http.StatusBadGateway, err
} }
if UsePersistentFcgiConnections {
persistentConnections.Lock()
persistentConnections.clientMap[network+address] = &(serialClient{backend: fcgiBackend})
persistentConnections.Unlock()
} else {
defer fcgiBackend.Close()
}
// Write response header // Write response header
writeHeader(w, resp) writeHeader(w, resp)
...@@ -112,6 +151,10 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) ...@@ -112,6 +151,10 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
if fcgiBackend.stderr.Len() != 0 { if fcgiBackend.stderr.Len() != 0 {
// Remove trailing newline, error logger already does this. // Remove trailing newline, error logger already does this.
err = LogError(strings.TrimSuffix(fcgiBackend.stderr.String(), "\n")) err = LogError(strings.TrimSuffix(fcgiBackend.stderr.String(), "\n"))
persistentConnections.Lock()
delete(persistentConnections.clientMap, network+address)
persistentConnections.Unlock()
fcgiBackend.Close()
} }
// Normally we would return the status code if it is an error status (>= 400), // Normally we would return the status code if it is an error status (>= 400),
......
...@@ -385,7 +385,7 @@ func (w *streamReader) Read(p []byte) (n int, err error) { ...@@ -385,7 +385,7 @@ func (w *streamReader) Read(p []byte) (n int, err error) {
// Do made the request and returns a io.Reader that translates the data read // Do made the request and returns a io.Reader that translates the data read
// from fcgi responder out of fcgi packet before returning it. // from fcgi responder out of fcgi packet before returning it.
func (c *FCGIClient) Do(p map[string]string, req io.Reader) (r io.Reader, err error) { func (c *FCGIClient) Do(p map[string]string, req io.Reader) (r io.Reader, err error) {
err = c.writeBeginRequest(uint16(Responder), 0) err = c.writeBeginRequest(uint16(Responder), FCGIKeepConn)
if err != nil { if err != nil {
return return
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment