From e180185856d758f48f5982d50f6d3b53ab34d004 Mon Sep 17 00:00:00 2001 From: Will Norris Date: Tue, 29 Nov 2016 15:30:07 -0800 Subject: [PATCH] add copy of net/http package this includes an exact copy of the TimeoutHandler func and supporting types from net/http, modified only to handle minor issues for imported types. Ref: #75 --- third_party/http/LICENSE | 27 ++++++++ third_party/http/README | 1 + third_party/http/server.go | 136 +++++++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 third_party/http/LICENSE create mode 100644 third_party/http/README create mode 100644 third_party/http/server.go diff --git a/third_party/http/LICENSE b/third_party/http/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/third_party/http/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/http/README b/third_party/http/README new file mode 100644 index 0000000..893738a --- /dev/null +++ b/third_party/http/README @@ -0,0 +1 @@ +Package http is based on a copy of net/http. diff --git a/third_party/http/server.go b/third_party/http/server.go new file mode 100644 index 0000000..133c3c9 --- /dev/null +++ b/third_party/http/server.go @@ -0,0 +1,136 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package http provides helpers for HTTP servers. +package http + +import ( + "bytes" + "errors" + "io" + "net/http" + "sync" + "time" +) + +// TimeoutHandler returns a Handler that runs h with the given time limit. +// +// The new Handler calls h.ServeHTTP to handle each request, but if a +// call runs for longer than its time limit, the handler responds with +// a 503 Service Unavailable error and the given message in its body. +// (If msg is empty, a suitable default message will be sent.) +// After such a timeout, writes by h to its ResponseWriter will return +// ErrHandlerTimeout. +// +// TimeoutHandler buffers all Handler writes to memory and does not +// support the Hijacker or Flusher interfaces. +func TimeoutHandler(h http.Handler, dt time.Duration, msg string) http.Handler { + return &timeoutHandler{ + handler: h, + body: msg, + dt: dt, + } +} + +// ErrHandlerTimeout is returned on ResponseWriter Write calls +// in handlers which have timed out. +var ErrHandlerTimeout = errors.New("http: Handler timeout") + +type timeoutHandler struct { + handler http.Handler + body string + dt time.Duration + + // When set, no timer will be created and this channel will + // be used instead. + testTimeout <-chan time.Time +} + +func (h *timeoutHandler) errorBody() string { + if h.body != "" { + return h.body + } + return "Timeout

Timeout

" +} + +func (h *timeoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + var t *time.Timer + timeout := h.testTimeout + if timeout == nil { + t = time.NewTimer(h.dt) + timeout = t.C + } + done := make(chan struct{}) + tw := &timeoutWriter{ + w: w, + h: make(http.Header), + } + go func() { + h.handler.ServeHTTP(tw, r) + close(done) + }() + select { + case <-done: + tw.mu.Lock() + defer tw.mu.Unlock() + dst := w.Header() + for k, vv := range tw.h { + dst[k] = vv + } + if !tw.wroteHeader { + tw.code = http.StatusOK + } + w.WriteHeader(tw.code) + w.Write(tw.wbuf.Bytes()) + if t != nil { + t.Stop() + } + case <-timeout: + tw.mu.Lock() + defer tw.mu.Unlock() + w.WriteHeader(http.StatusServiceUnavailable) + io.WriteString(w, h.errorBody()) + tw.timedOut = true + return + } +} + +type timeoutWriter struct { + w http.ResponseWriter + h http.Header + wbuf bytes.Buffer + + mu sync.Mutex + timedOut bool + wroteHeader bool + code int +} + +func (tw *timeoutWriter) Header() http.Header { return tw.h } + +func (tw *timeoutWriter) Write(p []byte) (int, error) { + tw.mu.Lock() + defer tw.mu.Unlock() + if tw.timedOut { + return 0, ErrHandlerTimeout + } + if !tw.wroteHeader { + tw.writeHeader(http.StatusOK) + } + return tw.wbuf.Write(p) +} + +func (tw *timeoutWriter) WriteHeader(code int) { + tw.mu.Lock() + defer tw.mu.Unlock() + if tw.timedOut || tw.wroteHeader { + return + } + tw.writeHeader(code) +} + +func (tw *timeoutWriter) writeHeader(code int) { + tw.wroteHeader = true + tw.code = code +}