Skip to content

Commit

Permalink
only set read limit for http redirect listener when request is http
Browse files Browse the repository at this point in the history
  • Loading branch information
WeidiDeng committed Oct 26, 2023
1 parent f71d779 commit 5ff9cd1
Showing 1 changed file with 28 additions and 20 deletions.
48 changes: 28 additions & 20 deletions modules/caddyhttp/httpredirectlistener.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ package caddyhttp

import (
"bufio"
"bytes"
"fmt"
"io"
"net"
"net/http"
"sync"

"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
Expand Down Expand Up @@ -86,39 +86,54 @@ func (l *httpRedirectListener) Accept() (net.Conn, error) {
}

return &httpRedirectConn{
Conn: c,
r: bufio.NewReader(io.LimitReader(c, maxHeaderBytes)),
Conn: c,
limit: maxHeaderBytes,
r: bufio.NewReader(c),
}, nil
}

type httpRedirectConn struct {
net.Conn
once sync.Once
r *bufio.Reader
once bool
limit int64
r *bufio.Reader
}

// Read tries to peek at the first few bytes of the request, and if we get
// an error reading the headers, and that error was due to the bytes looking
// like an HTTP request, then we perform a HTTP->HTTPS redirect on the same
// port as the original connection.
func (c *httpRedirectConn) Read(p []byte) (int, error) {
var errReturn error
c.once.Do(func() {
// no need to use sync.Once - net.Conn is not read from concurrently.
if !c.once {
c.once = true

firstBytes, err := c.r.Peek(5)
if err != nil {
return
return 0, err
}

// If the request doesn't look like HTTP, then it's probably
// TLS bytes and we don't need to do anything.
// TLS bytes, and we don't need to do anything.
// 0, nil are valid return values - caller most likely will try again.
if !firstBytesLookLikeHTTP(firstBytes) {
return
return 0, nil
}

// From now on, we can be almost certain the request is HTTP.
// The returned error will be non nil and caller are expected to
// close the connection.

// Set the read limit, io.MultiReader is needed because
// when resetting, *bufio.Reader discards buffered data.
buffered, _ := c.r.Peek(c.r.Buffered())
mr := io.MultiReader(bytes.NewReader(buffered), c.Conn)
c.r.Reset(io.LimitReader(mr, c.limit))

// Parse the HTTP request, so we can get the Host and URL to redirect to.
req, err := http.ReadRequest(c.r)
if err != nil {
return
return 0, fmt.Errorf("couldn't read HTTP request")
}

// Build the redirect response, using the same Host and URL,
Expand All @@ -136,18 +151,11 @@ func (c *httpRedirectConn) Read(p []byte) (int, error) {

err = resp.Write(c.Conn)
if err != nil {
errReturn = fmt.Errorf("couldn't write HTTP->HTTPS redirect")
return
return 0, fmt.Errorf("couldn't write HTTP->HTTPS redirect")
}

errReturn = fmt.Errorf("redirected HTTP request on HTTPS port")
c.Conn.Close()
})

if errReturn != nil {
return 0, errReturn
return 0, fmt.Errorf("redirected HTTP request on HTTPS port")
}

return c.r.Read(p)
}

Expand Down

0 comments on commit 5ff9cd1

Please sign in to comment.