From 07c65117ade7a9a137856617fd78d4732700a72a Mon Sep 17 00:00:00 2001 From: Leonid Bugaev Date: Wed, 8 Jul 2015 18:26:14 +0500 Subject: [PATCH] Handle proxy request format and HTTP 1.0 requests --- http_client.go | 24 +++++++++++++++--------- http_client_test.go | 40 +++++++++++++++++++++++++++++++--------- proto/proto.go | 18 ++++++++++++++++++ proto/proto_test.go | 11 +++++++++++ 4 files changed, 75 insertions(+), 18 deletions(-) diff --git a/http_client.go b/http_client.go index 3d8d5bf..50237ec 100644 --- a/http_client.go +++ b/http_client.go @@ -21,7 +21,9 @@ type HTTPClientConfig struct { } type HTTPClient struct { - baseURL *url.URL + baseURL string + scheme string + host string conn net.Conn respBuf []byte config *HTTPClientConfig @@ -33,24 +35,28 @@ func NewHTTPClient(baseURL string, config *HTTPClientConfig) *HTTPClient { baseURL = "http://" + baseURL } + u, _ := url.Parse(baseURL) + if !strings.Contains(u.Host, ":") { + u.Host += ":" + defaultPorts[u.Scheme] + } + + client := new(HTTPClient) - client.baseURL, _ = url.Parse(baseURL) + client.baseURL = u.String() + client.host = u.Host + client.scheme = u.Scheme client.respBuf = make([]byte, 4096*10) client.config = config - if !strings.Contains(client.baseURL.Host, ":") { - client.baseURL.Host += ":" + defaultPorts[client.baseURL.Scheme] - } - return client } func (c *HTTPClient) Connect() (err error) { c.Disconnect() - c.conn, err = net.Dial("tcp", c.baseURL.Host) + c.conn, err = net.Dial("tcp", c.host) - if c.baseURL.Scheme == "https" { + if c.scheme == "https" { tlsConn := tls.Client(c.conn, &tls.Config{InsecureSkipVerify: true}) if err = tlsConn.Handshake(); err != nil { @@ -93,7 +99,7 @@ func (c *HTTPClient) Send(data []byte) (response []byte, err error) { c.conn.SetWriteDeadline(timeout) - data = proto.SetHeader(data, []byte("Host"), []byte(c.baseURL.Host)) + data = proto.SetHost(data, []byte(c.baseURL), []byte(c.host)) if c.config.Debug { Debug("Sending:", string(data)) diff --git a/http_client_test.go b/http_client_test.go index b22eabd..a5b238b 100644 --- a/http_client_test.go +++ b/http_client_test.go @@ -13,23 +13,23 @@ import ( func TestHTTPClientURLPort(t *testing.T) { c1 := NewHTTPClient("http://example.com", &HTTPClientConfig{}) - if c1.baseURL.String() != "http://example.com:80" { - t.Error("Sould add 80 port for http:", c1.baseURL.String()) + if c1.baseURL != "http://example.com:80" { + t.Error("Sould add 80 port for http:", c1.baseURL) } c2 := NewHTTPClient("https://example.com", &HTTPClientConfig{}) - if c2.baseURL.String() != "https://example.com:443" { - t.Error("Sould add 443 port for https:", c2.baseURL.String()) + if c2.baseURL != "https://example.com:443" { + t.Error("Sould add 443 port for https:", c2.baseURL) } c3 := NewHTTPClient("https://example.com:1", &HTTPClientConfig{}) - if c3.baseURL.String() != "https://example.com:1" { - t.Error("Sould use specified port:", c3.baseURL.String()) + if c3.baseURL != "https://example.com:1" { + t.Error("Sould use specified port:", c3.baseURL) } c4 := NewHTTPClient("example.com", &HTTPClientConfig{}) - if c4.baseURL.String() != "http://example.com:80" { - t.Error("Sould add default protocol:", c4.baseURL.String()) + if c4.baseURL != "http://example.com:80" { + t.Error("Sould add default protocol:", c4.baseURL) } } @@ -70,7 +70,7 @@ func TestHTTPClientSend(t *testing.T) { wg.Done() })) - client := NewHTTPClient(server.URL, &HTTPClientConfig{Debug: false}) + client := NewHTTPClient(server.URL, &HTTPClientConfig{Debug: true}) wg.Add(4) client.Send(payload("POST")) @@ -245,3 +245,25 @@ func TestHTTPClientRedirectLimit(t *testing.T) { wg.Wait() } + +func TestHTTPClientHandleHTTP10(t *testing.T) { + wg := new(sync.WaitGroup) + + GET_payload := []byte("GET http://foobar.com/path HTTP/1.0\r\n\r\n") + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + + if r.URL.Path != "/path" { + t.Error("Path not match:", r.URL.Path) + } + + wg.Done() + })) + + client := NewHTTPClient(server.URL, &HTTPClientConfig{Debug: true}) + + wg.Add(1) + client.Send(GET_payload) + + wg.Wait() +} diff --git a/proto/proto.go b/proto/proto.go index 07622db..330115b 100644 --- a/proto/proto.go +++ b/proto/proto.go @@ -86,6 +86,24 @@ func SetPath(payload, path []byte) []byte { return byteutils.Replace(payload, start, start+end, path) } +func SetHost(payload, url, host []byte) []byte { + // If this is HTTP 1.0 traffic or proxy traffic it may include host right into path variable, so instead of setting Host header we rewrite Path + // Fix for https://github.com/buger/gor/issues/156 + if path := Path(payload); bytes.HasPrefix(path, []byte("http")) { + hostStart := bytes.IndexByte(path, ':') // : position "https?:" + hostStart += 3 // Skip 1 ':' and 2 '\' + hostEnd := hostStart + bytes.IndexByte(path[hostStart:], '/') + + newPath := make([]byte, len(path)) + copy(newPath, path) + newPath = byteutils.Replace(newPath, 0, hostEnd, url) + + return SetPath(payload, newPath) + } else { + return SetHeader(payload, []byte("Host"), host) + } +} + func Method(payload []byte) []byte { end := bytes.IndexByte(payload, ' ') diff --git a/proto/proto_test.go b/proto/proto_test.go index bc7dcaf..3ada427 100644 --- a/proto/proto_test.go +++ b/proto/proto_test.go @@ -87,4 +87,15 @@ func TestSetPath(t *testing.T) { if payload = SetPath(payload, []byte("/new_path")); !bytes.Equal(payload, payload_after) { t.Error("Should replace path", string(payload)) } +} + +func TestSetHostHTTP10(t *testing.T) { + var payload, payload_after []byte + + payload = []byte("POST http://example.com/post HTTP/1.0\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + payload_after = []byte("POST http://new.com/post HTTP/1.0\r\nContent-Length: 7\r\nHost: www.w3.org\r\n\r\na=1&b=2") + + if payload = SetHost(payload, []byte("http://new.com"), []byte("new.com")); !bytes.Equal(payload, payload_after) { + t.Error("Should replace host", string(payload)) + } } \ No newline at end of file