func TestFakeSecureService(t *testing.T) { var resp, token []byte wg := new(sync.WaitGroup) server := NewFakeSecureService(wg, func(path string, status int, resp []byte) { }) defer server.Close() wg.Add(3) client := NewHTTPClient(server.URL, &HTTPClientConfig{Debug: true}) resp, _ = client.Get("/token") token = proto.Body(resp) // Right token resp, _ = client.Get("/secure?token=" + string(token)) if !bytes.Equal(proto.Status(resp), []byte("202")) { t.Error("Valid token should return status 202:", string(proto.Status(resp))) } // Wrong tokens forbidden resp, _ = client.Get("/secure?token=wrong") if !bytes.Equal(proto.Status(resp), []byte("403")) { t.Error("Wrong token should returns status 403:", string(proto.Status(resp))) } wg.Wait() }
func (p *ESPlugin) ResponseAnalyze(req, resp []byte, start, stop time.Time) { if len(resp) == 0 { // nil http response - skipped elasticsearch export for this request return } t := time.Now() rtt := p.RttDurationToMs(stop.Sub(start)) req = payloadBody(req) esResp := ESRequestResponse{ ReqURL: string(proto.Path(req)), ReqMethod: string(proto.Method(req)), ReqUserAgent: string(proto.Header(req, []byte("User-Agent"))), ReqAcceptLanguage: string(proto.Header(req, []byte("Accept-Language"))), ReqAccept: string(proto.Header(req, []byte("Accept"))), ReqAcceptEncoding: string(proto.Header(req, []byte("Accept-Encoding"))), ReqIfModifiedSince: string(proto.Header(req, []byte("If-Modified-Since"))), ReqConnection: string(proto.Header(req, []byte("Connection"))), ReqCookies: string(proto.Header(req, []byte("Cookie"))), RespStatus: string(proto.Status(resp)), RespStatusCode: string(proto.Status(resp)), RespProto: string(proto.Method(resp)), RespContentLength: string(proto.Header(resp, []byte("Content-Length"))), RespContentType: string(proto.Header(resp, []byte("Content-Type"))), RespTransferEncoding: string(proto.Header(resp, []byte("Transfer-Encoding"))), RespContentEncoding: string(proto.Header(resp, []byte("Content-Encoding"))), RespExpires: string(proto.Header(resp, []byte("Expires"))), RespCacheControl: string(proto.Header(resp, []byte("Cache-Control"))), RespVary: string(proto.Header(resp, []byte("Vary"))), RespSetCookie: string(proto.Header(resp, []byte("Set-Cookie"))), Rtt: rtt, Timestamp: t, } j, err := json.Marshal(&esResp) if err != nil { log.Println(err) } else { p.indexor.Index(p.Index, "RequestResponse", "", "", "", &t, j) } return }
func TestHTTPClientBasicAuth(t *testing.T) { wg := new(sync.WaitGroup) wg.Add(2) GETPayload := []byte("GET / HTTP/1.1\r\n\r\n") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { user, pass, _ := r.BasicAuth() if user != "user" || pass != "pass" { http.Error(w, "Unauthorized.", 401) wg.Done() return } wg.Done() })) defer server.Close() client := NewHTTPClient(server.URL, &HTTPClientConfig{Debug: false}) resp, _ := client.Send(GETPayload) client.Disconnect() if !bytes.Equal(proto.Status(resp), []byte("401")) { t.Error("Should return unauthorized error", string(resp)) } authUrl := strings.Replace(server.URL, "http://", "http://*****:*****@", -1) client = NewHTTPClient(authUrl, &HTTPClientConfig{Debug: false}) resp, _ = client.Send(GETPayload) client.Disconnect() if !bytes.Equal(proto.Status(resp), []byte("200")) { t.Error("Should return proper response", string(resp)) } wg.Wait() }
func (c *HTTPClient) Send(data []byte) (response []byte, err error) { // Don't exit on panic defer func() { if r := recover(); r != nil { Debug("[HTTPClient]", r, string(data)) if _, ok := r.(error); !ok { log.Println("[HTTPClient] Failed to send request: ", string(data)) log.Println("PANIC: pkg:", r, debug.Stack()) } } }() if c.conn == nil || !c.isAlive() { Debug("[HTTPClient] Connecting:", c.baseURL) if err = c.Connect(); err != nil { log.Println("[HTTPClient] Connection error:", err) response = errorPayload(HTTP_CONNECTION_ERROR) return } } timeout := time.Now().Add(c.config.Timeout) c.conn.SetWriteDeadline(timeout) if !c.config.OriginalHost { data = proto.SetHost(data, []byte(c.baseURL), []byte(c.host)) } if c.auth != "" { data = proto.SetHeader(data, []byte("Authorization"), []byte(c.auth)) } if c.config.Debug { Debug("[HTTPClient] Sending:", string(data)) } if _, err = c.conn.Write(data); err != nil { Debug("[HTTPClient] Write error:", err, c.baseURL) response = errorPayload(HTTP_TIMEOUT) return } var readBytes, n int var currentChunk []byte timeout = time.Now().Add(c.config.Timeout) chunked := false contentLength := -1 currentContentLength := 0 chunks := 0 for { c.conn.SetReadDeadline(timeout) if readBytes < len(c.respBuf) { n, err = c.conn.Read(c.respBuf[readBytes:]) readBytes += n chunks++ if err != nil { if err == io.EOF { err = nil } break } // First chunk if chunked || contentLength != -1 { currentContentLength += n } else { // If headers are finished if bytes.Contains(c.respBuf[:readBytes], proto.EmptyLine) { if bytes.Equal(proto.Header(c.respBuf, []byte("Transfer-Encoding")), []byte("chunked")) { chunked = true } else { status, _ := strconv.Atoi(string(proto.Status(c.respBuf))) if (status >= 100 && status < 200) || status == 204 || status == 304 { contentLength = 0 } else { l := proto.Header(c.respBuf, []byte("Content-Length")) if len(l) > 0 { contentLength, _ = strconv.Atoi(string(l)) } } } currentContentLength += len(proto.Body(c.respBuf[:readBytes])) } } if chunked { // Check if chunked message finished if bytes.HasSuffix(c.respBuf[:readBytes], chunkedSuffix) { break } } else if contentLength != -1 { if currentContentLength > contentLength { Debug("[HTTPClient] disconnected, wrong length", currentContentLength, contentLength) c.Disconnect() break } else if currentContentLength == contentLength { break } } } else { if currentChunk == nil { currentChunk = make([]byte, readChunkSize) } n, err = c.conn.Read(currentChunk) if err == io.EOF { break } else if err != nil { Debug("[HTTPClient] Read the whole body error:", err, c.baseURL) break } readBytes += int(n) chunks++ currentContentLength += n if chunked { // Check if chunked message finished if bytes.HasSuffix(currentChunk[:n], chunkedSuffix) { break } } else if contentLength != -1 { if currentContentLength > contentLength { Debug("[HTTPClient] disconnected, wrong length", currentContentLength, contentLength) c.Disconnect() break } else if currentContentLength == contentLength { break } } else { Debug("[HTTPClient] disconnected, can't find Content-Length or Chunked") c.Disconnect() break } } if readBytes >= maxResponseSize { Debug("[HTTPClient] Body is more than the max size", maxResponseSize, c.baseURL) break } // For following chunks expect less timeout timeout = time.Now().Add(c.config.Timeout / 5) } if err != nil { Debug("[HTTPClient] Response read error", err, c.conn, readBytes) response = errorPayload(HTTP_TIMEOUT) return } if readBytes > len(c.respBuf) { readBytes = len(c.respBuf) } payload := make([]byte, readBytes) copy(payload, c.respBuf[:readBytes]) if c.config.Debug { Debug("[HTTPClient] Received:", string(payload)) } if c.config.FollowRedirects > 0 && c.redirectsCount < c.config.FollowRedirects { status := payload[9:12] // 3xx requests if status[0] == '3' { c.redirectsCount++ location := proto.Header(payload, []byte("Location")) redirectPayload := []byte("GET " + string(location) + " HTTP/1.1\r\n\r\n") if c.config.Debug { Debug("[HTTPClient] Redirecting to: " + string(location)) } return c.Send(redirectPayload) } } if bytes.Equal(proto.Status(payload), []byte("400")) { c.Disconnect() Debug("[HTTPClient] Closed connection on 400 response") } c.redirectsCount = 0 return payload, err }
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", "127.0.0.1:0") client = NewHTTPClient("http://"+ln.Addr().String(), &HTTPClientConfig{Debug: true, Timeout: 10 * time.Millisecond}) defer ln.Close() 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", "127.0.0.1:0") go func() { ln1.Accept() }() defer ln1.Close() 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") } }
func TestTokenMiddleware(t *testing.T) { var resp, token []byte wg := new(sync.WaitGroup) from := NewFakeSecureService(wg, func(path string, status int, tok []byte) { time.Sleep(10 * time.Millisecond) }) defer from.Close() to := NewFakeSecureService(wg, func(path string, status int, tok []byte) { switch path { case "/secure": if status != 202 { t.Error("Server should receive valid rewritten token") } } time.Sleep(10 * time.Millisecond) }) defer to.Close() quit := make(chan int) Settings.middleware = "go run ./examples/middleware/token_modifier.go" fromAddr := strings.Replace(from.Listener.Addr().String(), "[::]", "127.0.0.1", -1) // Catch traffic from one service input := NewRAWInput(fromAddr, testRawExpire) defer input.Close() // And redirect to another output := NewHTTPOutput(to.URL, &HTTPOutputConfig{Debug: true}) Plugins.Inputs = []io.Reader{input} Plugins.Outputs = []io.Writer{output} // Start Gor go Start(quit) // Wait for middleware to initialize // Give go compiller time to build programm time.Sleep(500 * time.Millisecond) // Should receive 2 requests from original + 2 from replayed wg.Add(4) client := NewHTTPClient(from.URL, &HTTPClientConfig{Debug: false}) // Sending traffic to original service resp, _ = client.Get("/token") token = proto.Body(resp) // When delay is too smal, middleware does not always rewrite requests in time // Hopefuly client will have delay more then 10ms :) time.Sleep(10 * time.Millisecond) resp, _ = client.Get("/secure?token=" + string(token)) if !bytes.Equal(proto.Status(resp), []byte("202")) { t.Error("Valid token should return 202:", proto.Status(resp)) } wg.Wait() close(quit) time.Sleep(100 * time.Millisecond) Settings.middleware = "" }