Commit fbab1f1b authored by Brad Fitzpatrick's avatar Brad Fitzpatrick Committed by Russ Cox

http: support HTTP/1.0 Keep-Alive

R=rsc, bradfitz1
CC=golang-dev
https://golang.org/cl/2261042
parent 5c3827cb
...@@ -678,3 +678,14 @@ func (r *Request) expectsContinue() bool { ...@@ -678,3 +678,14 @@ func (r *Request) expectsContinue() bool {
expectation, ok := r.Header["Expect"] expectation, ok := r.Header["Expect"]
return ok && strings.ToLower(expectation) == "100-continue" 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 { ...@@ -56,14 +56,19 @@ type Conn struct {
hijacked bool // connection has been hijacked by handler hijacked bool // connection has been hijacked by handler
// state for the current reply // state for the current reply
closeAfterReply bool // close connection after this reply chunking bool // using chunked transfer encoding for reply body
chunking bool // using chunked transfer encoding for reply body wroteHeader bool // reply header has been written
wroteHeader bool // reply header has been written wroteContinue bool // 100 Continue response was written
wroteContinue bool // 100 Continue response was written header map[string]string // reply header parameters
header map[string]string // reply header parameters written int64 // number of bytes written in body
written int64 // number of bytes written in body status int // status code passed to WriteHeader
status int // status code passed to WriteHeader usingTLS bool // a flag indicating connection over TLS
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. // Create new connection from rwc.
...@@ -142,10 +147,9 @@ func (c *Conn) readRequest() (req *Request, err os.Error) { ...@@ -142,10 +147,9 @@ func (c *Conn) readRequest() (req *Request, err os.Error) {
} else { } else {
// HTTP version < 1.1: cannot do chunked transfer // HTTP version < 1.1: cannot do chunked transfer
// encoding, so signal EOF by closing connection. // encoding, so signal EOF by closing connection.
// Could avoid closing the connection if there is // Will be overridden if the HTTP handler ends up
// a Content-Length: header in the response, // writing a Content-Length and the client requested
// but everyone who expects persistent connections // "Connection: keep-alive"
// does HTTP/1.1 now.
c.closeAfterReply = true c.closeAfterReply = true
c.chunking = false c.chunking = false
} }
...@@ -220,6 +224,15 @@ func (c *Conn) Write(data []byte) (n int, err os.Error) { ...@@ -220,6 +224,15 @@ func (c *Conn) Write(data []byte) (n int, err os.Error) {
return 0, ErrHijacked return 0, ErrHijacked
} }
if !c.wroteHeader { 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) c.WriteHeader(StatusOK)
} }
if len(data) == 0 { if len(data) == 0 {
...@@ -302,6 +315,14 @@ func errorKludge(c *Conn, req *Request) { ...@@ -302,6 +315,14 @@ func errorKludge(c *Conn, req *Request) {
} }
func (c *Conn) finishRequest() { 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 { if !c.wroteHeader {
c.WriteHeader(StatusOK) c.WriteHeader(StatusOK)
} }
...@@ -341,9 +362,11 @@ func (c *Conn) serve() { ...@@ -341,9 +362,11 @@ func (c *Conn) serve() {
if err != nil { if err != nil {
break 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, // Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine. // 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) c.handler.ServeHTTP(c, req)
if c.hijacked { if c.hijacked {
return return
......
...@@ -352,9 +352,20 @@ func fixLength(status int, requestMethod string, header map[string]string, te [] ...@@ -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 // Determine whether to hang up after sending a request and body, or
// receiving a response and body // receiving a response and body
// 'header' is the request headers
func shouldClose(major, minor int, header map[string]string) bool { func shouldClose(major, minor int, header map[string]string) bool {
if major < 1 || (major == 1 && minor < 1) { if major < 1 {
return true 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 { } else if v, present := header["Connection"]; present {
// TODO: Should split on commas, toss surrounding white space, // TODO: Should split on commas, toss surrounding white space,
// and check each field. // and check each field.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment