From 09a1f02971b99b8c7e4e583398608c09a9747976 Mon Sep 17 00:00:00 2001 From: Sebastian Schmittner Date: Sat, 10 Sep 2016 14:47:47 +0200 Subject: [PATCH] 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 --- caddyhttp/fastcgi/fastcgi.go | 57 +++++++++++++++++++++++++++++---- caddyhttp/fastcgi/fcgiclient.go | 2 +- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/caddyhttp/fastcgi/fastcgi.go b/caddyhttp/fastcgi/fastcgi.go index 30426b33..3a562891 100644 --- a/caddyhttp/fastcgi/fastcgi.go +++ b/caddyhttp/fastcgi/fastcgi.go @@ -12,10 +12,29 @@ import ( "path/filepath" "strconv" "strings" + "sync" "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. type Handler struct { Next httpserver.Handler @@ -73,9 +92,25 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) // Connect to FastCGI gateway network, address := rule.parseAddress() - fcgiBackend, err := Dial(network, address) - if err != nil { - return http.StatusBadGateway, err + + 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 { + return http.StatusBadGateway, err + } } var resp *http.Response @@ -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) } - if resp.Body != nil { - defer resp.Body.Close() - } - if err != nil && err != io.EOF { return http.StatusBadGateway, err } + if UsePersistentFcgiConnections { + persistentConnections.Lock() + persistentConnections.clientMap[network+address] = &(serialClient{backend: fcgiBackend}) + persistentConnections.Unlock() + } else { + defer fcgiBackend.Close() + } + // Write response header writeHeader(w, resp) @@ -112,6 +151,10 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) if fcgiBackend.stderr.Len() != 0 { // Remove trailing newline, error logger already does this. 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), diff --git a/caddyhttp/fastcgi/fcgiclient.go b/caddyhttp/fastcgi/fcgiclient.go index 9ee66092..667ce101 100644 --- a/caddyhttp/fastcgi/fcgiclient.go +++ b/caddyhttp/fastcgi/fcgiclient.go @@ -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 // 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) { - err = c.writeBeginRequest(uint16(Responder), 0) + err = c.writeBeginRequest(uint16(Responder), FCGIKeepConn) if err != nil { return }