1) We cannot send "Connection: close", because the fsouza docker client expects the tcp socket to stay open between requests. 2) Because we cannot force-close the connection, we can't hijack the connection (because go's net/http doesn't let use un-hijack it). 3) Because we need to maintain the individual chunking of messages (for docker-py), we can't just copy the response body, as Go will remove and re-add the chunking willy-nilly. Therefore, we have to read each chunk one-by-one, and flush the ResponseWriter after each one.
137 lines
3.0 KiB
Go
137 lines
3.0 KiB
Go
// Based on net/http/internal
|
|
package proxy
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestChunk(t *testing.T) {
|
|
r := bufio.NewScanner(bytes.NewBufferString(
|
|
"7\r\nhello, \r\n17\r\nworld! 0123456789abcdef\r\n0\r\n",
|
|
))
|
|
r.Split(splitChunks)
|
|
|
|
assertNextChunk(t, r, "hello, ")
|
|
assertNextChunk(t, r, "world! 0123456789abcdef")
|
|
assertNoMoreChunks(t, r)
|
|
}
|
|
|
|
func TestMalformedChunks(t *testing.T) {
|
|
r := bufio.NewScanner(bytes.NewBufferString(
|
|
"7\r\nhello, GARBAGEBYTES17\r\nworld! 0123456789abcdef\r\n0\r\n",
|
|
))
|
|
r.Split(splitChunks)
|
|
|
|
// First chunk fails
|
|
{
|
|
if r.Scan() {
|
|
t.Errorf("Expected failure when reading chunks, but got one")
|
|
}
|
|
e := "malformed chunked encoding"
|
|
if r.Err() == nil || r.Err().Error() != e {
|
|
t.Errorf("chunk reader errored %q; want %q", r.Err(), e)
|
|
}
|
|
data := r.Bytes()
|
|
if len(data) != 0 {
|
|
t.Errorf("chunk should have been empty. got %q", data)
|
|
}
|
|
}
|
|
|
|
if r.Scan() {
|
|
t.Errorf("Expected no more chunks, but found too many")
|
|
}
|
|
}
|
|
|
|
func TestChunkTooLarge(t *testing.T) {
|
|
data := make([]byte, maxChunkSize+1)
|
|
r := bufio.NewScanner(bytes.NewBufferString(strings.Join(
|
|
[]string{
|
|
strconv.FormatInt(maxChunkSize+1, 16), string(data),
|
|
"0", "",
|
|
},
|
|
"\r\n",
|
|
)))
|
|
r.Split(splitChunks)
|
|
|
|
// First chunk fails
|
|
{
|
|
if r.Scan() {
|
|
t.Errorf("Expected failure when reading chunks, but got one")
|
|
}
|
|
e := "chunk too long"
|
|
if r.Err() == nil || r.Err().Error() != e {
|
|
t.Errorf("chunk reader errored %q; want %q", r.Err(), e)
|
|
}
|
|
data := r.Bytes()
|
|
if len(data) != 0 {
|
|
t.Errorf("chunk should have been empty. got %q", data)
|
|
}
|
|
}
|
|
|
|
if r.Scan() {
|
|
t.Errorf("Expected no more chunks, but found too many")
|
|
}
|
|
}
|
|
|
|
func TestInvalidChunkSize(t *testing.T) {
|
|
r := bufio.NewScanner(bytes.NewBufferString(
|
|
"foobar\r\nhello, \r\n0\r\n",
|
|
))
|
|
r.Split(splitChunks)
|
|
|
|
// First chunk fails
|
|
{
|
|
if r.Scan() {
|
|
t.Errorf("Expected failure when reading chunks, but got one")
|
|
}
|
|
e := "invalid byte in chunk length"
|
|
if r.Err() == nil || r.Err().Error() != e {
|
|
t.Errorf("chunk reader errored %q; want %q", r.Err(), e)
|
|
}
|
|
data := r.Bytes()
|
|
if len(data) != 0 {
|
|
t.Errorf("chunk should have been empty. got %q", data)
|
|
}
|
|
}
|
|
|
|
if r.Scan() {
|
|
t.Errorf("Expected no more chunks, but found too many")
|
|
}
|
|
}
|
|
|
|
func TestBytesAfterLastChunkAreIgnored(t *testing.T) {
|
|
r := bufio.NewScanner(bytes.NewBufferString(
|
|
"7\r\nhello, \r\n0\r\nGARBAGEBYTES",
|
|
))
|
|
r.Split(splitChunks)
|
|
|
|
assertNextChunk(t, r, "hello, ")
|
|
assertNoMoreChunks(t, r)
|
|
}
|
|
|
|
func assertNextChunk(t *testing.T, r *bufio.Scanner, expected string) {
|
|
if !r.Scan() {
|
|
t.Fatalf("Expected chunk, but ran out early: %v", r.Err())
|
|
}
|
|
if r.Err() != nil {
|
|
t.Fatalf("Error reading chunk: %q", r.Err())
|
|
}
|
|
data := r.Bytes()
|
|
if string(data) != expected {
|
|
t.Errorf("chunk reader read %q; want %q", data, expected)
|
|
}
|
|
}
|
|
|
|
func assertNoMoreChunks(t *testing.T, r *bufio.Scanner) {
|
|
if r.Scan() {
|
|
t.Errorf("Expected no more chunks, but found too many")
|
|
}
|
|
if r.Err() != nil {
|
|
t.Errorf("Expected no error, but found: %q", r.Err())
|
|
}
|
|
}
|