github.com/mackerelio/mackerel-agent-plugins@v0.89.3/mackerel-plugin-php-fpm/lib/fcgi.go (about)

     1  //go:build linux
     2  
     3  package mpphpfpm
     4  
     5  import (
     6  	"bytes"
     7  	"io"
     8  	"net/http"
     9  	"strconv"
    10  	"time"
    11  
    12  	"github.com/tomasen/fcgi_client"
    13  )
    14  
    15  // FastCGITransport is an implementation of RoundTripper that supports FastCGI.
    16  type FastCGITransport struct {
    17  	Network string
    18  	Address string
    19  }
    20  
    21  func (*FastCGITransport) timeout(req *http.Request) time.Duration {
    22  	t, ok := req.Context().Deadline()
    23  	if !ok {
    24  		return 0 // no timeout
    25  	}
    26  	return time.Until(t)
    27  }
    28  
    29  // RoundTrip implements the RoundTripper interface.
    30  func (t *FastCGITransport) RoundTrip(req *http.Request) (*http.Response, error) {
    31  	c, err := fcgiclient.DialTimeout(t.Network, t.Address, t.timeout(req))
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	defer c.Close()
    36  
    37  	params := make(map[string]string)
    38  	params["REQUEST_METHOD"] = req.Method
    39  	if req.ContentLength >= 0 {
    40  		params["CONTENT_LENGTH"] = strconv.FormatInt(req.ContentLength, 10)
    41  	}
    42  
    43  	// https://github.com/dreamcat4/php-fpm/blob/master/cgi/cgi_main.c#L781
    44  	params["PATH_INFO"] = req.URL.Path       // TODO(lufia): correct?
    45  	params["SCRIPT_NAME"] = req.URL.Path     // TODO(lufia): correct?
    46  	params["SCRIPT_FILENAME"] = req.URL.Path // TODO(lufia): correct?
    47  	params["REQUEST_URI"] = req.URL.RequestURI()
    48  	params["QUERY_STRING"] = req.URL.RawQuery
    49  	params["SERVER_NAME"] = req.URL.Hostname()
    50  	params["SERVER_ADDR"] = req.URL.Port()
    51  	if req.URL.Scheme == "https" {
    52  		params["HTTPS"] = "on"
    53  	}
    54  	params["SERVER_PROTOCOL"] = req.Proto
    55  	if ctype := req.Header.Get("Content-Type"); ctype != "" {
    56  		params["CONTENT_TYPE"] = ctype
    57  	}
    58  	if ua := req.Header.Get("User-Agent"); ua != "" {
    59  		params["USER_AGENT"] = ua
    60  	}
    61  	resp, err := c.Request(params, req.Body)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	body := resp.Body
    66  	defer body.Close()
    67  
    68  	// Var c can disconnect before end of reading of resp.Body.
    69  	// So contents of resp.Body should copy in memory.
    70  	var buf bytes.Buffer
    71  	if _, err := buf.ReadFrom(body); err != nil {
    72  		return nil, err
    73  	}
    74  	resp.Body = io.NopCloser(&buf)
    75  	return resp, nil
    76  }