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.
} 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
}
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 {
}
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)
}
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
// 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.