123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- // +build !windows
- package hack
- import "net"
- // MalformedHostHeaderOverride is a wrapper to be able
- // to overcome the 400 Bad request coming from old docker
- // clients that send an invalid Host header.
- type MalformedHostHeaderOverride struct {
- net.Listener
- }
- // MalformedHostHeaderOverrideConn wraps the underlying unix
- // connection and keeps track of the first read from http.Server
- // which just reads the headers.
- type MalformedHostHeaderOverrideConn struct {
- net.Conn
- first bool
- }
- var closeConnHeader = []byte("\r\nConnection: close\r")
- // Read reads the first *read* request from http.Server to inspect
- // the Host header. If the Host starts with / then we're talking to
- // an old docker client which send an invalid Host header. To not
- // error out in http.Server we rewrite the first bytes of the request
- // to sanitize the Host header itself.
- // In case we're not dealing with old docker clients the data is just passed
- // to the server w/o modification.
- func (l *MalformedHostHeaderOverrideConn) Read(b []byte) (n int, err error) {
- // http.Server uses a 4k buffer
- if l.first && len(b) == 4096 {
- // This keeps track of the first read from http.Server which just reads
- // the headers
- l.first = false
- // The first read of the connection by http.Server is done limited to
- // DefaultMaxHeaderBytes (usually 1 << 20) + 4096.
- // Here we do the first read which gets us all the http headers to
- // be inspected and modified below.
- c, err := l.Conn.Read(b)
- if err != nil {
- return c, err
- }
- var (
- start, end int
- firstLineFeed = -1
- buf []byte
- )
- for i := 0; i <= c-1-7; i++ {
- if b[i] == '\n' && firstLineFeed == -1 {
- firstLineFeed = i
- }
- if b[i] != '\n' {
- continue
- }
- if b[i+1] == '\r' && b[i+2] == '\n' {
- return c, nil
- }
- if b[i+1] != 'H' {
- continue
- }
- if b[i+2] != 'o' {
- continue
- }
- if b[i+3] != 's' {
- continue
- }
- if b[i+4] != 't' {
- continue
- }
- if b[i+5] != ':' {
- continue
- }
- if b[i+6] != ' ' {
- continue
- }
- if b[i+7] != '/' {
- continue
- }
- // ensure clients other than the docker clients do not get this hack
- if i != firstLineFeed {
- return c, nil
- }
- start = i + 7
- // now find where the value ends
- for ii, bbb := range b[start:c] {
- if bbb == '\n' {
- end = start + ii
- break
- }
- }
- buf = make([]byte, 0, c+len(closeConnHeader)-(end-start))
- // strip the value of the host header and
- // inject `Connection: close` to ensure we don't reuse this connection
- buf = append(buf, b[:start]...)
- buf = append(buf, closeConnHeader...)
- buf = append(buf, b[end:c]...)
- copy(b, buf)
- break
- }
- if len(buf) == 0 {
- return c, nil
- }
- return len(buf), nil
- }
- return l.Conn.Read(b)
- }
- // Accept makes the listener accepts connections and wraps the connection
- // in a MalformedHostHeaderOverrideConn initilizing first to true.
- func (l *MalformedHostHeaderOverride) Accept() (net.Conn, error) {
- c, err := l.Listener.Accept()
- if err != nil {
- return c, err
- }
- return &MalformedHostHeaderOverrideConn{c, true}, nil
- }
|