Gottp is not a regular front-end server to do user-facing CSS powered websites. It was designed using backend servers in mind and offers a variety of features like:
- Background Workers
- Call Aggregation using Non-Blocking or Blocking Pipes. [1]
- Optionally Listens on Unix Domain socket.
- In-built error traceback emails.
- Optional data compression using zlib/gzip.
- Pagination support.
- Automatic listing of all exposed URLs
[1] Much like Batch requests in Facebook Graph API (https://developers.facebook.com/docs/graph-api/making-multiple-requests)
Installation is as easy as:
go get gopkg.in/meson10/gottp.v3
A sample application named helloWorld is available inside the tests directory of your checkout
To start building a web service using gottp just create a new project with the following structure.
- conf.go -> Configuration
- main.go -> main Server engine
- urls.go -> Register urls & corresponding handlers.
- handlers.go -> handlers processing the request.
This section is applicable only when you need to provide application based settings alongside those used by gottp.
Configuration must implement the Configurer interface.
Configurer requires two method Sets:
- MakeConfig(string) which accepts the path of the .cfg file provided as an command line argument.
- GetGottpConfig() which must return the Settings
type Configurer interface {
MakeConfig(string)
GetGottpConfig() *GottpSettings
}
A minimalist extended configuration looks like:
import "gopkg.in/meson10/gottp.v3/conf"
type config struct {
Custom struct {
VarOne string
VarTwo string
}
Gottp conf.GottpSettings
}
func (self *config) MakeConfig(configPath string) {
if configPath != "" {
conf.MakeConfig(configPath, self)
}
}
func (self *config) GetGottpConfig() *conf.GottpSettings {
return &self.Gottp
}
var settings config
A sample urls.go looks like:
package main
import "gopkg.in/meson10/gottp.v3"
func init(){
gottp.NewUrl("hello", "/hello/\\w{3,5}/?$", new(handlers.HelloMessage)),
}
This would match all urls that are like "/hello/world" or "/hello/greet" but NOT /hello/123 and not /hello/yester
A sample handler looks like:
package handlers
import "gopkg.in/meson10/gottp.v3"
type HelloMessage struct {
gottp.BaseHandler
}
func (self *HelloMessage) Get(req *gottp.Request) {
req.Write("hello world")
}
A sample main.go looks like:
package main
import (
"log"
"gopkg.in/meson10/gottp.v3"
)
func main() {
gottp.MakeServer(&settings)
}
go install test && test
Point your browser to http://127.0.0.1:8005/hello/check
Should give you a JSON output:
{
"data": "hello world",
"message": "",
"status": 200
}
Gottp allows you to provide .cfg files via the command line which is as easy as ./binary -config=path_to_cfg
Default gottp settings is a struct called GottpSettings
type GottpSettings struct {
EmailHost string //SMTP Host to send server Tracebacks.
EmailPort string //SMTP Port
EmailUsername string //Username or Password to connect with SMTP
EmailPassword string
EmailSender string //Sender Name to be used for traceback.
EmailFrom string //Verified sender email address like [email protected]
ErrorTo []string //List of recipients for tracebacks.
EmailDummy bool //Set to True, if Tracebacks should not be sent.
Listen string //Address to Listen on default: 127.0.0.1:8005
}
You can provide a simple configuration file in .cfg format to load the settings which would look like this:
[custom]
VarOne="one"
VarTwo="two"
[gottp]
listen="/tmp/custom.sock"
EmailHost="email-smtp.us-east-1.amazonaws.com"
EmailPort="587"
EmailPassword="TDVAGCWCTCTWCTCQ&&*!!*!*!*/NeURB5"
EmailUsername="HelloWorldSample"
EmailSender="My Gottp Server"
EmailFrom="[email protected]"
ErrorTo="[email protected]"
EmailDummy=false
Urls are of type gottp.Url
type Url struct {
name string //shortname of the url
url string //provided regular pattern
handler func(r *Request) //ReuqestHandler
pattern *regexp.Regexp //Compiled Regular Expression
}
URLs can be constructed using gottp.NewUrl which accepts shortname, regular expression & Handler interface implementor respectively.
A request handler must implement the Handler Interface which must expose the following methods
type Handler interface {
Get(request *Request)
Put(request *Request)
Post(request *Request)
Delete(request *Request)
Head(request *Request)
Options(request *Request)
Patch(request *Request)
}
gottp.BaseHandler has most common HTTP requests as method sets. So, If a struct uses BaseHandler as an embedded type it is sufficient to qualify as a Handler. To expose a new HTTP method for a URL type, implement the HTTP method set in the handler struct. See the Example below:
type helloMessage struct {
gottp.BaseHandler
}
func (self *helloMessage) Get(req *gottp.Request) {
req.Write("hello world - GET")
}
func (self *helloMessage) Post(req *gottp.Request) {
req.Write("hello world - POST")
}
Request exposes a few method structs:
GetArguments() *utils.Q
_ Returns a map of all arguments passed to the request. This includes Body arguments in case of a PUT/POST request, url GET arguments and named arguments captured in URL regular expression. You can call this over as it handles caching internally. _
GetArgument(key string) interface{}
ConvertArguments(dest interface{})
ConvertArgument(key string, dest interface{})
These methods come quite handy when you have to deal with the arguments. For a request with GET arguments that look like:
?abc=world¶m=1&check=0
You can either fetch individual arguments like:
abcVar, _ := req.GetArgument("abc").(string)
log.Println(abcVar)
Or initialize a struct to convert all the arguments:
type params struct {
abc string
param int
check int
}
getArgs := params{}
req.ConvertArguments(&getArgs)
log.Println(getArgs.abc)
With backends powered by Gottp, consumers can simply access http://{host}:{port}/urls to fetch a json of all URLs exposed by the application. URLs are returned as a map of key: pattern where the key is the human-readable-identifier provided at the time of constructing URLS and the pattern is the URL regular expression pattern.
This helps in preventing hard coding of endpoints where consumers can fetch the URLs at application start time and reconstruct URLs using the indentifiers.
Sample Output:
{
"data": {
"hello": "/hello/\\w{3,5}/?$"
},
"message": "",
"status": 200
}
All gottp based servers allow clubbing of requests to be executed together. This is a very handy feature that is used for clubbing calls that need to processed one after the other. It can save the network handshaking costs as the handshake is only performed once and this does not effect server performance as all gottp requests are performed as goroutines.
Example usage of such calls is:
- Create a Comment
- Mark the parent notification as read
- Send out notification to the parent autho
- Churn the new activity feed as per edge rank.
Using Pipes, call 1, 2, 3 & 4 can be combined into a single request to the server.
All the requests would sequentially evaluated and data would be returned in the same order as requested.
To submit a PIPE request issue a POST call on /pipe (available by default).
Async pipes behave the same way, except the requests are executed in parallel as go-routines. Async pipes are as fast as the slowest call in your request stack.
Despite parallel execution they return the data in the same order as requested.
To submit an Async PIPE request issue a POST call on /async-pipe (available by default)
Requests submitted as PIPEs should again be a valid JSON dump of
{
"stack": [
{"url": "/url1", "method": "POST", "data": {"sample": "json"}},
{"url": "/url2", "method": "GET", "data": {"get": "argument"}},
...
]
}
Gottp can send error tracebacks if a request failed. This can be enabled by setting EmailDummy as false in the gottp section of cfg.
A sample traceback email looks like this:
TODO: Expose a way to use custom email templates.
Gottp now supports background workers. Background workers allow parallel background execution of long running processes.
There are a few advantage of using gottp managed background worker over simply spawning a goroutine:
- Gottp Workers are autoamtically recovered and re-spawned in case of Panic. You also get a fancy error traceback provided you have configured the email tracebacks, discussed earlier.
- Gottp Workers do not quit abruptly when main thread receives an interrupt. This is beneficial to process cleanups and other vital exit routines.
- Gottp Workers are provided with a default timeout of 10 seconds and a worker that seems to be taking forever is then terminated.
- Gottp gracefully handles all process interrupts and signals the background worker accordingly.
NOTE: At this moment, gottp only supports the option of one background worker.
Usage:
import "gopkg.in/meson10/gottp.v3"
func main() {
gottp.RunWorker(func(exitChan bool) {
log.Println("Do some background work here");
})
}