func buildTestRequest(method string, path string, body string, headers map[string][]string, cookies []*http.Cookie) *http.Request { host := "127.0.0.1" port := "80" rawurl := "http://" + host + ":" + port + path url_, _ := url.Parse(rawurl) proto := "HTTP/1.1" if headers == nil { headers = map[string][]string{} } headers["User-Agent"] = []string{"web.go test"} if method == "POST" { headers["Content-Length"] = []string{fmt.Sprintf("%d", len(body))} if headers["Content-Type"] == nil { headers["Content-Type"] = []string{"text/plain"} } } req := http.Request{Method: method, RawURL: rawurl, URL: url_, Proto: proto, Host: host, Header: http.Header(headers), Body: ioutil.NopCloser(bytes.NewBufferString(body)), } for _, cookie := range cookies { req.AddCookie(cookie) } return &req }
// for hdrs, see 'http://wiki.basho.com/HTTP-Fetch-Object.html' // NB: If no accept is set, we will choose multipart/mixed. func GetMultiItem(c Client, bucket, key string, hdrs http.Header, parms http.Values, respch chan<- *http.Response, cc *http.ClientConn) (err os.Error) { req := getMultiItemRequest(c, bucket, key, hdrs, parms) err = dispatchRequest(cc, req, map[int]func(*http.Response) os.Error{ -1: debugFailf(os.Stdout, true, "GetMultiItem failed"), 400: func(*http.Response) os.Error { return ErrBadRequest }, 404: func(*http.Response) os.Error { return ErrUnknownKey }, 406: func(*http.Response) os.Error { return ErrUnacceptable }, 503: func(*http.Response) os.Error { return ErrServiceUnavailable }, 200: func(resp *http.Response) (err os.Error) { // This doesn't actually happen unless someone else has resolved the item for us, but we'll // take it if it happens. respch <- resp return }, 300: func(resp *http.Response) (err os.Error) { mtype, mparms := mime.ParseMediaType(resp.Header.Get("Content-Type")) if mtype != "multipart/mixed" { return debugFailf(os.Stdout, true, "Server gave us a 300, but not a multipart/mixed message\t"+mtype)(resp) } if err == nil && mparms["boundary"] == "" { err = os.NewError("No boundry name found in content-type") } if err == nil { mpart := multipart.NewReader(io.LimitReader(resp.Body, resp.ContentLength), mparms["boundary"]) var part *multipart.Part for part, err = mpart.NextPart(); err == nil; part, err = mpart.NextPart() { // if we don't swallow the reader now, the caller may not get their bits (multipart closes when we call NextPart()). buff := bytes.NewBuffer(nil) n, _ := buff.ReadFrom(part) rr := &http.Response{ Body: ioutil.NopCloser(buff), Header: http.Header(part.Header), ContentLength: int64(n), } // Riak doesn't include a vclock in the sub-headers, and readers may want to use them. rr.Header.Set("X-Riak-Vclock", resp.Header.Get("X-Riak-Vclock")) respch <- rr } if err == os.EOF { err = nil } } return }, }) close(respch) return }
// 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 }