urlfetch.go 5.9 KB

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