Skip to content

Instantly share code, notes, and snippets.

@KushalP
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KushalP/46d2651acae353ee8ee6 to your computer and use it in GitHub Desktop.
Save KushalP/46d2651acae353ee8ee6 to your computer and use it in GitHub Desktop.
This is a minimal reproducible test case to show that between go 1.1.2 and go 1.2 the error behaviour for HTTP requests with an incorrect Content-Length changed in a backwards-incompatible way.
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
hello := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World")
})
log.Fatal(http.ListenAndServe(":8081", hello))
}

Regression in net/http between Go 1.1.2 and Go 1.2

Between Go 1.1.2 and Go 1.2 the net/http package changed behaviour in how it handled bad Content-Length values in requests.

Link to issue: https://code.google.com/p/go/issues/detail?id=8003

Versions

Tested using go version go1.2.1 linux/amd64 and go version go1.1.2 linux/amd64. Also tested using go version devel +f8b50ad4cac4 Mon Apr 21 17:00:27 2014 -0700 + darwin/amd64.

Setup

In separate shells run go run backend.go and go run server.go.

Results

Go 1.1.2

$ ruby test_client.rb
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Date: Thu, 15 May 2014 12:49:39 GMT
Transfer-Encoding: chunked
0

Go 1.2.1

$ ruby test_client.rb
HTTP/1.1 500 Internal Server Error
Date: Thu, 15 May 2014 12:47:14 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8
package main
import (
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"regexp"
"strings"
"time"
)
type LoggingTransport struct {
transport *http.Transport
}
func (l *LoggingTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
log.Println("Request:", req)
resp, err = l.transport.RoundTrip(req)
if err != nil {
log.Println("Err:", err)
invalidContentLengthRegexp := regexp.MustCompile(`http: Request.ContentLength=\d+ with Body length \d+`)
if invalidContentLengthRegexp.MatchString(err.Error()) {
return &http.Response{
Body: ioutil.NopCloser(strings.NewReader("")),
StatusCode: 400, // HTTP: Bad Request
}, nil
}
return resp, err
}
return resp, err
}
func main() {
connectTimeout, headerTimeout := 1*time.Second, 1*time.Second
backendURL, _ := url.Parse("http://localhost:8081")
proxy := httputil.NewSingleHostReverseProxy(backendURL)
proxy.Transport = &LoggingTransport{&http.Transport{
Dial: func(network, address string) (net.Conn, error) {
log.Println("Dialling...", network, address, connectTimeout)
return net.DialTimeout(network, address, connectTimeout)
},
ResponseHeaderTimeout: headerTimeout,
}}
log.Fatal(http.ListenAndServe(":8080", proxy))
}
require 'socket'
require 'uri'
def raw_http_request(url, headers = {}, body = nil)
uri = URI.parse(url)
s = TCPSocket.new(uri.host, uri.port)
s.write("GET #{uri.request_uri} HTTP/1.1\r\n")
headers.each { |k, v| s.write("#{k}: #{v}\r\n") }
s.write("\r\n")
s.write(body) if body
s.close_write
headers, body, found_body = [], "", false
s.each_line do |line|
if !found_body and line.strip == ""
found_body = true
next
end
if found_body
body << line
else
headers << line.strip
end
end
[headers, body]
ensure
s.close if s
end
puts raw_http_request("http://localhost:8080/",
{"Host" => "localhost:8080", "Content-Length" => 2000},
"Short body")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment