Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/crypto/tls/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,24 @@ type Config struct {
// autoSessionTicketKeys is like sessionTicketKeys but is owned by the
// auto-rotation logic. See Config.ticketKeys.
autoSessionTicketKeys []ticketKey

// HttpOnHttpsPortErrorResponseString is sent on an HTTPS connection
// which receives what looks like an HTTP request.
// "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\nClient sent an HTTP request to an HTTPS server.\n"
HttpOnHttpsPortErrorResponse string

// HttpOnHttpsPortErrorRedirect is sent on an HTTPS connection
// which receives what looks like an HTTP request.
// If true, 307 redirect will be sent
HttpOnHttpsPortErrorRedirect bool

// HttpOnHttpsPortErrorHandler handles HTTP requests sent to an HTTPS port.
//
// WriteString use:
// io.WriteString(conn, "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\nClient sent an HTTP request to an HTTPS server.\n")
// Parse the request header use:
// http.ReadRequestForHttpOnHttpsPortErrorHandler(conn, recondBytes)
HttpOnHttpsPortErrorHandler func(conn net.Conn, recondBytes []byte, badRequestResponse string)
}

const (
Expand Down
6 changes: 5 additions & 1 deletion src/crypto/tls/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,9 @@ type RecordHeaderError struct {
// RecordHeader contains the five bytes of TLS record header that
// triggered the error.
RecordHeader [5]byte
// RecondBytes contains all the bytes of the TLS record header that
// triggered the error.
RecondBytes []byte
// Conn provides the underlying net.Conn in the case that a client
// sent an initial handshake that didn't look like TLS.
// It is nil if there's already been a handshake or a TLS alert has
Expand All @@ -580,7 +583,8 @@ func (e RecordHeaderError) Error() string { return "tls: " + e.Msg }
func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err RecordHeaderError) {
err.Msg = msg
err.Conn = conn
copy(err.RecordHeader[:], c.rawInput.Bytes())
err.RecondBytes = c.rawInput.Bytes()
copy(err.RecordHeader[:], err.RecondBytes)
return err
}

Expand Down
67 changes: 66 additions & 1 deletion src/net/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1923,7 +1923,7 @@ func (c *conn) serve(ctx context.Context) {
// TLS, assume they're speaking plaintext HTTP and write a
// 400 response on the TLS conn's underlying net.Conn.
if re, ok := err.(tls.RecordHeaderError); ok && re.Conn != nil && tlsRecordHeaderLooksLikeHTTP(re.RecordHeader) {
io.WriteString(re.Conn, "HTTP/1.0 400 Bad Request\r\n\r\nClient sent an HTTP request to an HTTPS server.\n")
c.httpOnHttpsPortErrorHandler(re.Conn, re.RecondBytes)
re.Conn.Close()
return
}
Expand Down Expand Up @@ -3841,3 +3841,68 @@ func MaxBytesHandler(h Handler, n int64) Handler {
h.ServeHTTP(w, &r2)
})
}

// net/http: configurable error message for Client sent an HTTP request to an HTTPS server.
// https://go.dev/issue/49310
func (c *conn) httpOnHttpsPortErrorHandler(conn net.Conn, recondBytes []byte) {
// Read Response string
badRequestResponse := c.server.TLSConfig.HttpOnHttpsPortErrorResponse
if badRequestResponse == "" {
badRequestResponse = "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\nClient sent an HTTP request to an HTTPS server.\n"
}

// Handler
if handler := c.server.TLSConfig.HttpOnHttpsPortErrorHandler; handler != nil {
handler(conn, recondBytes, badRequestResponse)
return
}

// Redirect
if c.server.TLSConfig.HttpOnHttpsPortErrorRedirect {
// Read Header
req, _, err := ReadRequestForHttpOnHttpsPortErrorHandler(conn, recondBytes)
if err != nil {
io.WriteString(conn, badRequestResponse)
return
}

// Send Redirect
io.WriteString(conn, fmt.Sprintf(
"HTTP/1.1 307 Temporary Redirect\r\nLocation: https://%s%s\r\nConnection: close\r\n\r\nClient sent an HTTP request to an HTTPS server.\n",
req.Host, req.URL.Path,
))
return
}

// Send Response string
io.WriteString(conn, badRequestResponse)
}

func ReadRequestForHttpOnHttpsPortErrorHandler(conn net.Conn, recondBytes []byte) (req *Request, reqBytes []byte, err error) {
// Max 4KB
if len(recondBytes) > 4096 {
return nil, recondBytes, errors.New("recondBytes too long")
}

// Content length may be insufficient, continue reading.
if len(recondBytes) < 4096 && !bytes.Contains(recondBytes, []byte("\r\n\r\n")) {
b := make([]byte, 4096-len(recondBytes))
// Set Timeout 1s
conn.SetReadDeadline(time.Now().Add(time.Duration(1 * time.Second)))
n, err := conn.Read(b)
if err != nil {
return nil, recondBytes, err
}
recondBytes = append(recondBytes, b[:n]...)
}

// Read Request
req, err = ReadRequest(bufio.NewReader(bytes.NewReader(recondBytes)))
if err != nil {
return nil, recondBytes, err
}
if req.Host == "" {
return nil, recondBytes, errors.New("missing required Host header")
}
return req, recondBytes, nil
}