Example #1
0
func init() {
	go func() {
		// falcore setup
		pipeline := falcore.NewPipeline()
		pipeline.Upstream.PushBack(falcore.NewRequestFilter(func(req *falcore.Request) *http.Response {
			for _, data := range eserverData {
				if data.path == req.HttpRequest.URL.Path {
					header := make(http.Header)
					header.Set("Etag", data.etag)
					if data.chunked {
						buf := new(bytes.Buffer)
						buf.Write(data.body)
						res := falcore.SimpleResponse(req.HttpRequest, data.status, header, -1, ioutil.NopCloser(buf))
						res.TransferEncoding = []string{"chunked"}
						return res
					} else {
						return falcore.StringResponse(req.HttpRequest, data.status, header, string(data.body))
					}
				}
			}
			return falcore.StringResponse(req.HttpRequest, 404, nil, "Not Found")
		}))

		pipeline.Downstream.PushBack(new(EtagFilter))

		esrv = falcore.NewServer(0, pipeline)
		if err := esrv.ListenAndServe(); err != nil {
			panic("Could not start falcore")
		}
	}()
}
Example #2
0
// Handle upload
func (f UploadFilter) FilterRequest(request *falcore.Request) *http.Response {
	multipartReader, error := request.HttpRequest.MultipartReader()

	if error == http.ErrNotMultipart {
		return falcore.StringResponse(request.HttpRequest, http.StatusBadRequest, nil, "Bad Request // Not Multipart")
	} else if error != nil {
		return falcore.StringResponse(request.HttpRequest, http.StatusInternalServerError, nil, "Upload Error: "+error.Error()+"\n")
	}

	length := request.HttpRequest.ContentLength
	fmt.Println("content length:", length)

	part, error := multipartReader.NextPart()
	if error != io.EOF {
		var read int64
		var percent, previousPercent float64
		var filename string

		// find non existing filename
		for i := 1; ; i++ {
			filename = fmt.Sprintf("uploadedFile%v.mov", i)
			_, err := os.Stat(filename)
			if os.IsNotExist(err) {
				break
			}
		}

		fmt.Println(filename)
		destination, error := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0644)
		if error != nil {
			return falcore.StringResponse(request.HttpRequest, http.StatusInternalServerError, nil, "Internal Server Error // Could not create file")
		}

		buffer := make([]byte, 1024) // 100 kB
		for {
			byteCount, error := part.Read(buffer)
			read += int64(byteCount)
			percent = math.Floor(float64(read)/float64(length)*100) + 1

			if error == io.EOF {
				break
			} else if read > maxUploadBytes {
				fmt.Println("file too big")
				return falcore.StringResponse(request.HttpRequest, http.StatusRequestEntityTooLarge, nil, "Request Entity Too Large")
			}

			if percent != previousPercent {
				previousPercent = percent
				fmt.Printf("progress %v%%, read %fmb, %v byte of %v\n", percent, (float64(read) / (1024 * 1024)), read, length)
			}
			destination.Write(buffer)
		}
	}

	return falcore.StringResponse(request.HttpRequest, http.StatusOK, nil, "upload finished\n")
}
Example #3
0
func (f helloFilter) FilterRequest(req *falcore.Request) *http.Response {
	if req.HttpRequest.URL.Path == "/fast" {
		return falcore.StringResponse(req.HttpRequest, 200, nil, string("Hello, world!"))
	}
	if req.HttpRequest.URL.Path == "/slow" {
		time.Sleep(5 * time.Second)
		return falcore.StringResponse(req.HttpRequest, 200, nil, string("Hello, world!"))
	}
	if req.HttpRequest.URL.Path == "/panic" {
		panic("There is no need for panic")
		return falcore.StringResponse(req.HttpRequest, 200, nil, string("Hello, world!"))
	}

	return falcore.StringResponse(req.HttpRequest, 404, nil, "Not Found")
}
// return response message with encoded json data and http status code 200 (OK)
func SuccessResponse(request *falcore.Request, jsonData interface{}) *http.Response {
	response, jsonError := falcore.JSONResponse(request.HttpRequest, http.StatusOK, nil, jsonData)
	if jsonError != nil {
		response = falcore.StringResponse(request.HttpRequest, http.StatusInternalServerError, nil, fmt.Sprintf("JSON error: %s", jsonError))
	}
	return response
}
Example #5
0
// NewLandingFilter generates a Falcore RequestFilter that produces a simple
// landing page.
func NewLandingFilter() falcore.RequestFilter {
	log().Debug("registering a new landing filter")

	return falcore.NewRequestFilter(
		func(req *falcore.Request) *http.Response {
			log().Info("running landing filter")

			return falcore.StringResponse(
				req.HttpRequest,
				200,
				nil,
				"<html><head><title>"+
					"Pullcord Landing Page"+
					"</title></head><body><h1>"+
					"Pullcord Landing Page"+
					"</h1><p>"+
					"This is the landing page for Pullcord, "+
					"a reverse proxy for cloud-based web apps "+
					"that allows the servers the web apps run on "+
					"to be turned off when not in use."+
					"</p><p>"+
					"If you are unsure of how to proceed, "+
					"please contact the site administrator."+
					"</p></body></html>",
			)
		},
	)
}
Example #6
0
func (f *FileFilter) FilterRequest(req *falcore.Request) (res *http.Response) {
	// Clean asset path
	asset_path := filepath.Clean(filepath.FromSlash(req.HttpRequest.URL.Path))

	// Resolve PathPrefix
	if strings.HasPrefix(asset_path, f.PathPrefix) {
		asset_path = asset_path[len(f.PathPrefix):]
	} else {
		falcore.Debug("%v doesn't match prefix %v", asset_path, f.PathPrefix)
		res = falcore.StringResponse(req.HttpRequest, 404, nil, "Not found.")
		return
	}

	// Resolve FSBase
	if f.BasePath != "" {
		asset_path = filepath.Join(f.BasePath, asset_path)
	} else {
		falcore.Error("file_filter requires a BasePath")
		return falcore.StringResponse(req.HttpRequest, 500, nil, "Server Error\n")
	}

	// Open File
	if file, err := os.Open(asset_path); err == nil {
		// Make sure it's an actual file
		if stat, err := file.Stat(); err == nil && stat.Mode()&os.ModeType == 0 {
			res = &http.Response{
				Request:       req.HttpRequest,
				StatusCode:    200,
				Proto:         "HTTP/1.1",
				ProtoMajor:    1,
				ProtoMinor:    1,
				Body:          file,
				Header:        make(http.Header),
				ContentLength: stat.Size(),
			}
			if ct := mime.TypeByExtension(filepath.Ext(asset_path)); ct != "" {
				res.Header.Set("Content-Type", ct)
			}
		} else {
			file.Close()
		}
	} else {
		falcore.Finest("Can't open %v: %v", asset_path, err)
	}
	return
}
Example #7
0
func notImplementedError(request *falcore.Request) *http.Response {
	return falcore.StringResponse(
		request.HttpRequest,
		501,
		nil,
		"<html><body><h1>Not Implemented</h1>"+
			"<p>The requested behavior has not yet been implemented."+
			"Please contact your system administrator.</p></body></html>",
	)
}
Example #8
0
func internalServerError(request *falcore.Request) *http.Response {
	return falcore.StringResponse(
		request.HttpRequest,
		500,
		nil,
		"<html><body><h1>Internal Server Error</h1>"+
			"<p>An internal server error occured."+
			"Please contact your system administrator.</p></body></html>",
	)
}
Example #9
0
func (f *FileFilter) FilterRequest(req *falcore.Request) (res *http.Response) {
	// Clean asset path
	asset_path := filepath.Clean(filepath.FromSlash(req.HttpRequest.URL.Path))

	// Resolve PathPrefix
	if strings.HasPrefix(asset_path, f.PathPrefix) {
		asset_path = asset_path[len(f.PathPrefix):]
	} else {
		// The requested path doesn't fall into the scope of paths that are supposed to be handled by this filter
		return
	}

	// Resolve FSBase
	if f.BasePath != "" {
		asset_path = filepath.Join(f.BasePath, asset_path)
	} else {
		falcore.Error("file_filter requires a BasePath")
		return falcore.StringResponse(req.HttpRequest, 500, nil, "Server Error\n")
	}

	// Open File
	if file, err := os.Open(asset_path); err == nil {
		// If it's a directory, try opening the directory index
		if stat, err := file.Stat(); f.DirectoryIndex != "" && err == nil && stat.Mode()&os.ModeDir > 0 {
			file.Close()

			asset_path = filepath.Join(asset_path, f.DirectoryIndex)
			if file, err = os.Open(asset_path); err != nil {
				return
			}
		}

		// Make sure it's an actual file
		if stat, err := file.Stat(); err == nil && stat.Mode()&os.ModeType == 0 {
			res = &http.Response{
				Request:       req.HttpRequest,
				StatusCode:    200,
				Proto:         "HTTP/1.1",
				ProtoMajor:    1,
				ProtoMinor:    1,
				Body:          file,
				Header:        make(http.Header),
				ContentLength: stat.Size(),
			}
			if ct := mime.TypeByExtension(filepath.Ext(asset_path)); ct != "" {
				res.Header.Set("Content-Type", ct)
			}
		} else {
			file.Close()
		}
	} else {
		falcore.Finest("Can't open %v: %v", asset_path, err)
	}
	return
}
Example #10
0
func init() {
	go func() {
		// falcore setup
		pipeline := falcore.NewPipeline()
		pipeline.Upstream.PushBack(falcore.NewRequestFilter(func(req *falcore.Request) *http.Response {
			for _, data := range eserverData {
				if data.path == req.HttpRequest.URL.Path {
					header := make(http.Header)
					header.Set("Etag", data.etag)
					return falcore.StringResponse(req.HttpRequest, data.status, header, string(data.body))
				}
			}
			return falcore.StringResponse(req.HttpRequest, 404, nil, "Not Found")
		}))

		pipeline.Downstream.PushBack(new(EtagFilter))

		esrv = falcore.NewServer(0, pipeline)
		if err := esrv.ListenAndServe(); err != nil {
			panic("Could not start falcore")
		}
	}()
}
Example #11
0
func loginPage(
	request *falcore.Request,
	sesh Session,
	badCreds bool,
) *http.Response {
	return falcore.StringResponse(
		request.HttpRequest,
		501,
		nil,
		"<html><body><h1>Not Implemented</h1>"+
			"<p>The requested behavior has not yet been implemented."+
			"Please contact your system administrator.</p></body></html>",
	)
}
Example #12
0
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)
		}
	}

}
// return response message with encoded json error message and a custom error code
// if logMessage is nil, do not write to log
func ErrorResponse(request *falcore.Request, status int, error string, errorDescription string, logMessage error) *http.Response {
	if logMessage != nil {
		log.Println(fmt.Sprintf("%s: %s, %s", error, errorDescription, logMessage))
	}

	type Json struct {
		Error            string
		ErrorDescription string
	}
	json := Json{error, errorDescription}

	response, jsonError := falcore.JSONResponse(request.HttpRequest, status, nil, json)
	if jsonError != nil {
		response = falcore.StringResponse(request.HttpRequest, http.StatusInternalServerError, nil, fmt.Sprintf("JSON error: %s", jsonError))
		log.Println(fmt.Sprintf("%s: %s, %s, json error:", error, errorDescription, logMessage, jsonError))
	}
	return response
}
Example #14
0
// very simple request filter
func Filter(request *falcore.Request) *http.Response {
	return falcore.StringResponse(request.HttpRequest, 200, nil, "OK\n")
}
Example #15
0
func (f helloFilter) FilterRequest(req *falcore.Request) *http.Response {
	return falcore.StringResponse(req.HttpRequest, 200, nil, "hello world!\n")
}
Example #16
0
					"</li>"
		}
		content += "</ul><h1>context</h1><ul>"
		sesh := req.Context["session"].(*MinSession)
		for key, val := range sesh.GetValues() {
			content +=
				"<li class=\"sesh\">" +
					key +
					": " +
					gostring(val) +
					"</li>"
		}
		content += "</ul></body></html>"
		return falcore.StringResponse(
			req.HttpRequest,
			200,
			nil,
			content,
		)
	},
)

var errorPage = falcore.NewRequestFilter(
	func(req *falcore.Request) *http.Response {
		// BUG(proidiot) issue-#45: Does not print context or cookies
		return falcore.StringResponse(
			req.HttpRequest,
			500,
			nil,
			"<html><body><p>"+
				"internal server error"+
				"</p></body></html>",
Example #17
0
func (handler *LoginHandler) FilterRequest(
	request *falcore.Request,
) *http.Response {
	errString := ""
	rawsesh, present := request.Context["session"]
	if !present {
		log().Crit(
			"login handler was unable to retrieve session from" +
				" context",
		)
		return internalServerError(request)
	}
	sesh := rawsesh.(Session)

	authSeshKey := "authenticated-" + handler.Identifier
	xsrfKey := "xsrf-" + handler.Identifier
	usernameKey := "username-" + handler.Identifier
	passwordKey := "password-" + handler.Identifier

	authd, err := sesh.GetValue(authSeshKey)
	if err == nil && authd == true {
		log().Debug("login handler passing request along")
		return handler.Downstream.FilterRequest(request)
	} else if err != NoSuchSessionValueError {
		log().Err(
			fmt.Sprintf(
				"login handler error during auth status"+
					" retrieval: %v",
				err,
			),
		)
		return internalServerError(request)
	}

	xsrfStored, err := sesh.GetValue(xsrfKey)
	if err != nil && err != NoSuchSessionValueError {
		log().Err(
			fmt.Sprintf(
				"login handler error during xsrf token"+
					" retrieval: %v",
				err,
			),
		)
		return internalServerError(request)
	} else if err == NoSuchSessionValueError {
		log().Info("login handler received new request")
	} else if err = request.HttpRequest.ParseForm(); err != nil {
		log().Warning(
			fmt.Sprintf(
				"login handler error during ParseForm: %v",
				err,
			),
		)
		errString = "Bad request"
	} else if xsrfRcvd, present :=
		request.HttpRequest.PostForm[xsrfKey]; !present {
		log().Info("login handler did not receive xsrf token")
		errString = "Invalid credentials"
	} else if len(xsrfRcvd) != 1 || 1 != subtle.ConstantTimeCompare(
		[]byte(xsrfStored.(string)),
		[]byte(xsrfRcvd[0]),
	) {
		log().Info("login handler received bad xsrf token")
		errString = "Invalid credentials"
	} else if uVals, present :=
		request.HttpRequest.PostForm[usernameKey]; !present {
		log().Info("login handler did not receive username")
		errString = "Invalid credentials"
	} else if pVals, present :=
		request.HttpRequest.PostForm[passwordKey]; !present {
		log().Info("login handler did not receive password")
		errString = "Invalid credentials"
	} else if len(uVals) != 1 || len(pVals) != 1 {
		log().Info(
			"login handler received multi values for username or" +
				" password",
		)
		errString = "Bad request"
	} else if err = handler.PasswordChecker.CheckPassword(
		uVals[0],
		pVals[0],
	); err == NoSuchIdentifierError {
		log().Info("login handler received bad username")
		errString = "Invalid credentials"
	} else if err == BadPasswordError {
		log().Info("login handler received bad password")
		errString = "Invalid credentials"
	} else if err != nil {
		log().Err(
			fmt.Sprintf(
				"login handler error during CheckPassword: %v",
				err,
			),
		)
		return internalServerError(request)
	} else if err = sesh.SetValue(authSeshKey, true); err != nil {
		log().Err(
			fmt.Sprintf(
				"login handler error during auth set: %v",
				err,
			),
		)
		return internalServerError(request)
	} else {
		log().Notice(
			fmt.Sprintf(
				"login successful for: %s",
				uVals[0],
			),
		)
		return handler.Downstream.FilterRequest(request)
	}

	rawXsrfToken := make([]byte, XsrfTokenLength)
	if rsize, err := rand.Read(
		rawXsrfToken[:],
	); err != nil || rsize != XsrfTokenLength {
		log().Err(
			fmt.Sprintf(
				"login handler error during xsrf generation:"+
					" len expected: %u, actual: %u, err: %v",
				XsrfTokenLength,
				rsize,
				err,
			),
		)
		return internalServerError(request)
	}
	nextXsrfToken := hex.EncodeToString(rawXsrfToken)

	if err = sesh.SetValue(xsrfKey, nextXsrfToken); err != nil {
		log().Err(
			fmt.Sprintf(
				"login handler error during xsrf set: %v",
				err,
			),
		)
		return internalServerError(request)
	}

	errMarkup := ""
	if errString != "" {
		errMarkup = fmt.Sprintf(
			"<label class=\"error\">%s</label>",
			errString,
		)
	}

	return falcore.StringResponse(
		request.HttpRequest,
		200,
		nil,
		fmt.Sprintf(
			"<html><head><title>Pullcord Login</title></head>"+
				"<body><form method=\"POST\" action=\"%s\">"+
				"<fieldset><legend>Pullcord Login</legend>%s<label "+
				"for=\"username\">Username:</label><input "+
				"type=\"text\" name=\"%s\" id=\"username\" /><label "+
				"for=\"password\">Password:</label><input "+
				"type=\"password\" name=\"%s\" id=\"password\" />"+
				"<input type=\"hidden\" name=\"%s\" value=\"%s\" />"+
				"<input type=\"submit\" value=\"Login\"/></fieldset>"+
				"</form></body></html>",
			request.HttpRequest.URL.Path,
			errMarkup,
			usernameKey,
			passwordKey,
			xsrfKey,
			nextXsrfToken,
		),
	)
}
Example #18
0
// catch all filter: if no filter matched, return 404 not found as default response
func (f NotFoundFilter) FilterRequest(request *falcore.Request) *http.Response {
	return falcore.StringResponse(request.HttpRequest, http.StatusNotFound, nil, "Not Found")
}
Example #19
0
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),
	)
}
Example #20
0
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"])
}
Example #21
0
func (svc *MinMonitorredService) FilterRequest(
	req *falcore.Request,
) *http.Response {
	log().Debug("running minmonitor filter")

	up, err := svc.Status()
	if err != nil {
		log().Warning(
			fmt.Sprintf(
				"minmonitor filter received an error"+
					" while requesting the status for \"%s:%d\":"+
					" %v",
				svc.Address,
				svc.Port,
				err,
			),
		)
		return falcore.StringResponse(
			req.HttpRequest,
			500,
			nil,
			"<html><head><title>Pullcord - Internal"+
				" Server Error</title></head><body><h1>"+
				"Pullcord - Internal Server Error</h1><p>An"+
				" internal server error has occurred, but it"+
				" might not be serious. However, If the"+
				" problem persists, the site administrator"+
				" should be contacted.</p></body></html>",
		)
	}

	if svc.Always != nil {
		err = svc.Always.Trigger()
		if err != nil {
			log().Warning(
				fmt.Sprintf(
					"minmonitor filter received"+
						" an error while running the"+
						" always trigger on \"%s:%d\": %v",
					svc.Address,
					svc.Port,
					err,
				),
			)
			return falcore.StringResponse(
				req.HttpRequest,
				500,
				nil,
				"<html><head><title>Pullcord -"+
					" Internal Server Error</title>"+
					"</head><body><h1>Pullcord -"+
					" Internal Server Error</h1><p>"+
					"An internal server error has"+
					" occurred, but it might not be"+
					" serious. However, if the problem"+
					" persists, the site administrator"+
					" should be contacted.</p></body>"+
					"</html>",
			)
		}
	}

	if up {
		if svc.OnUp != nil {
			err = svc.OnUp.Trigger()
			if err != nil {
				log().Warning(
					fmt.Sprintf(
						"minmonitor filter"+
							" received an error"+
							" while running the"+
							" onDown trigger on"+
							" \"%s:%d\": %v",
						svc.Address,
						svc.Port,
						err,
					),
				)
				return falcore.StringResponse(
					req.HttpRequest,
					500,
					nil,
					"<html><head><title>Pullcord"+
						" - Internal Server Error"+
						"</title></head><body><h1>"+
						"Pullcord - Internal Server"+
						" Error</h1><p>An internal"+
						" server error has occurred,"+
						" but it might not be"+
						" serious. However, if the"+
						" problem persists, the site"+
						" administrator should be"+
						" contacted.</p></body></html>",
				)
			}
		}

		log().Debug("minmonitor filter passthru")
		return svc.passthru.FilterRequest(req)
	}

	if svc.OnDown != nil {
		err = svc.OnDown.Trigger()
		if err != nil {
			log().Warning(
				fmt.Sprintf(
					"minmonitor filter received"+
						" an error while running the"+
						" onDown trigger on \"%s:%d\": %v",
					svc.Address,
					svc.Port,
					err,
				),
			)
			return falcore.StringResponse(
				req.HttpRequest,
				500,
				nil,
				"<html><head><title>Pullcord -"+
					" Internal Server Error</title>"+
					"</head><body><h1>Pullcord -"+
					" Internal Server Error</h1><p>"+
					"An internal server error has"+
					" occurred, but it might not be"+
					" serious. However, If the problem"+
					" persists, the site administrator"+
					" should be contacted.</p></body>"+
					"</html>",
			)
		}
	}

	log().Info(
		fmt.Sprintf(
			"minmonitor filter has reached a down"+
				" service (\"%s:%d\"), but any triggers have"+
				" fired successfully",
			svc.Address,
			svc.Port,
		),
	)
	return falcore.StringResponse(
		req.HttpRequest,
		503,
		nil,
		"<html><head><title>Pullcord - Service Not Ready"+
			"</title></head><body><h1>Pullcord - Service Not"+
			" Ready</h1><p>The requested service is not yet"+
			" ready, but any trigger to start the service has"+
			" been started successfully, so hopefully the"+
			" service will be up in a few minutes.</p><p>If you"+
			" would like further information, please contact the"+
			" site administrator.</p></body></html>",
	)
}
Example #22
0
func (u *Upstream) FilterRequest(request *falcore.Request) (res *http.Response) {
	var err error
	req := request.HttpRequest

	if u.Name != "" {
		request.CurrentStage.Name = fmt.Sprintf("%s[%s]", request.CurrentStage.Name, u.Name)
	}

	// Throttle
	// Wait for an opening, then increment in flight counter
	u.throttleC.L.Lock()
	u.throttleQueue += 1
	for u.throttleMax > 0 && u.throttleInFlight >= u.throttleMax {
		u.throttleC.Wait()
	}
	u.throttleQueue -= 1
	u.throttleInFlight += 1
	u.throttleC.L.Unlock()
	// Decrement and signal when done
	defer func() {
		u.throttleC.L.Lock()
		u.throttleInFlight -= 1
		u.throttleC.Signal()
		u.throttleC.L.Unlock()
	}()

	// Force the upstream to use http
	if u.ForceHttp || req.URL.Scheme == "" {
		req.URL.Scheme = "http"
		req.URL.Host = req.Host
	}
	before := time.Now()
	req.Header.Set("Connection", "Keep-Alive")
	var upstrRes *http.Response
	upstrRes, err = u.Transport.transport.RoundTrip(req)
	diff := falcore.TimeDiff(before, time.Now())
	if err == nil {
		// Copy response over to new record.  Remove connection noise.  Add some sanity.
		res = falcore.StringResponse(req, upstrRes.StatusCode, nil, "")
		if upstrRes.ContentLength > 0 {
			res.ContentLength = upstrRes.ContentLength
			res.Body = upstrRes.Body
		} else if res.ContentLength == -1 {
			res.Body = upstrRes.Body
			res.ContentLength = -1
			res.TransferEncoding = []string{"chunked"}
		} else {
			// Any bytes?
			var testBuf [1]byte
			n, _ := io.ReadFull(upstrRes.Body, testBuf[:])
			if n == 1 {
				// Yes there are.  Chunked it is.
				res.TransferEncoding = []string{"chunked"}
				res.ContentLength = -1
				rc := &passThruReadCloser{
					io.MultiReader(bytes.NewBuffer(testBuf[:]), upstrRes.Body),
					upstrRes.Body,
				}

				res.Body = rc
			} else {
				// There was an error reading the body
				upstrRes.Body.Close()
				res.ContentLength = 0
				res.Body = nil
			}
		}
		// Copy over headers with a few exceptions
		res.Header = make(http.Header)
		for hn, hv := range upstrRes.Header {
			switch hn {
			case "Content-Length":
			case "Connection":
			case "Transfer-Encoding":
			default:
				res.Header[hn] = hv
			}
		}
	} else {
		if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
			falcore.Error("%s [%s] Upstream Timeout error: %v", request.ID, u.Name, err)
			res = falcore.StringResponse(req, 504, nil, "Gateway Timeout\n")
			request.CurrentStage.Status = 2 // Fail
		} else {
			falcore.Error("%s [%s] Upstream error: %v", request.ID, u.Name, err)
			res = falcore.StringResponse(req, 502, nil, "Bad Gateway\n")
			request.CurrentStage.Status = 2 // Fail
		}
	}
	falcore.Debug("%s %s [%s] [%s] %s s=%d Time=%.4f", request.ID, u.Name, req.Method, u.Transport.host, req.URL, res.StatusCode, diff)
	return
}
Example #23
0
)

// Command line options
var (
	port = flag.Int("port", 8000, "the port to listen on")
)

func main() {
	// parse command line options
	flag.Parse()

	// setup pipeline
	pipeline := falcore.NewPipeline()

	// upstream
	pipeline.Upstream.PushBack(helloFilter)

	// setup server
	server := falcore.NewServer(*port, pipeline)

	// start the server
	// this is normally blocking forever unless you send lifecycle commands
	if err := server.ListenAndServe(); err != nil {
		fmt.Println("Could not start server:", err)
	}
}

var helloFilter = falcore.NewRequestFilter(func(req *falcore.Request) *http.Response {
	return falcore.StringResponse(req.HttpRequest, 200, nil, "hello world!")
})
Example #24
0
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)
			}
		}
	}

}