Handle HTTP error codes

This commit is contained in:
Leonid Bugaev
2015-08-17 20:36:59 +03:00
parent 69424bf670
commit d96e3393ec
2 changed files with 122 additions and 2 deletions
+31 -2
View File
@@ -21,6 +21,7 @@ type HTTPClientConfig struct {
FollowRedirects int
Debug bool
OriginalHost bool
ConnectionTimeout time.Duration
Timeout time.Duration
ResponseBufferSize int
}
@@ -69,9 +70,9 @@ func (c *HTTPClient) Connect() (err error) {
c.Disconnect()
if !strings.Contains(c.host, ":") {
c.conn, err = net.Dial("tcp", c.host+":80")
c.conn, err = net.DialTimeout("tcp", c.host+":80", c.config.ConnectionTimeout)
} else {
c.conn, err = net.Dial("tcp", c.host)
c.conn, err = net.DialTimeout("tcp", c.host, c.config.ConnectionTimeout)
}
if c.scheme == "https" {
@@ -124,6 +125,7 @@ func (c *HTTPClient) Send(data []byte) (response []byte, err error) {
Debug("[HTTPClient] Connecting:", c.baseURL)
if err = c.Connect(); err != nil {
log.Println("[HTTPClient] Connection error:", err)
response = errorPayload(HTTP_CONNECTION_ERROR)
return
}
}
@@ -142,6 +144,7 @@ func (c *HTTPClient) Send(data []byte) (response []byte, err error) {
if _, err = c.conn.Write(data); err != nil {
Debug("[HTTPClient] Write error:", err, c.baseURL)
response = errorPayload(HTTP_TIMEOUT)
return
}
@@ -160,6 +163,7 @@ func (c *HTTPClient) Send(data []byte) (response []byte, err error) {
if err != nil {
Debug("[HTTPClient] Response read error", err, c.conn)
response = errorPayload(HTTP_TIMEOUT)
return
}
@@ -197,3 +201,28 @@ func (c *HTTPClient) Get(path string) (response []byte, err error) {
return c.Send([]byte(payload))
}
const (
// https://support.cloudflare.com/hc/en-us/articles/200171936-Error-520-Web-server-is-returning-an-unknown-error
HTTP_UNKNOWN_ERROR = "520"
// https://support.cloudflare.com/hc/en-us/articles/200171916-Error-521-Web-server-is-down
HTTP_CONNECTION_ERROR = "521"
// https://support.cloudflare.com/hc/en-us/articles/200171906-Error-522-Connection-timed-out
HTTP_CONNECTION_TIMEOUT = "522"
// https://support.cloudflare.com/hc/en-us/articles/200171946-Error-523-Origin-is-unreachable
HTTP_UNREACHABLE = "523"
// https://support.cloudflare.com/hc/en-us/articles/200171926-Error-524-A-timeout-occurred
HTTP_TIMEOUT = "524"
)
var errorPayloadTemplate = "HTTP/1.1 202 Accepted\r\nDate: Mon, 17 Aug 2015 14:10:11 GMT\r\nContent-Length: 0\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n"
func errorPayload(errorCode string) []byte {
payload := make([]byte, len(errorPayloadTemplate))
copy(payload, errorPayloadTemplate)
copy(payload[29:58], []byte(time.Now().Format(time.RFC1123)))
copy(payload[9:12], errorCode)
return payload
}
+91
View File
@@ -8,7 +8,11 @@ import (
"net/http"
"net/http/httptest"
"net/http/httputil"
"github.com/buger/gor/proto"
"sync"
"time"
"log"
_ "reflect"
"testing"
_ "time"
)
@@ -299,3 +303,90 @@ func TestHTTPClientHandleHTTP10(t *testing.T) {
wg.Wait()
}
func TestHTTPClientErrors(t *testing.T) {
req := []byte("GET http://foobar.com/path HTTP/1.0\r\n\r\n")
// Port not exists
client := NewHTTPClient("http://127.0.0.1:1", &HTTPClientConfig{Debug: true})
if resp, err := client.Send(req); err != nil {
if s := proto.Status(resp); !bytes.Equal(s, []byte("521")) {
t.Error("Should return status 521 for connection refused, instead:", string(s))
}
} else {
t.Error("Should throw error")
}
client = NewHTTPClient("http://not.existing", &HTTPClientConfig{Debug: true})
if resp, err := client.Send(req); err != nil {
if s := proto.Status(resp); !bytes.Equal(s, []byte("521")) {
t.Error("Should return status 521 for no such host, instead:", string(s))
}
} else {
t.Error("Should throw error")
}
// Non routable IP address to simulate connection timeout
client = NewHTTPClient("http://10.255.255.1", &HTTPClientConfig{Debug: true, ConnectionTimeout: 100 * time.Millisecond })
if resp, err := client.Send(req); err != nil {
if s := proto.Status(resp); !bytes.Equal(s, []byte("521")) {
t.Error("Should return status 521 for io/timeout:", string(s))
}
} else {
t.Error("Should throw error")
}
// Connecting but io timeout on read
ln, _ := net.Listen("tcp", ":0")
client = NewHTTPClient("http://" + ln.Addr().String(), &HTTPClientConfig{Debug: true, Timeout: 10 * time.Millisecond})
if resp, err := client.Send(req); err != nil {
if s := proto.Status(resp); !bytes.Equal(s, []byte("524")) {
t.Error("Should return status 524 for io read, instead:", string(s))
}
} else {
t.Error("Should throw error")
}
// Response read error read tcp [::1]:51128: connection reset by peer &{{0xc20802a000}}
ln1, _ := net.Listen("tcp", ":0")
go func(){
ln1.Accept()
}()
client = NewHTTPClient("http://" + ln1.Addr().String(), &HTTPClientConfig{Debug: true, Timeout: 10 * time.Millisecond})
if resp, err := client.Send(req); err != nil {
if s := proto.Status(resp); !bytes.Equal(s, []byte("524")) {
t.Error("Should return status 524 for connection reset by peer, instead:", string(s))
}
} else {
t.Error("Should throw error")
}
ln2, _ := net.Listen("tcp", ":0")
go func(){
for {
buf := make([]byte, 64*1024)
conn, err := ln2.Accept()
if err != nil {
log.Println("Error while Accept()", err)
continue
}
conn.Read(buf)
}
}()
client = NewHTTPClient("http://" + ln2.Addr().String(), &HTTPClientConfig{Debug: true, Timeout: 10 * time.Millisecond})
if resp, err := client.Send(req); err != nil {
if s := proto.Status(resp); !bytes.Equal(s, []byte("524")) {
t.Error("Should return status 524 for connection reset by peer, instead:", string(s))
}
} else {
t.Error("Should throw error")
}
}