• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pact-foundation / pact-go / 5757151961

pending completion
5757151961

push

github

web-flow
Merge pull request #323 from boreyuk/golangci-lint-fixes

fix(golangci-lint): fix errors as raised by linter

24 of 80 new or added lines in 10 files covered. (30.0%)

1 existing line in 1 file now uncovered.

1842 of 4952 relevant lines covered (37.2%)

6.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

36.28
/proxy/http.go
1
package proxy
2

3
import (
4
        "crypto/tls"
5
        "fmt"
6
        "log"
7
        "net"
8
        "net/http"
9
        "net/http/httputil"
10
        "net/url"
11
        "strings"
12
        "time"
13

14
        "github.com/pact-foundation/pact-go/v2/utils"
15
)
16

17
// Middleware is a way to use composition to add functionality
18
// by intercepting the req/response cycle of the Reverse Proxy.
19
// Each handler must accept an http.Handler and also return an
20
// http.Handler, allowing a simple way to chain functionality together
21
type Middleware func(http.Handler) http.Handler
22

23
// Options for the Reverse Proxy configuration
24
type Options struct {
25

26
        // TargetScheme is one of 'http' or 'https'
27
        TargetScheme string
28

29
        // TargetAddress is the host:port component to proxy
30
        TargetAddress string
31

32
        // TargetPath is the path on the target to proxy
33
        TargetPath string
34

35
        // ProxyPort is the port to make available for proxying
36
        // Defaults to a random port
37
        ProxyPort int
38

39
        // Middleware to apply to the Proxy
40
        Middleware []Middleware
41

42
        // Internal request prefix for proxy to not rewrite
43
        InternalRequestPathPrefix string
44

45
        // Custom TLS Configuration for communicating with a Provider
46
        // Useful when verifying self-signed services, MASSL etc.
47
        CustomTLSConfig *tls.Config
48
}
49

50
// loggingMiddleware logs requests to the proxy
51
func loggingMiddleware(next http.Handler) http.Handler {
4✔
52
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
8✔
53
                log.Printf("[DEBUG] http reverse proxy received connection from %s on path %s\n", r.RemoteAddr, r.RequestURI)
4✔
54
                next.ServeHTTP(w, r)
4✔
55
        })
4✔
56
}
57

58
// chainHandlers takes a set of middleware and joins them together
59
// into a single Middleware, making it much simpler to compose middleware
60
// together
61
func chainHandlers(mw ...Middleware) Middleware {
8✔
62
        return func(final http.Handler) http.Handler {
15✔
63
                return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
11✔
64
                        last := final
4✔
65
                        for i := len(mw) - 1; i >= 0; i-- {
20✔
66
                                last = mw[i](last)
16✔
67
                        }
16✔
68
                        last.ServeHTTP(w, r)
4✔
69
                })
70
        }
71
}
72

73
// HTTPReverseProxy provides a default setup for proxying
74
// internal components within the framework
75
func HTTPReverseProxy(options Options) (int, error) {
4✔
76
        log.Println("[DEBUG] starting new proxy with opts", options)
4✔
77
        port := options.ProxyPort
4✔
78
        var err error
4✔
79

4✔
80
        url := &url.URL{
4✔
81
                Scheme: options.TargetScheme,
4✔
82
                Host:   options.TargetAddress,
4✔
83
                Path:   options.TargetPath,
4✔
84
        }
4✔
85

4✔
86
        proxy := createProxy(url, options.InternalRequestPathPrefix)
4✔
87
        proxy.Transport = customTransport{tlsConfig: options.CustomTLSConfig}
4✔
88

4✔
89
        if port == 0 {
8✔
90
                port, err = utils.GetFreePort()
4✔
91
                if err != nil {
4✔
92
                        log.Println("[ERROR] unable to start reverse proxy server:", err)
×
93
                        return 0, err
×
94
                }
×
95
        }
96

97
        wrapper := chainHandlers(append(options.Middleware, loggingMiddleware)...)
4✔
98

4✔
99
        log.Println("[DEBUG] starting reverse proxy on port", port)
4✔
100
        go func() {
7✔
101
                err := http.ListenAndServe(fmt.Sprintf(":%d", port), wrapper(proxy))
3✔
102
                if err != nil {
3✔
NEW
103
                        log.Println("[ERROR] error when starting reverse proxy server:", err)
×
NEW
104
                        panic(err)
×
105
                }
106
        }()
107

108
        return port, nil
4✔
109
}
110

111
// https://stackoverflow.com/questions/52986853/how-to-debug-httputil-newsinglehostreverseproxy
112
// Set the proxy.Transport field to an implementation that dumps the request before delegating to the default transport:
113

114
type customTransport struct {
115
        tlsConfig *tls.Config
116
}
117

118
func (c customTransport) RoundTrip(r *http.Request) (*http.Response, error) {
×
119
        b, err := httputil.DumpRequestOut(r, false)
×
120
        if err != nil {
×
121
                return nil, err
×
122
        }
×
123
        log.Println("[TRACE] proxy outgoing request\n", string(b))
×
124

×
125
        transport := &http.Transport{
×
126
                Proxy: http.ProxyFromEnvironment,
×
127
                DialContext: (&net.Dialer{
×
128
                        Timeout:   30 * time.Second,
×
129
                        KeepAlive: 30 * time.Second,
×
130
                        DualStack: true,
×
131
                }).DialContext,
×
132
                MaxIdleConns:          100,
×
133
                IdleConnTimeout:       90 * time.Second,
×
134
                TLSHandshakeTimeout:   10 * time.Second,
×
135
                ExpectContinueTimeout: 1 * time.Second,
×
136
        }
×
137

×
138
        if c.tlsConfig != nil {
×
139
                log.Println("[DEBUG] applying custom TLS config")
×
140
                transport.TLSClientConfig = c.tlsConfig
×
141
        }
×
142
        var DefaultTransport http.RoundTripper = transport
×
143

×
144
        res, err := DefaultTransport.RoundTrip(r)
×
145
        if err != nil {
×
146
                log.Println("[ERROR]", err)
×
147
                return nil, err
×
148
        }
×
149
        b, err = httputil.DumpResponse(res, true)
×
150
        log.Println("[TRACE] proxied server response\n", string(b))
×
151

×
152
        return res, err
×
153
}
154

155
// Adapted from https://github.com/golang/go/blob/master/src/net/http/httputil/reverseproxy.go
156
func createProxy(target *url.URL, ignorePrefix string) *httputil.ReverseProxy {
4✔
157
        targetQuery := target.RawQuery
4✔
158
        director := func(req *http.Request) {
4✔
159
                if !strings.HasPrefix(req.URL.Path, ignorePrefix) {
×
160
                        log.Println("[DEBUG] setting proxy to target")
×
161
                        log.Println("[DEBUG] incoming request", req.URL)
×
162
                        req.URL.Scheme = target.Scheme
×
163
                        req.URL.Host = target.Host
×
164
                        req.Host = target.Host
×
165

×
166
                        req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
×
167
                        log.Println("[DEBUG] outgoing request to target", req.URL)
×
168
                        if targetQuery == "" || req.URL.RawQuery == "" {
×
169
                                req.URL.RawQuery = targetQuery + req.URL.RawQuery
×
170
                        } else {
×
171
                                req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
×
172
                        }
×
173
                        if _, ok := req.Header["User-Agent"]; !ok {
×
174
                                req.Header.Set("User-Agent", "Pact Go")
×
175
                        }
×
176
                } else {
×
177
                        log.Println("[DEBUG] setting proxy to internal server")
×
178
                        req.URL.Scheme = "http"
×
179
                        req.URL.Host = "localhost"
×
180
                        req.Host = "localhost"
×
181
                }
×
182
        }
183
        return &httputil.ReverseProxy{Director: director}
4✔
184
}
185

186
// From httputil package
187
// https://github.com/golang/go/blob/master/src/net/http/httputil/reverseproxy.go
188
func singleJoiningSlash(a, b string) string {
×
189
        aslash := strings.HasSuffix(a, "/")
×
190
        bslash := strings.HasPrefix(b, "/")
×
191
        switch {
×
192
        case aslash && bslash:
×
193
                return a + b[1:]
×
194
        case !aslash && !bslash:
×
195
                return a + "/" + b
×
196
        }
197
        return a + b
×
198
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc