1
0
mirror of https://github.com/golang/go synced 2024-11-25 00:07:56 -07:00

http: add Server type supporting timeouts

R=rsc
CC=golang-dev
https://golang.org/cl/4172041
This commit is contained in:
Brad Fitzpatrick 2011-02-10 14:36:22 -08:00
parent 12bdb29bdf
commit 876e9d1b89
2 changed files with 107 additions and 7 deletions

View File

@ -9,10 +9,13 @@ package http
import (
"bufio"
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"net"
"testing"
"time"
)
type dummyAddr string
@ -283,3 +286,66 @@ func TestMuxRedirectLeadingSlashes(t *testing.T) {
}
}
}
func TestServerTimeouts(t *testing.T) {
l, err := net.ListenTCP("tcp", &net.TCPAddr{Port: 0})
if err != nil {
t.Fatalf("listen error: %v", err)
}
addr, _ := l.Addr().(*net.TCPAddr)
reqNum := 0
handler := HandlerFunc(func(res ResponseWriter, req *Request) {
reqNum++
fmt.Fprintf(res, "req=%d", reqNum)
})
const second = 1000000000 /* nanos */
server := &Server{Handler: handler, ReadTimeout: 0.25 * second, WriteTimeout: 0.25 * second}
go server.Serve(l)
url := fmt.Sprintf("http://localhost:%d/", addr.Port)
// Hit the HTTP server successfully.
r, _, err := Get(url)
if err != nil {
t.Fatalf("http Get #1: %v", err)
}
got, _ := ioutil.ReadAll(r.Body)
expected := "req=1"
if string(got) != expected {
t.Errorf("Unexpected response for request #1; got %q; expected %q",
string(got), expected)
}
// Slow client that should timeout.
t1 := time.Nanoseconds()
conn, err := net.Dial("tcp", "", fmt.Sprintf("localhost:%d", addr.Port))
if err != nil {
t.Fatalf("Dial: %v", err)
}
buf := make([]byte, 1)
n, err := conn.Read(buf)
latency := time.Nanoseconds() - t1
if n != 0 || err != os.EOF {
t.Errorf("Read = %v, %v, wanted %v, %v", n, err, 0, os.EOF)
}
if latency < second*0.20 /* fudge from 0.25 above */ {
t.Errorf("got EOF after %d ns, want >= %d", latency, second*0.20)
}
// Hit the HTTP server successfully again, verifying that the
// previous slow connection didn't run our handler. (that we
// get "req=2", not "req=3")
r, _, err = Get(url)
if err != nil {
t.Fatalf("http Get #2: %v", err)
}
got, _ = ioutil.ReadAll(r.Body)
expected = "req=2"
if string(got) != expected {
t.Errorf("Get #2 got %q, want %q", string(got), expected)
}
l.Close()
}

View File

@ -670,6 +670,39 @@ func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
// read requests and then call handler to reply to them.
// Handler is typically nil, in which case the DefaultServeMux is used.
func Serve(l net.Listener, handler Handler) os.Error {
srv := &Server{Handler: handler}
return srv.Serve(l)
}
// A Server defines parameters for running an HTTP server.
type Server struct {
Addr string // TCP address to listen on, ":http" if empty
Handler Handler // handler to invoke, http.DefaultServeMux if nil
ReadTimeout int64 // the net.Conn.SetReadTimeout value for new connections
WriteTimeout int64 // the net.Conn.SetWriteTimeout value for new connections
}
// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections. If
// srv.Addr is blank, ":http" is used.
func (srv *Server) ListenAndServe() os.Error {
addr := srv.Addr
if addr == "" {
addr = ":http"
}
l, e := net.Listen("tcp", addr)
if e != nil {
return e
}
return srv.Serve(l)
}
// Serve accepts incoming connections on the Listener l, creating a
// new service thread for each. The service threads read requests and
// then call srv.Handler to reply to them.
func (srv *Server) Serve(l net.Listener) os.Error {
defer l.Close()
handler := srv.Handler
if handler == nil {
handler = DefaultServeMux
}
@ -678,6 +711,12 @@ func Serve(l net.Listener, handler Handler) os.Error {
if e != nil {
return e
}
if srv.ReadTimeout != 0 {
rw.SetReadTimeout(srv.ReadTimeout)
}
if srv.WriteTimeout != 0 {
rw.SetWriteTimeout(srv.WriteTimeout)
}
c, err := newConn(rw, handler)
if err != nil {
continue
@ -715,13 +754,8 @@ func Serve(l net.Listener, handler Handler) os.Error {
// }
// }
func ListenAndServe(addr string, handler Handler) os.Error {
l, e := net.Listen("tcp", addr)
if e != nil {
return e
}
e = Serve(l, handler)
l.Close()
return e
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
// ListenAndServeTLS acts identically to ListenAndServe, except that it