diff options
Diffstat (limited to 'vendor/google.golang.org/appengine/urlfetch')
-rw-r--r-- | vendor/google.golang.org/appengine/urlfetch/urlfetch.go | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/vendor/google.golang.org/appengine/urlfetch/urlfetch.go b/vendor/google.golang.org/appengine/urlfetch/urlfetch.go new file mode 100644 index 0000000..6ffe1e6 --- /dev/null +++ b/vendor/google.golang.org/appengine/urlfetch/urlfetch.go | |||
@@ -0,0 +1,210 @@ | |||
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 | } | ||