mirror of
https://github.com/golang/go
synced 2024-11-21 19:04:44 -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"]
|
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 {
|
|||||||
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) {
|
|||||||
} 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) {
|
|||||||
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) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
|||||||
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 []
|
|||||||
|
|
||||||
// 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.
|
||||||
|
Loading…
Reference in New Issue
Block a user