mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-27 00:32:44 -04:00 
			
		
		
		
	I am not a lawyer, but according to the appendix of the license, these boilerplate notices should be included with every source file.
		
			
				
	
	
		
			248 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			248 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2015 Light Code Labs, LLC
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //     http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| package httpserver
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"io"
 | |
| 	"net/http"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // ResponseRecorder is a type of http.ResponseWriter that captures
 | |
| // the status code written to it and also the size of the body
 | |
| // written in the response. A status code does not have
 | |
| // to be written, however, in which case 200 must be assumed.
 | |
| // It is best to have the constructor initialize this type
 | |
| // with that default status code.
 | |
| //
 | |
| // Setting the Replacer field allows middlewares to type-assert
 | |
| // the http.ResponseWriter to ResponseRecorder and set their own
 | |
| // placeholder values for logging utilities to use.
 | |
| //
 | |
| // Beware when accessing the Replacer value; it may be nil!
 | |
| type ResponseRecorder struct {
 | |
| 	*ResponseWriterWrapper
 | |
| 	Replacer Replacer
 | |
| 	status   int
 | |
| 	size     int
 | |
| 	start    time.Time
 | |
| }
 | |
| 
 | |
| // NewResponseRecorder makes and returns a new ResponseRecorder.
 | |
| // Because a status is not set unless WriteHeader is called
 | |
| // explicitly, this constructor initializes with a status code
 | |
| // of 200 to cover the default case.
 | |
| func NewResponseRecorder(w http.ResponseWriter) *ResponseRecorder {
 | |
| 	return &ResponseRecorder{
 | |
| 		ResponseWriterWrapper: &ResponseWriterWrapper{ResponseWriter: w},
 | |
| 		status:                http.StatusOK,
 | |
| 		start:                 time.Now(),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // WriteHeader records the status code and calls the
 | |
| // underlying ResponseWriter's WriteHeader method.
 | |
| func (r *ResponseRecorder) WriteHeader(status int) {
 | |
| 	r.status = status
 | |
| 	r.ResponseWriterWrapper.WriteHeader(status)
 | |
| }
 | |
| 
 | |
| // Write is a wrapper that records the size of the body
 | |
| // that gets written.
 | |
| func (r *ResponseRecorder) Write(buf []byte) (int, error) {
 | |
| 	n, err := r.ResponseWriterWrapper.Write(buf)
 | |
| 	if err == nil {
 | |
| 		r.size += n
 | |
| 	}
 | |
| 	return n, err
 | |
| }
 | |
| 
 | |
| // Size returns the size of the recorded response body.
 | |
| func (r *ResponseRecorder) Size() int {
 | |
| 	return r.size
 | |
| }
 | |
| 
 | |
| // Status returns the recorded response status code.
 | |
| func (r *ResponseRecorder) Status() int {
 | |
| 	return r.status
 | |
| }
 | |
| 
 | |
| // ResponseBuffer is a type that conditionally buffers the
 | |
| // response in memory. It implements http.ResponseWriter so
 | |
| // that it can stream the response if it is not buffering.
 | |
| // Whether it buffers is decided by a func passed into the
 | |
| // constructor, NewResponseBuffer.
 | |
| //
 | |
| // This type implements http.ResponseWriter, so you can pass
 | |
| // this to the Next() middleware in the chain and record its
 | |
| // response. However, since the entire response body will be
 | |
| // buffered in memory, only use this when explicitly configured
 | |
| // and required for some specific reason. For example, the
 | |
| // text/template package only parses templates out of []byte
 | |
| // and not io.Reader, so the templates directive uses this
 | |
| // type to obtain the entire template text, but only on certain
 | |
| // requests that match the right Content-Type, etc.
 | |
| //
 | |
| // ResponseBuffer also implements io.ReaderFrom for performance
 | |
| // reasons. The standard lib's http.response type (unexported)
 | |
| // uses io.Copy to write the body. io.Copy makes an allocation
 | |
| // if the destination does not have a ReadFrom method (or if
 | |
| // the source does not have a WriteTo method, but that's
 | |
| // irrelevant here). Our ReadFrom is smart: if buffering, it
 | |
| // calls the buffer's ReadFrom, which makes no allocs because
 | |
| // it is already a buffer! If we're streaming the response
 | |
| // instead, ReadFrom uses io.CopyBuffer with a pooled buffer
 | |
| // that is managed within this package.
 | |
| type ResponseBuffer struct {
 | |
| 	*ResponseWriterWrapper
 | |
| 	Buffer       *bytes.Buffer
 | |
| 	header       http.Header
 | |
| 	status       int
 | |
| 	shouldBuffer func(status int, header http.Header) bool
 | |
| 	stream       bool
 | |
| 	rw           http.ResponseWriter
 | |
| }
 | |
| 
 | |
| // NewResponseBuffer returns a new ResponseBuffer that will
 | |
| // use buf to store the full body of the response if shouldBuffer
 | |
| // returns true. If shouldBuffer returns false, then the response
 | |
| // body will be streamed directly to rw.
 | |
| //
 | |
| // shouldBuffer will be passed the status code and header fields of
 | |
| // the response. With that information, the function should decide
 | |
| // whether to buffer the response in memory. For example: the templates
 | |
| // directive uses this to determine whether the response is the
 | |
| // right Content-Type (according to user config) for a template.
 | |
| //
 | |
| // For performance, the buf you pass in should probably be obtained
 | |
| // from a sync.Pool in order to reuse allocated space.
 | |
| func NewResponseBuffer(buf *bytes.Buffer, rw http.ResponseWriter,
 | |
| 	shouldBuffer func(status int, header http.Header) bool) *ResponseBuffer {
 | |
| 	rb := &ResponseBuffer{
 | |
| 		Buffer:       buf,
 | |
| 		header:       make(http.Header),
 | |
| 		status:       http.StatusOK, // default status code
 | |
| 		shouldBuffer: shouldBuffer,
 | |
| 		rw:           rw,
 | |
| 	}
 | |
| 	rb.ResponseWriterWrapper = &ResponseWriterWrapper{ResponseWriter: rw}
 | |
| 	return rb
 | |
| }
 | |
| 
 | |
| // Header returns the response header map.
 | |
| func (rb *ResponseBuffer) Header() http.Header {
 | |
| 	return rb.header
 | |
| }
 | |
| 
 | |
| // WriteHeader calls shouldBuffer to decide whether the
 | |
| // upcoming body should be buffered, and then writes
 | |
| // the header to the response.
 | |
| func (rb *ResponseBuffer) WriteHeader(status int) {
 | |
| 	rb.status = status
 | |
| 	rb.stream = !rb.shouldBuffer(status, rb.header)
 | |
| 	if rb.stream {
 | |
| 		rb.CopyHeader()
 | |
| 		rb.ResponseWriterWrapper.WriteHeader(status)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Write writes buf to rb.Buffer if buffering, otherwise
 | |
| // to the ResponseWriter directly if streaming.
 | |
| func (rb *ResponseBuffer) Write(buf []byte) (int, error) {
 | |
| 	if rb.stream {
 | |
| 		return rb.ResponseWriterWrapper.Write(buf)
 | |
| 	}
 | |
| 	return rb.Buffer.Write(buf)
 | |
| }
 | |
| 
 | |
| // Buffered returns whether rb has decided to buffer the response.
 | |
| func (rb *ResponseBuffer) Buffered() bool {
 | |
| 	return !rb.stream
 | |
| }
 | |
| 
 | |
| // CopyHeader copies the buffered header in rb to the ResponseWriter,
 | |
| // but it does not write the header out.
 | |
| func (rb *ResponseBuffer) CopyHeader() {
 | |
| 	for field, val := range rb.header {
 | |
| 		rb.ResponseWriterWrapper.Header()[field] = val
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ReadFrom avoids allocations when writing to the buffer (if buffering),
 | |
| // and reduces allocations when writing to the ResponseWriter directly
 | |
| // (if streaming).
 | |
| //
 | |
| // In local testing with the templates directive, req/sec were improved
 | |
| // from ~8,200 to ~9,600 on templated files by ensuring that this type
 | |
| // implements io.ReaderFrom.
 | |
| func (rb *ResponseBuffer) ReadFrom(src io.Reader) (int64, error) {
 | |
| 	if rb.stream {
 | |
| 		// first see if we can avoid any allocations at all
 | |
| 		if wt, ok := src.(io.WriterTo); ok {
 | |
| 			return wt.WriteTo(rb.ResponseWriterWrapper)
 | |
| 		}
 | |
| 		// if not, use a pooled copy buffer to reduce allocs
 | |
| 		// (this improved req/sec from ~25,300 to ~27,000 on
 | |
| 		// static files served directly with the fileserver,
 | |
| 		// but results fluctuated a little on each run).
 | |
| 		// a note of caution:
 | |
| 		// https://go-review.googlesource.com/c/22134#message-ff351762308fe05f6b72a487d6842e3988916486
 | |
| 		buf := respBufPool.Get().([]byte)
 | |
| 		n, err := io.CopyBuffer(rb.ResponseWriterWrapper, src, buf)
 | |
| 		respBufPool.Put(buf) // defer'ing this slowed down benchmarks a smidgin, I think
 | |
| 		return n, err
 | |
| 	}
 | |
| 	return rb.Buffer.ReadFrom(src)
 | |
| }
 | |
| 
 | |
| // StatusCodeWriter returns an http.ResponseWriter that always
 | |
| // writes the status code stored in rb from when a response
 | |
| // was buffered to it.
 | |
| func (rb *ResponseBuffer) StatusCodeWriter(w http.ResponseWriter) http.ResponseWriter {
 | |
| 	return forcedStatusCodeWriter{w, rb}
 | |
| }
 | |
| 
 | |
| // forcedStatusCodeWriter is used to force a status code when
 | |
| // writing the header. It uses the status code saved on rb.
 | |
| // This is useful if passing a http.ResponseWriter into
 | |
| // http.ServeContent because ServeContent hard-codes 2xx status
 | |
| // codes. If we buffered the response, we force that status code
 | |
| // instead.
 | |
| type forcedStatusCodeWriter struct {
 | |
| 	http.ResponseWriter
 | |
| 	rb *ResponseBuffer
 | |
| }
 | |
| 
 | |
| func (fscw forcedStatusCodeWriter) WriteHeader(int) {
 | |
| 	fscw.ResponseWriter.WriteHeader(fscw.rb.status)
 | |
| }
 | |
| 
 | |
| // respBufPool is used for io.CopyBuffer when ResponseBuffer
 | |
| // is configured to stream a response.
 | |
| var respBufPool = &sync.Pool{
 | |
| 	New: func() interface{} {
 | |
| 		return make([]byte, 32*1024)
 | |
| 	},
 | |
| }
 | |
| 
 | |
| // Interface guards
 | |
| var (
 | |
| 	_ HTTPInterfaces = (*ResponseRecorder)(nil)
 | |
| 	_ HTTPInterfaces = (*ResponseBuffer)(nil)
 | |
| 	_ io.ReaderFrom  = (*ResponseBuffer)(nil)
 | |
| )
 |