func (srv *Server) handlerExecutePipeline(request *Request, keepAlive bool) *http.Response { var res *http.Response // execute the pipeline if res = srv.Pipeline.execute(request); res == nil { res = StringResponse(request.HttpRequest, 404, nil, "Not Found") } // The res.Write omits Content-length on 0 length bodies, and by spec, // it SHOULD. While this is not MUST, it's kinda broken. See sec 4.4 // of rfc2616 and a 200 with a zero length does not satisfy any of the // 5 conditions if Connection: keep-alive is set :( // I'm forcing chunked which seems to work because I couldn't get the // content length to write if it was 0. // Specifically, the android http client waits forever if there's no // content-length instead of assuming zero at the end of headers. der. if res.Body == nil { if request.HttpRequest.Method != "HEAD" { res.ContentLength = 0 } res.TransferEncoding = []string{"identity"} res.Body = ioutil.NopCloser(bytes.NewBuffer([]byte{})) } else if res.ContentLength == 0 && len(res.TransferEncoding) == 0 && !((res.StatusCode-100 < 100) || res.StatusCode == 204 || res.StatusCode == 304) { // the following is copied from net/http/transfer.go // in the std lib, this is only applied to a request. we need it on a response // Test to see if it's actually zero or just unset. var buf [1]byte n, _ := io.ReadFull(res.Body, buf[:]) if n == 1 { // Oh, guess there is data in this Body Reader after all. // The ContentLength field just wasn't set. // Stich the Body back together again, re-attaching our // consumed byte. res.ContentLength = -1 res.Body = &lengthFixReadCloser{io.MultiReader(bytes.NewBuffer(buf[:]), res.Body), res.Body} } else { res.TransferEncoding = []string{"identity"} } } if res.ContentLength < 0 && request.HttpRequest.Method != "HEAD" { res.TransferEncoding = []string{"chunked"} } // For HTTP/1.0 and Keep-Alive, sending the Connection: Keep-Alive response header is required // because close is default (opposite of 1.1) if keepAlive && !request.HttpRequest.ProtoAtLeast(1, 1) { res.Header.Set("Connection", "Keep-Alive") } // cleanup request.HttpRequest.Body.Close() return res }
// RoundTrip implements http.RoundTripper.RoundTrip. func (binder Binder) RoundTrip(req *http.Request) (*http.Response, error) { if req.Proto == "" { req.Proto = fmt.Sprintf("HTTP/%d.%d", req.ProtoMajor, req.ProtoMinor) } if req.Body != nil { if req.ContentLength == -1 { req.TransferEncoding = []string{"chunked"} } } else { req.Body = ioutil.NopCloser(bytes.NewReader(nil)) } recorder := httptest.NewRecorder() binder.handler.ServeHTTP(recorder, req) resp := http.Response{ Request: req, StatusCode: recorder.Code, Status: http.StatusText(recorder.Code), Header: recorder.HeaderMap, } if recorder.Flushed { resp.TransferEncoding = []string{"chunked"} } if recorder.Body != nil { resp.Body = ioutil.NopCloser(recorder.Body) } return &resp, nil }
func FixResponse(resp *http.Response) { // https://code.google.com/p/go/issues/detail?id=5381 // fix add Content-Length: 0 when resp.Write() if resp != nil && resp.StatusCode == 200 && resp.ContentLength == 0 && len(resp.TransferEncoding) == 0 { resp.TransferEncoding = append(resp.TransferEncoding, "identity") } }
func (c *URLCache) Restore(res *http.Response) { res.Status = c.CachedResponse.Status res.StatusCode = c.CachedResponse.StatusCode res.Header = c.CachedResponse.Header res.ContentLength = c.CachedResponse.ContentLength res.TransferEncoding = c.CachedResponse.TransferEncoding res.Body = &ClosableBuffer{bytes.NewReader(c.CachedBody)} res.Header.Set(CachedHeader, CachedHeaderVal) res.Header.Set(CachedMD5Header, c.MD5) }
// ResponseHeader returns a new set of headers from a request. func ResponseHeader(res *http.Response) *Header { return &Header{ h: res.Header, host: func() string { return "" }, cl: func() int64 { return res.ContentLength }, te: func() []string { return res.TransferEncoding }, setHost: func(string) {}, setCL: func(cl int64) { res.ContentLength = cl }, setTE: func(te []string) { res.TransferEncoding = te }, } }
func (p *MockResponseWriter) String() string { resp := new(http.Response) resp.StatusCode = p.StatusCode resp.Proto = "HTTP/1.1" resp.ProtoMajor = 1 resp.ProtoMinor = 1 resp.Header = p.Headers resp.Body = ioutil.NopCloser(bytes.NewBuffer(p.Buffer.Bytes())) resp.ContentLength = int64(p.Buffer.Len()) if p.Headers.Get("Transfer-Encoding") != "" { resp.TransferEncoding = []string{p.Headers.Get("Transfer-Encoding")} } else { resp.TransferEncoding = nil } resp.Close = p.Headers.Get("Connection") == "close" resp.Trailer = make(http.Header) resp.Request = p.Request b, _ := httputil.DumpResponse(resp, true) return string(b) }
func marshalResponseBody(r *http.Response, body interface{}) error { newBody, err := json.Marshal(body) if err != nil { return err } r.Body = ioutil.NopCloser(bytes.NewReader(newBody)) r.ContentLength = int64(len(newBody)) // Stop it being chunked, because that hangs r.TransferEncoding = nil return nil }
func (res *HTTPResponseEvent) ToResponse(body bool) *http.Response { raw := new(http.Response) raw.Header = res.Headers raw.ProtoMajor = 1 raw.ProtoMinor = 1 raw.StatusCode = int(res.StatusCode) raw.Status = http.StatusText(raw.StatusCode) raw.TransferEncoding = res.Headers["TransferEncoding"] if body { raw.Body = NewHTTPBody(res.GetContentLength(), res.Content) } return raw }
func (f *EtagFilter) FilterResponse(request *falcore.Request, res *http.Response) { request.CurrentStage.Status = 1 // Skipped (default) if if_none_match := request.HttpRequest.Header.Get("If-None-Match"); if_none_match != "" { if res.StatusCode == 200 && res.Header.Get("Etag") == if_none_match { res.StatusCode = 304 res.Status = "304 Not Modified" res.Body.Close() res.Body = nil res.ContentLength = 0 res.TransferEncoding = nil request.CurrentStage.Status = 0 // Success } } }
func (r *response) Response() *http.Response { if r.Data == nil { r.Data = new(bytes.Buffer) } out := new(http.Response) out.Status = fmt.Sprintf("%d %s", r.StatusCode, http.StatusText(r.StatusCode)) out.StatusCode = r.StatusCode out.Proto = "HTTP/1.1" out.ProtoMajor = 1 out.ProtoMinor = 1 out.Header = r.Header out.Body = &readCloser{r.Data} out.ContentLength = int64(r.Data.Len()) out.TransferEncoding = nil out.Close = true out.Trailer = make(http.Header) out.Request = r.Request return out }
func (srv *Server) handler(c net.Conn) { startTime := time.Now() bpe := srv.bufferPool.take(c) defer srv.bufferPool.give(bpe) var closeSentinelChan = make(chan int) go srv.sentinel(c, closeSentinelChan) defer srv.connectionFinished(c, closeSentinelChan) var err error var req *http.Request // no keepalive (for now) reqCount := 0 keepAlive := true for err == nil && keepAlive { if req, err = http.ReadRequest(bpe.br); err == nil { if req.Header.Get("Connection") != "Keep-Alive" { keepAlive = false } request := newRequest(req, c, startTime) reqCount++ var res *http.Response pssInit := new(PipelineStageStat) pssInit.Name = "server.Init" pssInit.StartTime = startTime pssInit.EndTime = time.Now() request.appendPipelineStage(pssInit) // execute the pipeline if res = srv.Pipeline.execute(request); res == nil { res = SimpleResponse(req, 404, nil, "Not Found") } // cleanup request.startPipelineStage("server.ResponseWrite") req.Body.Close() // shutting down? select { case <-srv.stopAccepting: keepAlive = false res.Close = true default: } // The res.Write omits Content-length on 0 length bodies, and by spec, // it SHOULD. While this is not MUST, it's kinda broken. See sec 4.4 // of rfc2616 and a 200 with a zero length does not satisfy any of the // 5 conditions if Connection: keep-alive is set :( // I'm forcing chunked which seems to work because I couldn't get the // content length to write if it was 0. // Specifically, the android http client waits forever if there's no // content-length instead of assuming zero at the end of headers. der. if res.ContentLength == 0 && len(res.TransferEncoding) == 0 && !((res.StatusCode-100 < 100) || res.StatusCode == 204 || res.StatusCode == 304) { res.TransferEncoding = []string{"identity"} } if res.ContentLength < 0 { res.TransferEncoding = []string{"chunked"} } // For HTTP/1.0 and Keep-Alive, sending the Connection: Keep-Alive response header is required // because close is default (opposite of 1.1) if keepAlive && !req.ProtoAtLeast(1, 1) { res.Header.Add("Connection", "Keep-Alive") } // write response if srv.sendfile { res.Write(c) srv.cycleNonBlock(c) } else { wbuf := bufio.NewWriter(c) res.Write(wbuf) wbuf.Flush() } if res.Body != nil { res.Body.Close() } request.finishPipelineStage() request.finishRequest() srv.requestFinished(request) if res.Close { keepAlive = false } // Reset the startTime // this isn't great since there may be lag between requests; but it's the best we've got startTime = time.Now() } else { // EOF is socket closed if nerr, ok := err.(net.Error); err != io.EOF && !(ok && nerr.Timeout()) { Error("%s %v ERROR reading request: <%T %v>", srv.serverLogPrefix(), c.RemoteAddr(), err, err) } } } //Debug("%s Processed %v requests on connection %v", srv.serverLogPrefix(), reqCount, c.RemoteAddr()) }
func (r *Response) Response() *http.Response { out := new(http.Response) r.headerM.Lock() out.Status = fmt.Sprintf("%d %s", r.StatusCode, http.StatusText(r.StatusCode)) out.StatusCode = r.StatusCode out.Header = r.Header r.headerM.Unlock() out.Proto = "HTTP/1.1" out.ProtoMajor = 1 out.ProtoMinor = 1 r.dataM.Lock() if r.data == nil { out.Body = &ReadCloser{new(bytes.Buffer)} } else if unrequestedGzip(r) { // User-agents MUST support gzip compression. // Regardless of the Accept-Encoding sent by the user-agent, the server may // always send content encoded with gzip or deflate encoding. r.data.Prep() out.Header.Del("Content-Encoding") out.Header.Del("Content-Length") out.ContentLength = -1 out.Body = &gzipReader{body: r.data} } else { r.data.Prep() out.Body = r.data out.ContentLength = r.data.written } r.dataM.Unlock() out.TransferEncoding = nil out.Close = true out.Trailer = make(http.Header) out.Request = r.Request return out }