~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/testing/roundtripper.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2013 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package testing
 
5
 
 
6
import (
 
7
        "fmt"
 
8
        "io/ioutil"
 
9
        "net/http"
 
10
        "strings"
 
11
)
 
12
 
 
13
// CannedRoundTripper can be used to provide canned "http" responses without
 
14
// actually starting an HTTP server.
 
15
//
 
16
// Use this in conjunction with ProxyRoundTripper. A ProxyRoundTripper is
 
17
// what gets registered as the default handler for a given protocol (such as
 
18
// "test") and then tests can direct the ProxyRoundTripper to delegate to a
 
19
// CannedRoundTripper. The reason for this is that we can register a
 
20
// roundtripper to handle a scheme, but there is no way to unregister it: you
 
21
// may need to re-use the same ProxyRoundTripper but use different
 
22
// CannedRoundTrippers to return different results.
 
23
type CannedRoundTripper struct {
 
24
        // files maps file names to their contents. If the roundtripper
 
25
        // receives a request for any of these files, and none of the entries
 
26
        // in errorURLs below matches, it will return the contents associated
 
27
        // with that filename here.
 
28
        // TODO(jtv): Do something more sensible here: either make files take
 
29
        // precedence over errors, or return the given error *with* the given
 
30
        // contents, or just disallow overlap.
 
31
        files map[string]string
 
32
 
 
33
        // errorURLs are prefixes that should return specific HTTP status
 
34
        // codes. If a request's URL matches any of these prefixes, the
 
35
        // associated error status is returned.
 
36
        // There is no clever longest-prefix selection here. If more than
 
37
        // one prefix matches, any one of them may be used.
 
38
        // TODO(jtv): Decide what to do about multiple matching prefixes.
 
39
        errorURLS map[string]int
 
40
}
 
41
 
 
42
var _ http.RoundTripper = (*CannedRoundTripper)(nil)
 
43
 
 
44
// ProxyRoundTripper is an http.RoundTripper implementation that does nothing
 
45
// but delegate to another RoundTripper. This lets tests change how they handle
 
46
// requests for a given scheme, despite the fact that the standard library does
 
47
// not support un-registration, or registration of a new roundtripper with a
 
48
// URL scheme that's already handled.
 
49
//
 
50
// Use the RegisterForScheme method to install this as the standard handler
 
51
// for a particular protocol. For example, if you call
 
52
// prt.RegisterForScheme("test") then afterwards, any request to "test:///foo"
 
53
// will be routed to prt.
 
54
type ProxyRoundTripper struct {
 
55
        // Sub is the roundtripper that this roundtripper delegates to, if any.
 
56
        // If you leave this nil, this roundtripper is effectively disabled.
 
57
        Sub http.RoundTripper
 
58
}
 
59
 
 
60
var _ http.RoundTripper = (*ProxyRoundTripper)(nil)
 
61
 
 
62
// RegisterForScheme registers a ProxyRoundTripper as the default roundtripper
 
63
// for the given URL scheme.
 
64
//
 
65
// This cannot be undone, nor overwritten with a different roundtripper. If
 
66
// you change your mind later about what the roundtripper should do, set its
 
67
// "Sub" field to delegate to a different roundtripper (or to nil if you don't
 
68
// want to handle its requests at all any more).
 
69
func (prt *ProxyRoundTripper) RegisterForScheme(scheme string) {
 
70
        http.DefaultTransport.(*http.Transport).RegisterProtocol(scheme, prt)
 
71
}
 
72
 
 
73
func (prt *ProxyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
 
74
        if prt.Sub == nil {
 
75
                panic("An attempt was made to request file content without having" +
 
76
                        " the virtual filesystem initialized.")
 
77
        }
 
78
        return prt.Sub.RoundTrip(req)
 
79
}
 
80
 
 
81
func newHTTPResponse(status string, statusCode int, body string) *http.Response {
 
82
        return &http.Response{
 
83
                Proto:      "HTTP/1.0",
 
84
                ProtoMajor: 1,
 
85
                Header:     make(http.Header),
 
86
                Close:      true,
 
87
 
 
88
                // Parameter fields:
 
89
                Status:        status,
 
90
                StatusCode:    statusCode,
 
91
                Body:          ioutil.NopCloser(strings.NewReader(body)),
 
92
                ContentLength: int64(len(body)),
 
93
        }
 
94
}
 
95
 
 
96
// RoundTrip returns a canned error or body for the given request.
 
97
func (v *CannedRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
 
98
        full := req.URL.String()
 
99
        for urlPrefix, statusCode := range v.errorURLS {
 
100
                if strings.HasPrefix(full, urlPrefix) {
 
101
                        status := fmt.Sprintf("%d Error", statusCode)
 
102
                        return newHTTPResponse(status, statusCode, ""), nil
 
103
                }
 
104
        }
 
105
        if contents, found := v.files[req.URL.Path]; found {
 
106
                return newHTTPResponse("200 OK", http.StatusOK, contents), nil
 
107
        }
 
108
        return newHTTPResponse("404 Not Found", http.StatusNotFound, ""), nil
 
109
}
 
110
 
 
111
// NewCannedRoundTripper returns a CannedRoundTripper with the given canned
 
112
// responses.
 
113
func NewCannedRoundTripper(files map[string]string, errorURLs map[string]int) *CannedRoundTripper {
 
114
        return &CannedRoundTripper{files, errorURLs}
 
115
}