Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / google.golang.org / appengine / urlfetch / urlfetch.go
1 // Copyright 2011 Google Inc. All rights reserved.
2 // Use of this source code is governed by the Apache 2.0
3 // license that can be found in the LICENSE file.
4
5 // Package urlfetch provides an http.RoundTripper implementation
6 // for fetching URLs via App Engine's urlfetch service.
7 package urlfetch // import "google.golang.org/appengine/urlfetch"
8
9 import (
10         "errors"
11         "fmt"
12         "io"
13         "io/ioutil"
14         "net/http"
15         "net/url"
16         "strconv"
17         "strings"
18         "time"
19
20         "github.com/golang/protobuf/proto"
21         "golang.org/x/net/context"
22
23         "google.golang.org/appengine/internal"
24         pb "google.golang.org/appengine/internal/urlfetch"
25 )
26
27 // Transport is an implementation of http.RoundTripper for
28 // App Engine. Users should generally create an http.Client using
29 // this transport and use the Client rather than using this transport
30 // directly.
31 type Transport struct {
32         Context context.Context
33
34         // Controls whether the application checks the validity of SSL certificates
35         // over HTTPS connections. A value of false (the default) instructs the
36         // application to send a request to the server only if the certificate is
37         // valid and signed by a trusted certificate authority (CA), and also
38         // includes a hostname that matches the certificate. A value of true
39         // instructs the application to perform no certificate validation.
40         AllowInvalidServerCertificate bool
41 }
42
43 // Verify statically that *Transport implements http.RoundTripper.
44 var _ http.RoundTripper = (*Transport)(nil)
45
46 // Client returns an *http.Client using a default urlfetch Transport. This
47 // client will have the default deadline of 5 seconds, and will check the
48 // validity of SSL certificates.
49 //
50 // Any deadline of the provided context will be used for requests through this client;
51 // if the client does not have a deadline then a 5 second default is used.
52 func Client(ctx context.Context) *http.Client {
53         return &http.Client{
54                 Transport: &Transport{
55                         Context: ctx,
56                 },
57         }
58 }
59
60 type bodyReader struct {
61         content   []byte
62         truncated bool
63         closed    bool
64 }
65
66 // ErrTruncatedBody is the error returned after the final Read() from a
67 // response's Body if the body has been truncated by App Engine's proxy.
68 var ErrTruncatedBody = errors.New("urlfetch: truncated body")
69
70 func statusCodeToText(code int) string {
71         if t := http.StatusText(code); t != "" {
72                 return t
73         }
74         return strconv.Itoa(code)
75 }
76
77 func (br *bodyReader) Read(p []byte) (n int, err error) {
78         if br.closed {
79                 if br.truncated {
80                         return 0, ErrTruncatedBody
81                 }
82                 return 0, io.EOF
83         }
84         n = copy(p, br.content)
85         if n > 0 {
86                 br.content = br.content[n:]
87                 return
88         }
89         if br.truncated {
90                 br.closed = true
91                 return 0, ErrTruncatedBody
92         }
93         return 0, io.EOF
94 }
95
96 func (br *bodyReader) Close() error {
97         br.closed = true
98         br.content = nil
99         return nil
100 }
101
102 // A map of the URL Fetch-accepted methods that take a request body.
103 var methodAcceptsRequestBody = map[string]bool{
104         "POST":  true,
105         "PUT":   true,
106         "PATCH": true,
107 }
108
109 // urlString returns a valid string given a URL. This function is necessary because
110 // the String method of URL doesn't correctly handle URLs with non-empty Opaque values.
111 // See http://code.google.com/p/go/issues/detail?id=4860.
112 func urlString(u *url.URL) string {
113         if u.Opaque == "" || strings.HasPrefix(u.Opaque, "//") {
114                 return u.String()
115         }
116         aux := *u
117         aux.Opaque = "//" + aux.Host + aux.Opaque
118         return aux.String()
119 }
120
121 // RoundTrip issues a single HTTP request and returns its response. Per the
122 // http.RoundTripper interface, RoundTrip only returns an error if there
123 // was an unsupported request or the URL Fetch proxy fails.
124 // Note that HTTP response codes such as 5xx, 403, 404, etc are not
125 // errors as far as the transport is concerned and will be returned
126 // with err set to nil.
127 func (t *Transport) RoundTrip(req *http.Request) (res *http.Response, err error) {
128         methNum, ok := pb.URLFetchRequest_RequestMethod_value[req.Method]
129         if !ok {
130                 return nil, fmt.Errorf("urlfetch: unsupported HTTP method %q", req.Method)
131         }
132
133         method := pb.URLFetchRequest_RequestMethod(methNum)
134
135         freq := &pb.URLFetchRequest{
136                 Method:                        &method,
137                 Url:                           proto.String(urlString(req.URL)),
138                 FollowRedirects:               proto.Bool(false), // http.Client's responsibility
139                 MustValidateServerCertificate: proto.Bool(!t.AllowInvalidServerCertificate),
140         }
141         if deadline, ok := t.Context.Deadline(); ok {
142                 freq.Deadline = proto.Float64(deadline.Sub(time.Now()).Seconds())
143         }
144
145         for k, vals := range req.Header {
146                 for _, val := range vals {
147                         freq.Header = append(freq.Header, &pb.URLFetchRequest_Header{
148                                 Key:   proto.String(k),
149                                 Value: proto.String(val),
150                         })
151                 }
152         }
153         if methodAcceptsRequestBody[req.Method] && req.Body != nil {
154                 // Avoid a []byte copy if req.Body has a Bytes method.
155                 switch b := req.Body.(type) {
156                 case interface {
157                         Bytes() []byte
158                 }:
159                         freq.Payload = b.Bytes()
160                 default:
161                         freq.Payload, err = ioutil.ReadAll(req.Body)
162                         if err != nil {
163                                 return nil, err
164                         }
165                 }
166         }
167
168         fres := &pb.URLFetchResponse{}
169         if err := internal.Call(t.Context, "urlfetch", "Fetch", freq, fres); err != nil {
170                 return nil, err
171         }
172
173         res = &http.Response{}
174         res.StatusCode = int(*fres.StatusCode)
175         res.Status = fmt.Sprintf("%d %s", res.StatusCode, statusCodeToText(res.StatusCode))
176         res.Header = make(http.Header)
177         res.Request = req
178
179         // Faked:
180         res.ProtoMajor = 1
181         res.ProtoMinor = 1
182         res.Proto = "HTTP/1.1"
183         res.Close = true
184
185         for _, h := range fres.Header {
186                 hkey := http.CanonicalHeaderKey(*h.Key)
187                 hval := *h.Value
188                 if hkey == "Content-Length" {
189                         // Will get filled in below for all but HEAD requests.
190                         if req.Method == "HEAD" {
191                                 res.ContentLength, _ = strconv.ParseInt(hval, 10, 64)
192                         }
193                         continue
194                 }
195                 res.Header.Add(hkey, hval)
196         }
197
198         if req.Method != "HEAD" {
199                 res.ContentLength = int64(len(fres.Content))
200         }
201
202         truncated := fres.GetContentWasTruncated()
203         res.Body = &bodyReader{content: fres.Content, truncated: truncated}
204         return
205 }
206
207 func init() {
208         internal.RegisterErrorCodeMap("urlfetch", pb.URLFetchServiceError_ErrorCode_name)
209         internal.RegisterTimeoutErrorCode("urlfetch", int32(pb.URLFetchServiceError_DEADLINE_EXCEEDED))
210 }