mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-03-21 17:44:58 -05:00
302 lines
8.3 KiB
Go
302 lines
8.3 KiB
Go
package nhttp
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
_http "net/http"
|
|
"path"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const TrailerPrefix = "Trailer:"
|
|
|
|
func fixPragmaCacheControl(header _http.Header) {
|
|
if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" {
|
|
if _, presentcc := header["Cache-Control"]; !presentcc {
|
|
header["Cache-Control"] = []string{"no-cache"}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *response) finalTrailers() _http.Header {
|
|
var t _http.Header
|
|
for k, vv := range w.handlerHeader {
|
|
if strings.HasPrefix(k, TrailerPrefix) {
|
|
if t == nil {
|
|
t = make(_http.Header)
|
|
}
|
|
t[strings.TrimPrefix(k, TrailerPrefix)] = vv
|
|
}
|
|
}
|
|
for _, k := range w.trailers {
|
|
if t == nil {
|
|
t = make(_http.Header)
|
|
}
|
|
for _, v := range w.handlerHeader[k] {
|
|
t.Add(k, v)
|
|
}
|
|
}
|
|
return t
|
|
}
|
|
|
|
func (w *response) sendExpectationFailed() {
|
|
// TODO(bradfitz): let ServeHTTP handlers handle
|
|
// requests with non-standard expectation[s]? Seems
|
|
// theoretical at best, and doesn't fit into the
|
|
// current ServeHTTP model anyway. We'd need to
|
|
// make the ResponseWriter an optional
|
|
// "ExpectReplier" interface or something.
|
|
//
|
|
// For now we'll just obey RFC 7231 5.1.1 which says
|
|
// "A server that receives an Expect field-value other
|
|
// than 100-continue MAY respond with a 417 (Expectation
|
|
// Failed) status code to indicate that the unexpected
|
|
// expectation cannot be met."
|
|
w.Header().Set("Connection", "close")
|
|
w.WriteHeader(_http.StatusExpectationFailed)
|
|
w.finishRequest()
|
|
}
|
|
|
|
func (w *response) finishRequest() {
|
|
w.handlerDone.SetTrue()
|
|
|
|
if !w.wroteHeader {
|
|
w.WriteHeader(_http.StatusOK)
|
|
}
|
|
|
|
w.w.Flush()
|
|
putBufioWriter(w.w)
|
|
w.cw.close()
|
|
w.conn.bufw.Flush()
|
|
|
|
w.conn.r.abortPendingRead()
|
|
|
|
// Close the body (regardless of w.closeAfterReply) so we can
|
|
// re-use its bufio.Reader later safely.
|
|
w.reqBody.Close()
|
|
|
|
if w.req.MultipartForm != nil {
|
|
w.req.MultipartForm.RemoveAll()
|
|
}
|
|
}
|
|
|
|
func (w *response) Header() _http.Header {
|
|
if w.cw.header == nil && w.wroteHeader && !w.cw.wroteHeader {
|
|
// Accessing the header between logically writing it
|
|
// and physically writing it means we need to allocate
|
|
// a clone to snapshot the logically written state.
|
|
w.cw.header = w.handlerHeader.Clone()
|
|
}
|
|
w.calledHeader = true
|
|
return w.handlerHeader
|
|
}
|
|
|
|
func (w *response) WriteHeader(code int) {
|
|
if w.wroteHeader {
|
|
caller := relevantCaller()
|
|
log.Printf("nhttp: superfluous response.WriteHeader call from %s (%s:%d)\n", caller.Function, path.Base(caller.File), caller.Line)
|
|
return
|
|
}
|
|
checkWriteHeaderCode(code)
|
|
|
|
// Handle informational headers
|
|
if code >= 100 && code <= 199 {
|
|
// Prevent a potential race with an automatically-sent 100 Continue triggered by Request.Body.Read()
|
|
if code == 100 && w.canWriteContinue.IsSet() {
|
|
w.writeContinueMu.Lock()
|
|
w.canWriteContinue.SetFalse()
|
|
w.writeContinueMu.Unlock()
|
|
}
|
|
|
|
writeStatusLine(w.conn.bufw, w.req.ProtoAtLeast(1, 1), code, w.statusBuf[:])
|
|
|
|
// Per RFC 8297 we must not clear the current header map
|
|
w.handlerHeader.WriteSubset(w.conn.bufw, map[string]bool{"Content-Length": true, "Transfer-Encoding": true})
|
|
w.conn.bufw.Write(crlf)
|
|
w.conn.bufw.Flush()
|
|
|
|
return
|
|
}
|
|
|
|
w.wroteHeader = true
|
|
w.status = code
|
|
|
|
if w.calledHeader && w.cw.header == nil {
|
|
w.cw.header = w.handlerHeader.Clone()
|
|
}
|
|
|
|
if cl := w.handlerHeader.Get("Content-Length"); cl != "" {
|
|
v, err := strconv.ParseInt(cl, 10, 64)
|
|
if err == nil && v >= 0 {
|
|
w.contentLength = v
|
|
} else {
|
|
log.Printf("nhttp: invalid Content-Length of %q\n", cl)
|
|
w.handlerHeader.Del("Content-Length")
|
|
}
|
|
}
|
|
}
|
|
|
|
// relevantCaller searches the call stack for the first function outside of net/nhttp.
|
|
// The purpose of this function is to provide more helpful error messages.
|
|
func relevantCaller() runtime.Frame {
|
|
pc := make([]uintptr, 16)
|
|
n := runtime.Callers(1, pc)
|
|
frames := runtime.CallersFrames(pc[:n])
|
|
var frame runtime.Frame
|
|
for {
|
|
frame, more := frames.Next()
|
|
if !strings.HasPrefix(frame.Function, "net/nhttp.") {
|
|
return frame
|
|
}
|
|
if !more {
|
|
break
|
|
}
|
|
}
|
|
return frame
|
|
}
|
|
|
|
func checkWriteHeaderCode(code int) {
|
|
// Issue 22880: require valid WriteHeader status codes.
|
|
// For now we only enforce that it's three digits.
|
|
// In the future we might block things over 599 (600 and above aren't defined
|
|
// at https://httpwg.org/specs/rfc7231.html#status.codes).
|
|
// But for now any three digits.
|
|
//
|
|
// We used to send "HTTP/1.1 000 0" on the wire in responses but there's
|
|
// no equivalent bogus thing we can realistically send in HTTP/2,
|
|
// so we'll consistently panic instead and help people find their bugs
|
|
// early. (We can't return an error from WriteHeader even if we wanted to.)
|
|
if code < 100 || code > 999 {
|
|
panic(fmt.Sprintf("invalid WriteHeader code %v", code))
|
|
}
|
|
}
|
|
|
|
// writeStatusLine writes an HTTP/1.x Status-Line (RFC 7230 Section 3.1.2)
|
|
// to bw. is11 is whether the HTTP request is HTTP/1.1. false means HTTP/1.0.
|
|
// code is the response status code.
|
|
// scratch is an optional scratch buffer. If it has at least capacity 3, it's used.
|
|
func writeStatusLine(bw *bufio.Writer, is11 bool, code int, scratch []byte) {
|
|
if is11 {
|
|
bw.WriteString("HTTP/1.1 ")
|
|
} else {
|
|
bw.WriteString("HTTP/1.0 ")
|
|
}
|
|
if text := _http.StatusText(code); text != "" {
|
|
bw.Write(strconv.AppendInt(scratch[:0], int64(code), 10))
|
|
bw.WriteByte(' ')
|
|
bw.WriteString(text)
|
|
bw.WriteString("\r\n")
|
|
} else {
|
|
// don't worry about performance
|
|
fmt.Fprintf(bw, "%03d status code %d\r\n", code, code)
|
|
}
|
|
}
|
|
|
|
func (w *response) Write(data []byte) (n int, err error) {
|
|
return w.write(len(data), data, "")
|
|
}
|
|
|
|
func (w *response) WriteString(data string) (n int, err error) {
|
|
return w.write(len(data), nil, data)
|
|
}
|
|
|
|
// either dataB or dataS is non-zero.
|
|
func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err error) {
|
|
if w.canWriteContinue.IsSet() {
|
|
// Body reader wants to write 100 Continue but hasn't yet.
|
|
// Tell it not to. The store must be done while holding the lock
|
|
// because the lock makes sure that there is not an active write
|
|
// this very moment.
|
|
w.writeContinueMu.Lock()
|
|
w.canWriteContinue.SetFalse()
|
|
w.writeContinueMu.Unlock()
|
|
}
|
|
|
|
if !w.wroteHeader {
|
|
w.WriteHeader(_http.StatusOK)
|
|
}
|
|
if lenData == 0 {
|
|
return 0, nil
|
|
}
|
|
/*if !w.bodyAllowed() {
|
|
return 0, _http.ErrBodyNotAllowed
|
|
}*/
|
|
|
|
w.written += int64(lenData) // ignoring errors, for errorKludge
|
|
if w.contentLength != -1 && w.written > w.contentLength {
|
|
return 0, _http.ErrContentLength
|
|
}
|
|
if dataB != nil {
|
|
return w.w.Write(dataB)
|
|
} else {
|
|
return w.w.WriteString(dataS)
|
|
}
|
|
}
|
|
|
|
// shouldReuseConnection reports whether the underlying TCP connection can be reused.
|
|
// It must only be called after the handler is done executing.
|
|
func (w *response) shouldReuseConnection() bool {
|
|
if w.closeAfterReply {
|
|
// The request or something set while executing the
|
|
// handler indicated we shouldn't reuse this
|
|
// connection.
|
|
return false
|
|
}
|
|
|
|
if w.req.Method != "HEAD" && w.contentLength != -1 && w.contentLength != w.written {
|
|
// Did not write enough. Avoid getting out of sync.
|
|
return false
|
|
}
|
|
|
|
// There was some error writing to the underlying connection
|
|
// during the request, so don't re-use this conn.
|
|
if w.conn.werr != nil {
|
|
return false
|
|
}
|
|
|
|
if w.closedRequestBodyEarly() {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (w *response) closedRequestBodyEarly() bool {
|
|
body, ok := w.req.Body.(*body)
|
|
return ok && body.didEarlyClose()
|
|
}
|
|
|
|
// rstAvoidanceDelay is the amount of time we sleep after closing the
|
|
// write side of a TCP connection before closing the entire socket.
|
|
// By sleeping, we increase the chances that the client sees our FIN
|
|
// and processes its final data before they process the subsequent RST
|
|
// from closing a connection with known unread data.
|
|
// This RST seems to occur mostly on BSD systems. (And Windows?)
|
|
// This timeout is somewhat arbitrary (~latency around the planet).
|
|
const rstAvoidanceDelay = 500 * time.Millisecond
|
|
|
|
type closeWriter interface {
|
|
CloseWrite() error
|
|
}
|
|
|
|
var _ closeWriter = (*net.TCPConn)(nil)
|
|
|
|
// closeWrite flushes any outstanding data and sends a FIN packet (if
|
|
// client is connected via TCP), signaling that we're done. We then
|
|
// pause for a bit, hoping the client processes it before any
|
|
// subsequent RST.
|
|
//
|
|
// See https://golang.org/issue/3595
|
|
func (c *conn) closeWriteAndWait() {
|
|
c.finalFlush()
|
|
if tcp, ok := c.rwc.(closeWriter); ok {
|
|
tcp.CloseWrite()
|
|
}
|
|
time.Sleep(rstAvoidanceDelay)
|
|
}
|