mirror of
https://github.com/golang/go
synced 2024-11-21 14:14:40 -07:00
http: support HTTP/1.0 Keep-Alive
R=rsc, bradfitz1 CC=golang-dev https://golang.org/cl/2261042
This commit is contained in:
parent
5c3827cb9f
commit
fbab1f1bad
@ -678,3 +678,14 @@ func (r *Request) expectsContinue() bool {
|
||||
expectation, ok := r.Header["Expect"]
|
||||
return ok && strings.ToLower(expectation) == "100-continue"
|
||||
}
|
||||
|
||||
func (r *Request) wantsHttp10KeepAlive() bool {
|
||||
if r.ProtoMajor != 1 || r.ProtoMinor != 0 {
|
||||
return false
|
||||
}
|
||||
value, exists := r.Header["Connection"]
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
return strings.Index(strings.ToLower(value), "keep-alive") != -1
|
||||
}
|
||||
|
@ -56,14 +56,19 @@ type Conn struct {
|
||||
hijacked bool // connection has been hijacked by handler
|
||||
|
||||
// state for the current reply
|
||||
closeAfterReply bool // close connection after this reply
|
||||
chunking bool // using chunked transfer encoding for reply body
|
||||
wroteHeader bool // reply header has been written
|
||||
wroteContinue bool // 100 Continue response was written
|
||||
header map[string]string // reply header parameters
|
||||
written int64 // number of bytes written in body
|
||||
status int // status code passed to WriteHeader
|
||||
usingTLS bool // a flag indicating connection over TLS
|
||||
chunking bool // using chunked transfer encoding for reply body
|
||||
wroteHeader bool // reply header has been written
|
||||
wroteContinue bool // 100 Continue response was written
|
||||
header map[string]string // reply header parameters
|
||||
written int64 // number of bytes written in body
|
||||
status int // status code passed to WriteHeader
|
||||
usingTLS bool // a flag indicating connection over TLS
|
||||
|
||||
// close connection after this reply. set on request and
|
||||
// updated after response from handler if there's a
|
||||
// "Connection: keep-alive" response header and a
|
||||
// Content-Length.
|
||||
closeAfterReply bool
|
||||
}
|
||||
|
||||
// Create new connection from rwc.
|
||||
@ -142,10 +147,9 @@ func (c *Conn) readRequest() (req *Request, err os.Error) {
|
||||
} else {
|
||||
// HTTP version < 1.1: cannot do chunked transfer
|
||||
// encoding, so signal EOF by closing connection.
|
||||
// Could avoid closing the connection if there is
|
||||
// a Content-Length: header in the response,
|
||||
// but everyone who expects persistent connections
|
||||
// does HTTP/1.1 now.
|
||||
// Will be overridden if the HTTP handler ends up
|
||||
// writing a Content-Length and the client requested
|
||||
// "Connection: keep-alive"
|
||||
c.closeAfterReply = true
|
||||
c.chunking = false
|
||||
}
|
||||
@ -220,6 +224,15 @@ func (c *Conn) Write(data []byte) (n int, err os.Error) {
|
||||
return 0, ErrHijacked
|
||||
}
|
||||
if !c.wroteHeader {
|
||||
if c.Req.wantsHttp10KeepAlive() {
|
||||
_, hasLength := c.header["Content-Length"]
|
||||
if hasLength {
|
||||
_, connectionHeaderSet := c.header["Connection"]
|
||||
if !connectionHeaderSet {
|
||||
c.header["Connection"] = "keep-alive"
|
||||
}
|
||||
}
|
||||
}
|
||||
c.WriteHeader(StatusOK)
|
||||
}
|
||||
if len(data) == 0 {
|
||||
@ -302,6 +315,14 @@ func errorKludge(c *Conn, req *Request) {
|
||||
}
|
||||
|
||||
func (c *Conn) finishRequest() {
|
||||
// If this was an HTTP/1.0 request with keep-alive and we sent a Content-Length
|
||||
// back, we can make this a keep-alive response ...
|
||||
if c.Req.wantsHttp10KeepAlive() {
|
||||
_, sentLength := c.header["Content-Length"]
|
||||
if sentLength && c.header["Connection"] == "keep-alive" {
|
||||
c.closeAfterReply = false
|
||||
}
|
||||
}
|
||||
if !c.wroteHeader {
|
||||
c.WriteHeader(StatusOK)
|
||||
}
|
||||
@ -341,9 +362,11 @@ func (c *Conn) serve() {
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
// HTTP cannot have multiple simultaneous active requests.
|
||||
// HTTP cannot have multiple simultaneous active requests.[*]
|
||||
// Until the server replies to this request, it can't read another,
|
||||
// so we might as well run the handler in this goroutine.
|
||||
// [*] Not strictly true: HTTP pipelining. We could let them all process
|
||||
// in parallel even if their responses need to be serialized.
|
||||
c.handler.ServeHTTP(c, req)
|
||||
if c.hijacked {
|
||||
return
|
||||
|
@ -352,9 +352,20 @@ func fixLength(status int, requestMethod string, header map[string]string, te []
|
||||
|
||||
// Determine whether to hang up after sending a request and body, or
|
||||
// receiving a response and body
|
||||
// 'header' is the request headers
|
||||
func shouldClose(major, minor int, header map[string]string) bool {
|
||||
if major < 1 || (major == 1 && minor < 1) {
|
||||
if major < 1 {
|
||||
return true
|
||||
} else if major == 1 && minor == 0 {
|
||||
v, present := header["Connection"]
|
||||
if !present {
|
||||
return true
|
||||
}
|
||||
v = strings.ToLower(v)
|
||||
if strings.Index(v, "keep-alive") == -1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} else if v, present := header["Connection"]; present {
|
||||
// TODO: Should split on commas, toss surrounding white space,
|
||||
// and check each field.
|
||||
|
Loading…
Reference in New Issue
Block a user