func BenchmarkStringBody(b *testing.B) { b.StopTimer() expected := []byte("test=123456&test2=987654&test3=somedatanstuff&test4=moredataontheend") expLen := int64(len(expected)) sbf := NewStringBodyFilter() for i := 0; i < b.N; i++ { tmp, _ := http.NewRequest("POST", "/hello", bytes.NewReader(expected)) tmp.Header.Set("Content-Type", "application/x-www-form-urlencoded") tmp.ContentLength = expLen b.StartTimer() // replace the body req, _ := falcore.TestWithRequest(tmp, sbf, nil) sbf.ReturnBuffer(req) // read the body twice /* nah, this isn't so useful io.CopyN(ioutil.Discard, req.HttpRequest.Body, req.HttpRequest.ContentLength) req.HttpRequest.Body .Close() io.CopyN(ioutil.Discard, req.HttpRequest.Body, req.HttpRequest.ContentLength) req.HttpRequest.Body .Close() */ b.StopTimer() } }
// TestCookiemaskMasking verifies that a Falcore RequestFilter generated by // NewCookiemaskFilter behaves as expected when given a request that contains // a cookie that is supposed to be masked. Specifically, this function verifies // that no error occurred, that the submitted cookie was not forwarded to the // next RequestFilter in the chain, that the next RequestFilter in the chain did // indeed receive an indication that a maskable cookie was found, and that no // new maskable cookie of the expected variant is being set. func TestCookiemaskMasking(t *testing.T) { /* setup */ cookieRegex := regexp.MustCompile( "^test-[0-9A-Fa-f]+=[0-9A-Fa-f]*[1-9A-Fa-f][0-9A-Fa-f]+", ) request, err := http.NewRequest("GET", "/", nil) assert.NoError(t, err) handler := NewMinSessionHandler("test", "/", "example.com") sesh, err := handler.GetSession() assert.NoError(t, err) fwd, ckes, err := sesh.CookieMask(nil) assert.NoError(t, err) assert.Empty(t, fwd) cookie := ckes[0] request.AddCookie(cookie) /* run */ filter := CookiemaskFilter{ handler, cookieMaskTestPage, errorPage, } _, response := falcore.TestWithRequest( request, &filter, nil, ) /* check */ assert.Equal(t, 200, response.StatusCode) contents, err := ioutil.ReadAll(response.Body) assert.NoError(t, err) assert.False( t, strings.Contains(string(contents), cookie.String()), "contents are "+string(contents), ) assert.False( t, strings.Contains( string(contents), "<li class=\"cke\">test-", ), "contents are "+string(contents), ) new_cookie_set := false for _, cke_str := range response.Header["Set-Cookie"] { if cookieRegex.MatchString(cke_str) { new_cookie_set = true } } assert.False( t, new_cookie_set, "regex matched one of "+ gostring(response.Header["Set-Cookie"]), ) }
// TestPassthru verifies that a Falcore RequestFilter generated by // NewPassthruFilter will forward web traffic as expected. func TestPassthru(t *testing.T) { request, err := http.NewRequest("GET", "http://localhost", nil) assert.NoError(t, err) landingPipeline := falcore.NewPipeline() landingPipeline.Upstream.PushBack(pullcord.NewLandingFilter()) landingServer := falcore.NewServer(0, landingPipeline) regex, err := regexp.Compile("Pullcord Landing Page") assert.NoError(t, err) go serveLandingPage(landingServer) defer landingServer.StopAccepting() <-landingServer.AcceptReady _, response := falcore.TestWithRequest( request, NewPassthruFilter("localhost", landingServer.Port()), nil, ) assert.Equal(t, 200, response.StatusCode) contents, err := ioutil.ReadAll(response.Body) assert.NoError(t, err) assert.True(t, regex.Match(contents)) }
// TestAnotherLandingPage verifies that a Falcore RequestFilter generated by // NewLandingFilter responds appropriately to a request for an unexpected URI. // Specifically, this test verifies that no error occurs in the response. func TestAnotherLandingPage(t *testing.T) { request, err := http.NewRequest( "GET", "/other/page/somewhere/else.php", nil, ) assert.NoError(t, err) _, response := falcore.TestWithRequest(request, NewLandingFilter(), nil) assert.Equal(t, 200, response.StatusCode) }
func TestUpstreamThrottle(t *testing.T) { // Start a test server sleepPipe := falcore.NewPipeline() sleepPipe.Upstream.PushBack(falcore.NewRequestFilter(func(req *falcore.Request) *http.Response { time.Sleep(time.Second) return falcore.StringResponse(req.HttpRequest, 200, nil, "OK") })) sleepSrv := falcore.NewServer(0, sleepPipe) go func() { sleepSrv.ListenAndServe() }() <-sleepSrv.AcceptReady // Build Upstream up := NewUpstream(NewUpstreamTransport("localhost", sleepSrv.Port(), 0, nil)) // pipe := falcore.NewPipeline() // pipe.Upstream.PushBack(up) resCh := make(chan *http.Response, 10) var i int64 = 1 for ; i < 12; i++ { start := time.Now() up.SetMaxConcurrent(i) for j := 0; j < 10; j++ { go func() { req, _ := http.NewRequest("GET", "/", nil) _, res := falcore.TestWithRequest(req, up, nil) resCh <- res // fmt.Println("OK") }() } for j := 0; j < 10; j++ { res := <-resCh if res.StatusCode != 200 { t.Fatalf("Error: %v", res) } } duration := time.Since(start) seconds := float64(duration) / float64(time.Second) goal := math.Ceil(10.0 / float64(i)) // fmt.Println(i, "Time:", seconds, "Goal:", goal) if seconds < goal { t.Errorf("%v: Too short: %v < %v", i, seconds, goal) } } }
func TestUpstreamTimeout(t *testing.T) { // Start a test server sleepPipe := falcore.NewPipeline() sleepPipe.Upstream.PushBack(falcore.NewRequestFilter(func(req *falcore.Request) *http.Response { tt, _ := strconv.Atoi(req.HttpRequest.URL.Query().Get("time")) b, _ := strconv.Atoi(req.HttpRequest.URL.Query().Get("body")) bl, _ := strconv.Atoi(req.HttpRequest.URL.Query().Get("bl")) time.Sleep(time.Duration(tt)) pr, pw := io.Pipe() go func() { buf := make([]byte, 1024) for i := 0; i < bl; i++ { <-time.After(time.Duration(b)) pw.Write(buf) } pw.Close() }() return falcore.SimpleResponse(req.HttpRequest, 200, nil, int64(bl*1024), pr) })) sleepSrv := falcore.NewServer(0, sleepPipe) go func() { sleepSrv.ListenAndServe() }() <-sleepSrv.AcceptReady // Build Upstream up := NewUpstream(NewUpstreamTransport("localhost", sleepSrv.Port(), time.Second, nil)) for _, test := range upstreamTimeoutTestData { req, _ := http.NewRequest("GET", fmt.Sprintf("http://localhost/test?time=%v&body=%v&bl=%v", int64(test.Time), int64(test.BodyTime), test.BodyLen), nil) _, res := falcore.TestWithRequest(req, up, nil) if res.StatusCode != test.StatusCode { t.Errorf("%v Expected status %v Got %v", test.Name, test.StatusCode, res.StatusCode) } if res.StatusCode == 200 { i, _ := io.Copy(ioutil.Discard, res.Body) res.Body.Close() if i != (test.BodyLen * 1024) { t.Errorf("%v Expected body len %v Got %v", test.Name, (test.BodyLen * 1024), i) } } } }
func TestStringBody(t *testing.T) { expected := []byte("HOT HOT HOT!!!") tmp, _ := http.NewRequest("POST", "/hello", bytes.NewReader(expected)) tmp.Header.Set("Content-Type", "text/plain") tmp.ContentLength = int64(len(expected)) sbf := NewStringBodyFilter() req, _ := falcore.TestWithRequest(tmp, sbf, nil) if sb, ok := req.HttpRequest.Body.(*StringBody); ok { readin, _ := ioutil.ReadAll(sb) sb.Close() if bytes.Compare(readin, expected) != 0 { t.Errorf("Body string not read %q expected %q", readin, expected) } } else { t.Errorf("Body not replaced with StringBody") } if req.CurrentStage.Status != 0 { t.Errorf("SBF failed to parse POST with status %d", req.CurrentStage.Status) } var body []byte = make([]byte, 100) l, _ := req.HttpRequest.Body.Read(body) if bytes.Compare(body[0:l], expected) != 0 { t.Errorf("Failed to read the right bytes %q expected %q", body, expected) } l, _ = req.HttpRequest.Body.Read(body) if l != 0 { t.Errorf("Should have read zero!") } // Close resets the buffer req.HttpRequest.Body.Close() l, _ = req.HttpRequest.Body.Read(body) if bytes.Compare(body[0:l], expected) != 0 { t.Errorf("Failed to read the right bytes after calling Close %q expected %q", body, expected) } }
func TestHandlerFilter(t *testing.T) { reply := "Hello, World" handler := func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, reply) } hff := NewHandlerFilter(http.HandlerFunc(handler)) tmp, _ := http.NewRequest("GET", "/hello", nil) _, res := falcore.TestWithRequest(tmp, hff, nil) if res == nil { t.Errorf("Response is nil") } if replyGot, err := ioutil.ReadAll(res.Body); err != nil { t.Errorf("Error reading body: %v", err) } else if string(replyGot) != reply { t.Errorf("Expected body does not match") } }
// TestCookiemaskError verifies that a Falcore RequestFilter generated by // NewCookiemaskFilter behaves as expected when the cookie mask function throws // an error. Specifically, this function verifies that an internal server error // is sent in the response, and that no cookie is being set. func TestCookiemaskError(t *testing.T) { /* setup */ cookieRegex := regexp.MustCompile( "^test-[0-9A-Fa-f]+=[0-9A-Fa-f]*[1-9A-Fa-f][0-9A-Fa-f]+", ) request, err := http.NewRequest("GET", "/", nil) assert.NoError(t, err) cookie := testCookieGen("foo") request.AddCookie(&cookie) /* run */ filter := CookiemaskFilter{ handlerAlwaysErrors, cookieMaskTestPage, errorPage, } _, response := falcore.TestWithRequest( request, &filter, nil, ) /* check */ assert.Equal(t, 500, response.StatusCode) new_cookie_set := false for _, cke_str := range response.Header["Set-Cookie"] { if cookieRegex.MatchString(cke_str) { new_cookie_set = true } } assert.False( t, new_cookie_set, "regex matched one of "+ gostring(response.Header["Set-Cookie"]), ) }
func validGetRequest() (req *falcore.Request) { tmp, _ := http.NewRequest("GET", "/hello", bytes.NewBuffer(make([]byte, 0))) req, _ = falcore.TestWithRequest(tmp, falcore.NewRequestFilter(func(req *falcore.Request) *http.Response { return nil }), nil) return }
func TestPassthruLoginPage(t *testing.T) { /* setup */ testUser := "******" testPassword := "******" downstreamFilter := falcore.NewRequestFilter( func(request *falcore.Request) *http.Response { return falcore.StringResponse( request.HttpRequest, 200, nil, "<html><body><p>logged in</p></body></html>", ) }, ) sessionHandler := NewMinSessionHandler( "testSessionHandler", "/", "example.com", ) hash, err := GetPbkdf2Hash(testPassword, Pbkdf2MinIterations) assert.NoError(t, err) passwordChecker := InMemPwdStore{ map[string]*Pbkdf2Hash{ testUser: hash, }, } request1, err := http.NewRequest("GET", "/", nil) assert.NoError(t, err) /* run */ handler := &LoginHandler{ "testLoginHandler", &passwordChecker, downstreamFilter, } filter := &CookiemaskFilter{ sessionHandler, handler, falcore.NewRequestFilter( func(request *falcore.Request) *http.Response { return internalServerError(request) }, ), } _, response1 := falcore.TestWithRequest(request1, filter, nil) assert.Equal(t, 200, response1.StatusCode) assert.NotEmpty(t, response1.Header["Set-Cookie"]) content1, err := ioutil.ReadAll(response1.Body) assert.NoError(t, err) htmlRoot, err := html.Parse(bytes.NewReader(content1)) assert.NoError(t, err) xsrfToken, err := getXsrfToken(htmlRoot, "xsrf-"+handler.Identifier) assert.NoError(t, err) postdata2 := url.Values{} postdata2.Add("xsrf-"+handler.Identifier, xsrfToken) postdata2.Add("username-"+handler.Identifier, testUser) postdata2.Add("password-"+handler.Identifier, testPassword) request2, err := http.NewRequest( "POST", "/", strings.NewReader(postdata2.Encode()), ) request2.Header.Set( "Content-Type", "application/x-www-form-urlencoded", ) assert.NoError(t, err) for _, cke := range response1.Cookies() { request2.AddCookie(cke) } _, response2 := falcore.TestWithRequest(request2, filter, nil) assert.Equal(t, 200, response2.StatusCode) content2, err := ioutil.ReadAll(response2.Body) assert.NoError(t, err) assert.True( t, strings.Contains(string(content2), "logged in"), "content is: "+string(content2), ) request3, err := http.NewRequest("GET", "/", nil) assert.NoError(t, err) for _, cke := range response1.Cookies() { request3.AddCookie(cke) } _, response3 := falcore.TestWithRequest(request3, filter, nil) /* check */ assert.Equal(t, 200, response3.StatusCode) content3, err := ioutil.ReadAll(response3.Body) assert.NoError(t, err) assert.True( t, strings.Contains(string(content3), "logged in"), "content is: "+string(content3), ) }
func TestInitialLoginPage(t *testing.T) { /* setup */ testUser := "******" testPassword := "******" downstreamFilter := falcore.NewRequestFilter( func(request *falcore.Request) *http.Response { return falcore.StringResponse( request.HttpRequest, 200, nil, "<html><body><p>logged in</p></body></html>", ) }, ) sessionHandler := NewMinSessionHandler( "testSessionHandler", "/", "example.com", ) hash, err := GetPbkdf2Hash(testPassword, Pbkdf2MinIterations) assert.NoError(t, err) passwordChecker := InMemPwdStore{ map[string]*Pbkdf2Hash{ testUser: hash, }, } request, err := http.NewRequest("GET", "/", nil) assert.NoError(t, err) /* run */ handler := &LoginHandler{ "testLoginHandler", &passwordChecker, downstreamFilter, } filter := &CookiemaskFilter{ sessionHandler, handler, falcore.NewRequestFilter( func(request *falcore.Request) *http.Response { return internalServerError(request) }, ), } _, response := falcore.TestWithRequest(request, filter, nil) /* check */ assert.Equal(t, 200, response.StatusCode) content, err := ioutil.ReadAll(response.Body) assert.NoError(t, err) assert.True( t, strings.Contains(string(content), "xsrf-testLoginHandler"), "content is: "+string(content), ) assert.False( t, strings.Contains(string(content), "error"), "content is: "+string(content), ) assert.NotEmpty(t, response.Header["Set-Cookie"]) }
// TestDoubleCookiemaskBottomErrorTopMasking verifies that a chain of two // Falcore RequestFilters each generated by NewCookiemaskFilter behaves as // expected when given a request that contains one cookie that is not to be // masked and a second cookie that is associated with the first RequestFilter, // though that RequestFilter will throw an error. Specifically, this function // verifies that the expected error occurred, that the submitted cookie which // was not supposed to be masked did in fact make it to the onError // RequestFilter, and that no new maskable cookie is being set. func TestDoubleCookiemaskTopErrorBottomNoMasking(t *testing.T) { /* setup */ cookieRegex1 := regexp.MustCompile( "^error-[0-9A-Fa-f]+=[0-9A-Fa-f]*[1-9A-Fa-f][0-9A-Fa-f]+", ) cookieRegex2 := regexp.MustCompile( "^test2-[0-9A-Fa-f]+=[0-9A-Fa-f]*[1-9A-Fa-f][0-9A-Fa-f]+", ) request, err := http.NewRequest("GET", "/", nil) assert.NoError(t, err) cookie1 := testCookieGen("error") cookie2 := testCookieGen("foo") request.AddCookie(&cookie1) request.AddCookie(&cookie2) /* run */ innerFilter := CookiemaskFilter{ NewMinSessionHandler( "test2", "/", "example.com", ), cookieMaskTestPage, errorPage, } outerFilter := CookiemaskFilter{ handlerAlwaysErrors, &innerFilter, errorPage, } _, response := falcore.TestWithRequest( request, &outerFilter, nil, ) /* check */ assert.Equal(t, 500, response.StatusCode) contents, err := ioutil.ReadAll(response.Body) assert.NoError(t, err) assert.False(t, strings.Contains(string(contents), cookie1.String())) assert.False(t, strings.Contains(string(contents), cookie2.String())) assert.False( t, strings.Contains( string(contents), "<li class=\"cke\">error-", ), "contents are "+string(contents), ) assert.False( t, strings.Contains( string(contents), "<li class=\"cke\">foo-", ), "contents are "+string(contents), ) new_cookie1_set := false new_cookie2_set := false for _, cke_str := range response.Header["Set-Cookie"] { if cookieRegex1.MatchString(cke_str) { new_cookie1_set = true } else if cookieRegex2.MatchString(cke_str) { new_cookie2_set = true } } assert.False( t, new_cookie1_set, "regex1 matched one of "+ gostring(response.Header["Set-Cookie"]), ) assert.False( t, new_cookie2_set, "regex2 matched one of "+ gostring(response.Header["Set-Cookie"]), ) }
// TestDoubleCookiemaskBottomErrorTopMasking verifies that a chain of two // Falcore RequestFilters each generated by NewCookiemaskFilter behaves as // expected when given a request that contains one cookie that is to be masked // by the first RequestFilter and a second cookie that is associated with the // second RequestFilter, though that RequestFilter will throw an error. // Specifically, this function verifies that the expected error occurred, that // the submitted cookie which was supposed to be masked did not in fact make it // to the onError RequestFilter, and that no new maskable cookie is being set. func TestDoubleCookiemaskBottomErrorTopMasking(t *testing.T) { /* setup */ cookieRegex1 := regexp.MustCompile( "^test1-[0-9A-Fa-f]+=[0-9A-Fa-f]*[1-9A-Fa-f][0-9A-Fa-f]+", ) cookieRegex2 := regexp.MustCompile( "^error-[0-9A-Fa-f]+=[0-9A-Fa-f]*[1-9A-Fa-f][0-9A-Fa-f]+", ) request, err := http.NewRequest("GET", "/", nil) assert.NoError(t, err) handler1 := NewMinSessionHandler("test1", "/", "example.com") sesh1, err := handler1.GetSession() assert.NoError(t, err) fwd, ckes1, err := sesh1.CookieMask(nil) assert.NoError(t, err) assert.Empty(t, fwd) cookie1 := ckes1[0] cookie2 := testCookieGen("error") request.AddCookie(cookie1) request.AddCookie(&cookie2) /* run */ innerFilter := CookiemaskFilter{ handlerAlwaysErrors, cookieMaskTestPage, errorPage, } outerFilter := CookiemaskFilter{ handler1, &innerFilter, errorPage, } _, response := falcore.TestWithRequest( request, &outerFilter, nil, ) /* check */ assert.Equal(t, 500, response.StatusCode) contents, err := ioutil.ReadAll(response.Body) assert.NoError(t, err) assert.False(t, strings.Contains(string(contents), cookie1.String())) assert.False(t, strings.Contains(string(contents), cookie2.String())) // BUG(proidiot) issue-#45: context should have been preserved assert.False( t, strings.Contains( string(contents), "<li class=\"cke\">test1-", ), "contents are "+string(contents), ) assert.False( t, strings.Contains( string(contents), "<li class=\"cke\">error-", ), "contents are "+string(contents), ) new_cookie1_set := false new_cookie2_set := false for _, cke_str := range response.Header["Set-Cookie"] { if cookieRegex1.MatchString(cke_str) { new_cookie1_set = true } else if cookieRegex2.MatchString(cke_str) { new_cookie2_set = true } } assert.False( t, new_cookie1_set, "regex1 matched one of "+ gostring(response.Header["Set-Cookie"]), ) assert.False( t, new_cookie2_set, "regex2 matched one of "+ gostring(response.Header["Set-Cookie"]), ) }
// TestPostLandingPage verifies that a Falcore RequestFilter generated by // NewLandingFilter responds appropriately to a POST request. Specifically, this // test verifies that no error occurs in the response. func TestPostLandingPage(t *testing.T) { request, err := http.NewRequest("POST", "/", nil) assert.NoError(t, err) _, response := falcore.TestWithRequest(request, NewLandingFilter(), nil) assert.Equal(t, 200, response.StatusCode) }
func TestUpstreamThrottle(t *testing.T) { // Build a thing for var started = make(chan chan bool, REQ_COUNT+1) // Start a test server sleepPipe := falcore.NewPipeline() sleepPipe.Upstream.PushBack(falcore.NewRequestFilter(func(req *falcore.Request) *http.Response { // get chan // j, _ := strconv.Atoi(req.HttpRequest.URL.Query().Get("j")) // fmt.Println(req.HttpRequest.URL, j) c := make(chan bool) started <- c // wait on chan <-c // fmt.Println("DONE") return falcore.StringResponse(req.HttpRequest, 200, nil, "OK") })) sleepSrv := falcore.NewServer(0, sleepPipe) defer sleepSrv.StopAccepting() go func() { sleepSrv.ListenAndServe() }() <-sleepSrv.AcceptReady // Build Upstream up := NewUpstream(NewUpstreamTransport("localhost", sleepSrv.Port(), 0, nil)) // pipe := falcore.NewPipeline() // pipe.Upstream.PushBack(up) resCh := make(chan *http.Response, REQ_COUNT) var i int64 = 1 for ; i < 12; i++ { // fmt.Println("Testing with limit", i) up.SetMaxConcurrent(i) for j := 0; j < REQ_COUNT; j++ { var jj = j go func() { // fmt.Println("STARTING") req, _ := http.NewRequest("GET", fmt.Sprintf("http://localhost/foo?j=%v", jj), nil) _, res := falcore.TestWithRequest(req, up, nil) res.Body.Close() resCh <- res // fmt.Println("OK") }() } for j := 0; j < REQ_COUNT; j++ { // make sure we haven't gone over the limit // fmt.Println(i, len(started)) if r := int64(len(started)); r > i { t.Errorf("%v: Over the limit: %v", i, r) } // send a finish signal (<-started) <- true // collect the result res := <-resCh if res.StatusCode != 200 { t.Fatalf("Error: %v", res) } } } }