// RoundTrip issues a single HTTP request and returns its response. Per the // http.RoundTripper interface, RoundTrip only returns an error if there // was a problem with the request being malformed // (ErrInvalidFetchRequest) or the URL Fetch proxy fails (ErrFetch). // Note that HTTP response codes such as 5xx, 403, 404, etc are not // errors as far as the transport is concerned and will be returned // with err set to nil. func (t *Transport) RoundTrip(req *http.Request) (res *http.Response, err os.Error) { methNum, ok := pb.URLFetchRequest_RequestMethod_value[req.Method] if !ok { return nil, &ErrInvalidFetchRequest{"Unsupported method: " + req.Method, nil} } method := pb.URLFetchRequest_RequestMethod(methNum) freq := &pb.URLFetchRequest{ Method: &method, Url: proto.String(req.URL.String()), FollowRedirects: proto.Bool(false), // http.Client's responsibility MustValidateServerCertificate: proto.Bool(!t.AllowInvalidServerCertificate), } if t.DeadlineSeconds != 0 { freq.Deadline = proto.Float64(t.DeadlineSeconds) } for k, vals := range req.Header { for _, val := range vals { freq.Header = append(freq.Header, &pb.URLFetchRequest_Header{ Key: proto.String(k), Value: proto.String(val), }) } } if methodAcceptsRequestBody[req.Method] { freq.Payload, err = ioutil.ReadAll(req.Body) if err != nil { return nil, &ErrInvalidFetchRequest{"Failed to read body", err} } } fres := &pb.URLFetchResponse{} if err := t.Context.Call("urlfetch", "Fetch", freq, fres); err != nil { return nil, &ErrFetch{err.String()} } res = &http.Response{} res.StatusCode = int(*fres.StatusCode) res.Status = fmt.Sprintf("%d %s", res.StatusCode, statusCodeToText(res.StatusCode)) res.Header = http.Header(make(map[string][]string)) res.RequestMethod = req.Method // Faked: res.ProtoMajor = 1 res.ProtoMinor = 1 res.Proto = "HTTP/1.1" res.Close = true for _, h := range fres.Header { hkey := http.CanonicalHeaderKey(*h.Key) hval := *h.Value if hkey == "Content-Length" { // Will get filled in below for all but HEAD requests. if req.Method == "HEAD" { res.ContentLength, _ = strconv.Atoi64(hval) } continue } res.Header.Add(hkey, hval) } if req.Method != "HEAD" { res.ContentLength = int64(len(fres.Content)) } truncated := proto.GetBool(fres.ContentWasTruncated) res.Body = &bodyReader{content: fres.Content, truncated: truncated} return }
// RoundTrip issues a single HTTP request and returns its response. Per the // http.RoundTripper interface, RoundTrip only returns an error if there // was an unsupported request or the URL Fetch proxy fails. // Note that HTTP response codes such as 5xx, 403, 404, etc are not // errors as far as the transport is concerned and will be returned // with err set to nil. func (t *Transport) RoundTrip(req *http.Request) (res *http.Response, err error) { methNum, ok := pb.URLFetchRequest_RequestMethod_value[req.Method] if !ok { return nil, fmt.Errorf("urlfetch: unsupported HTTP method %q", req.Method) } method := pb.URLFetchRequest_RequestMethod(methNum) freq := &pb.URLFetchRequest{ Method: &method, Url: proto.String(urlString(req.URL)), FollowRedirects: proto.Bool(false), // http.Client's responsibility MustValidateServerCertificate: proto.Bool(!t.AllowInvalidServerCertificate), } opts := &appengine_internal.CallOptions{} if t.Deadline != 0 { freq.Deadline = proto.Float64(t.Deadline.Seconds()) opts.Timeout = t.Deadline } for k, vals := range req.Header { for _, val := range vals { freq.Header = append(freq.Header, &pb.URLFetchRequest_Header{ Key: proto.String(k), Value: proto.String(val), }) } } if methodAcceptsRequestBody[req.Method] && req.Body != nil { freq.Payload, err = ioutil.ReadAll(req.Body) if err != nil { return nil, err } } fres := &pb.URLFetchResponse{} if err := t.Context.Call("urlfetch", "Fetch", freq, fres, opts); err != nil { return nil, err } res = &http.Response{} res.StatusCode = int(*fres.StatusCode) res.Status = fmt.Sprintf("%d %s", res.StatusCode, statusCodeToText(res.StatusCode)) res.Header = make(http.Header) res.Request = req // Faked: res.ProtoMajor = 1 res.ProtoMinor = 1 res.Proto = "HTTP/1.1" res.Close = true for _, h := range fres.Header { hkey := http.CanonicalHeaderKey(*h.Key) hval := *h.Value if hkey == "Content-Length" { // Will get filled in below for all but HEAD requests. if req.Method == "HEAD" { res.ContentLength, _ = strconv.ParseInt(hval, 10, 64) } continue } res.Header.Add(hkey, hval) } if req.Method != "HEAD" { res.ContentLength = int64(len(fres.Content)) } truncated := fres.GetContentWasTruncated() res.Body = &bodyReader{content: fres.Content, truncated: truncated} return }