Mango is a modular web-application framework for Go, inspired by Rack, and PEP333.

Mango

Mango is a modular web-application framework for Go, inspired by Rack and PEP333.

Note: Not actively maintained.

Overview

Mango is most of all a framework for other modules, and apps. It takes a list of middleware, and an application and compiles them into a single http server object. The middleware and apps are written in a functional style, which keeps everything very self-contained.

Mango aims to make building reusable modules of HTTP functionality as easy as possible by enforcing a simple and unified API for web frameworks, web apps, middleware.

API

As in Rack, the API is very minimal.

Applications are of the form:

func Hello(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
  return 200, mango.Headers{}, mango.Body("Hello World!")
}

Where:

  • mango.Env is a map[string]interface{} of the environment
    • It also has accessors for several other environment attributes:
      • mango.Env.Request() is the http.Request object
      • mango.Env.Session() is the map[string]interface for the session (only if using the Sessions middleware)
      • mango.Env.Logger() is the default logger for the app (or your custom logger if using the Logger middleware)
  • mango.Status is an integer for the HTTP status code for the response
  • mango.Headers is a map[string][]string of the response headers (similar to http.Header)
  • mango.Body is a string for the response body

Installation

$ go install github.com/paulbellamy/mango

Available Modules

  • Sessions

    Usage: mango.Sessions(app_secret, cookie_name, cookie_domain string)

    Basic session management. Provides a mango.Env.Session() helper which returns a map[string]interface{} representing the session. Any data stored in here will be serialized into the response session cookie.

  • Logger

    Usage: mango.Logger(custom_logger \*log.Logger)

    Provides a way to set a custom log.Logger object for the app. If this middleware is not provided Mango will set up a default logger to os.Stdout for the app to log to.

  • ShowErrors

    Usage: mango.ShowErrors(templateString string)

    Catch any panics thrown from the app, and display them in an HTML template. If templateString is "", a default template is used. Not recommended to use the default template in production as it could provide information helpful to attackers.

  • Routing

    Usage: mango.Routing(routes map[string]App)

    "routes" is of the form { "/path1(.*)": sub-stack1, "/path2(.*)": sub-stack2 }. It lets us route different requests to different mango sub-stacks based on regexing the path.

  • Static

    Usage: mango.Static(directory string)

    Serves static files from the directory provided.

  • JSONP

    Usage: mango.JSONP

    Provides JSONP support. If a request has a 'callback' parameter, and your application responds with a Content-Type of "application/json", the JSONP middleware will wrap the response in the callback function and set the Content-Type to "application/javascript".

  • Basic Auth

    Usage: mango.BasicAuth(auth func(username string, password string, Request, error) bool, failure func(Env) (Status, Headers, Body))

    Performs HTTP Basic Auth. The auth function returns true if the username and password are accepted. If failure is nil, a default failure page will be used.

Example App

package main

import (
  "github.com/paulbellamy/mango"
)

func Hello(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
  env.Logger().Println("Got a", env.Request().Method, "request for", env.Request().RequestURI)
  return 200, mango.Headers{}, mango.Body("Hello World!")
}

func main() {
  stack := new(mango.Stack)
  stack.Address = ":3000"
  stack.Middleware(mango.ShowErrors(""))
  stack.Run(Hello)
}

Mango Stacks

Mango revolves around the idea of a "stack", which is a collection of middleware and an application. Stacks can be compiled without being run. When you compile a stack it returns a Mango App which incorporates all of the middleware and the application. We can compile a Mango stack like:

stack := new(mango.Stack)
var compiled mango.App = stack.Compile(Hello)

This compiled stack can be passed to the Routing middleware as a "sub-stack".

Stack can also be compiled into an http.HandlerFunc by calling:

stack := new(mango.Stack)
var listener http.HandlerFunc = stack.HandlerFunc(Hello)

This returns a http.HandlerFunc ready to be passed to http.ListenAndServe, which incorporates the entire Mango stack.

Custom Middleware

Building middleware for Mango is fairly straightforward.

If you build some middleware and think others might find it useful, please let me know so I can include it in the core Mango source. The success of Mango really depends on having excellent middleware packages.

An extremely basic middleware package is simply a function:

func SilenceErrors(env mango.Env, app mango.App) (mango.Status, mango.Headers, mango.Body) {
  // Call our upstream app
  status, headers, body := app(env)

  // If we got an error
  if status == 500 {
    // Silence it!
    status = 200
    headers = mango.Headers{}
    body = "Silence is golden!"
  }

  // Pass the response back to the client
  return status, headers, body
}

To use this middleware we would do:

func main() {
  stack := new(mango.Stack)
  stack.Address = ":3000"

  stack.Middleware(SilenceErrors) // Include our custom middleware

  stack.Run(Hello)
}

For more complex middleware we may want to pass it configuration parameters. An example middleware package is one which will replace any image tags with funny pictures of cats:

func Cats(cat_images []string) mango.Middleware {
  // Initial setup stuff here
  // Done on application setup

  // Initialize our regex for finding image links
  regex := regexp.MustCompile("[^\"']+(.jpg|.png|.gif)")

  // This is our middleware's request handler
  return func(env mango.Env, app mango.App) (mango.Status, mango.Headers, mango.Body) {
    // Call the upstream application
    status, headers, body := app(env)

    // Pick a random cat image
    image_url := cat_images[rand.Int()%len(cat_images)]

    // Substitute in our cat picture
    body = mango.Body(regex.ReplaceAllString(string(body), image_url))

    // Send the modified response onwards
    return status, headers, body
  }
}

This works by building a closure (function) based on the parameters we pass, and returning it as the middleware. Through the magic of closures, the value we pass for cat_images gets built into the function returned.

To use our middleware we would do:

func main() {

  stack := new(mango.Stack)
  stack.Address = ":3000"

  // Initialize our cats middleware with our list of cat_images
  cat_images := []string{"ceiling_cat.jpg", "itteh_bitteh_kittehs.jpg", "monorail_cat.jpg"}
  cats_middleware := Cats(cat_images)

  stack.Middleware(cats_middleware) // Include the Cats middleware in our stack

  stack.Run(Hello)
}

Routing example

The following example routes "/hello" traffic to the hello handler and "/bye" traffic to the bye handler, any other traffic goes to routeNotFound handler returning a 404.

package main

import(
  "github.com/paulbellamy/mango"
  "fmt"
)

func hello(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
  env.Logger().Println("Got a", env.Request().Method, "request for", env.Request().RequestURI)
  return 200, mango.Headers{}, mango.Body("Hello World!")
}

func bye(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
  return 200, mango.Headers{}, mango.Body("Bye Bye!")
}

func routeNotFound(env mango.Env) (mango.Status, mango.Headers, mango.Body) {
  return 404, mango.Headers{}, mango.Body("You probably got lost :(")
}

func main() {
  routes := make(map[string]mango.App)
  routes["/hello"] = new(mango.Stack).Compile(hello)
  routes["/bye"] = new(mango.Stack).Compile(bye)

  testServer := new(mango.Stack)
  testServer.Middleware(mango.ShowErrors("<html><body>{Error|html}</body></html>"), mango.Routing(routes))
  testServer.Address = "localhost:3000"
  testServer.Run(routeNotFound)
  fmt.Printf("Running server on: %s\n", testServer.Address)
}

About

Mango was written by Paul Bellamy.

Follow me on Twitter!

Comments
  • Incompatibilities with HEAD

    Incompatibilities with HEAD

    I presume that these are issues related to newer versions of go:

    tobi@i7 ~  $ goinstall github.com/paulbellamy/mango
    vgoinstall: === cd /Users/tobi/local/cellar/go/r57.2/src/pkg/github.com/paulbellamy/mango; gomake -f- install
    6g  -o _go_.6 jsonp.go logger.go mango.go mime.go routing.go sessions.go show_errors.go static.go 
    sessions.go:52: not enough arguments in call to strings.Split
    sessions.go:97: env.Request().Cookies undefined (type *Request has no field or method Cookies)
    sessions.go:98: typechecking loop
    sessions.go:113: cookie.String undefined (type *http.Cookie has no field or method String)
    make: *** [_go_.6] Error 1
    --- exit status 2
    goinstall: installing github.com/paulbellamy/mango: running gomake: exit status 2
    
  • Examples building

    Examples building

    Refactored Makefile to compile libs. It's pretty straight and maybe there's lot of space to improve (I'm a make noob) but it works.

    Also added requirements to logger example.

  • improve performance for not Set-Cookie all the time

    improve performance for not Set-Cookie all the time

    1. If session doesn't change, which is most of the case, it should't Set-Cookie all the time. So that nginx can do proxy cache correctly.
    2. Using map for session store make it impossible to compare old cookie and new cookie are the same. because of map are random accessed.
  • RawURL deprecated

    RawURL deprecated

    From the Readme:

    env.Logger().Println("Got a", env.Request().Method, "request for", env.Request().RawURL)

    Somewhere last year Request.RawURL was deprecated. See discussion and commit:

    https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/t7YlEOJB0xg http://code.google.com/p/go/source/detail?r=af3d7d10735c

    Request().RequestURI should do the trick.

  • Bug: Binary static files being served incorrectly

    Bug: Binary static files being served incorrectly

    Symptom: Incorrect data is being served by mango.Static for binary files.

    Possible cause: The mango.Body type is a string, so binary bytes are typecast to string in mango.Static.

    Possible solution: Change mango.Body type to []byte

  • Potential Middleware: Basic-Auth

    Potential Middleware: Basic-Auth

    Would be nice to have a basic-auth middleware. It should probably accept an 'authenticator' function that verifies a set of credentials? That way it could be very extensible.

  • fixed and improved README

    fixed and improved README

    Hey Paul,

    I was playing with your mango lib and I figured that the least I could do was to contribute fixes to the readme and provide a small routing example.

    • Matt
  • Pull request: one typo and two unexported fields

    Pull request: one typo and two unexported fields

    Typo in README references RawUrl, should be RawURL for *http. Two examples reference unexported address field, instead of Address. Mango looks great, can't wait to wire up some of my own middleware.

  • Sessions: HMAC instead of SHA1

    Sessions: HMAC instead of SHA1

    Please use HMAC instead of doing SHA1 over the concatenation of data and secret to avoid length-extension attacks. Although, the use of gob encoding makes them harder to implement, I think they are still possible.

    Thank you!

  • Potential Middleware: Edge-Side Include Processor

    Potential Middleware: Edge-Side Include Processor

    It might be nice to have an Edge-Side Include processing middleware.

    For more info on ESIs see: http://en.wikipedia.org/wiki/Edge_Side_Includes

    Other Thoughts:

    • This may be a bad idea. Maybe we should just leave ESIs to Varnish, and real caching proxies.
  • Potential Middleware: Caching

    Potential Middleware: Caching

    It would be awesome to support drop-in http-caching.

    Other Thoughts:

    • Will probably want to pass the caching 'stuff' (i.e. how long to cache this for, invalidation, etc...) back from the app via some specific header field (or several)
    • In-memory store would be a great first step, but it would be nice to eventually support redis/memcached/etc... for caching across servers.
  • Potential Middleware: Lint for validation

    Potential Middleware: Lint for validation

    It would be nice to have a Lint module so that we could validate the environment, and response.

    Other Thoughts:

    • Will probably be a lot simpler than Rack::Lint, because Go is statically-typed, so we primarily just need to check the HTTP spec is being followed. (i.e. status code is >= 100, content-length matches, etc...)
  • Potential Middleware: Test module

    Potential Middleware: Test module

    It might be nice to have a "Test" middleware, where requests could be injected into the app stack, and the responses given extra information about what got called, etc...

    Other Thoughts:

    • I'm not 100% sure if this is really needed, since we can do: stack.Compile(handler), and then pass that an Env, or do stack.HandlerFunc(handler), and pass that an http.Request.
A Go framework for building JSON web services inspired by Dropwizard

Tiger Tonic A Go framework for building JSON web services inspired by Dropwizard. If HTML is your game, this will hurt a little. Like the Go language

Dec 9, 2022
Golanger Web Framework is a lightweight framework for writing web applications in Go.

/* Copyright 2013 Golanger.com. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except

Nov 14, 2022
Fast and Reliable Golang Web Framework
Fast and Reliable Golang Web Framework

Gramework The Good Framework Gramework long-term testing stand metrics screenshot made with Gramework Stats Dashboard and metrics middleware What is i

Dec 18, 2022
An ideally refined web framework for Go.

Air An ideally refined web framework for Go. High-performance? Fastest? Almost all web frameworks are using these words to tell people that they are t

Dec 15, 2022
Web framework for creating apps using Go in Google AppEngine

Welcome to app.go v3.0 app.go is a simple web framework for use in Google AppEngine. Just copy the app folder to your working folder and import it fro

Mar 21, 2021
Eudore is the core of a golang lightweight web framework.

Eudore eudore是一个golang轻量级web框架核心,可以轻松扩展成一个技术栈专用框架,具有完整框架设计体系。 反馈和交流请加群组:QQ群373278915。 Features 易扩展:主要设计目标、核心全部解耦,接口即为逻辑。 简单:对象语义明确,框架代码量少复杂度低,无依赖库。 易用

Nov 7, 2022
Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.
Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.

Gin Web Framework Gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks

Jan 2, 2023
Goal is a toolkit for high productivity web development in Go language in the spirit of Revel Framework that is built around the concept of code generation.

Goal Goal is a set of tools for high productivity web development in Go language. Goal, being mostly inspired by Revel Framework and its discussions,

Sep 27, 2021
a golang web mvc framework, like asp.net mvc.

goku goku is a Web Mvc Framework for golang, mostly like ASP.NET MVC. doc & api Installation To install goku, simply run go get github.com/QLeelulu/go

Dec 7, 2022
A high level web-framework for Go

go-start is a high level web-framework for Go, like Django for Python or Rails for Ruby. Installation: go get github.com/ungerik/go-start Documentatio

Dec 24, 2022
A lightweight RESTful web framework for Go
A lightweight RESTful web framework for Go

Goweb A lightweight RESTful web framework for Go. For examples and usage, please read the Goweb API Documentation Read our Articles Who uses Goweb? "U

Dec 12, 2022
Classy web framework for Go

Martini NOTE: The martini framework is no longer maintained. Martini is a powerful package for quickly writing modular web applications/services in Go

Dec 29, 2022
The web framework for Golang
The web framework for Golang

uAdmin the Golang Web Framework Easy to use, blazing fast and secure. Originally open source by IntegrityNet Solutions and Services For Documentation:

Dec 24, 2022
Simple web framework for go, still quite beta at this point

WFDR Framework - Beta Release New 18/Feb/2012: Updated for go 1.0, new directory layout to take advantage of the go build tool. Background There's a m

Feb 11, 2021
A simple blog framework built with GO. Uses HTML files and a JSON dict to give you more control over your content.

Go-Blog A simple template based blog framework. Instructions Built for GO version: 1 See the Documentation or Getting Started pages in the wiki. Notes

Sep 10, 2022
A small and evil REST framework for Go

go-rest A small and evil REST framework for Go Reflection, Go structs, and JSON marshalling FTW! go get github.com/ungerik/go-rest import "github.com/

Dec 6, 2022
Golang CTF framework and exploit development module

Golang CTF framework and exploit development module

Dec 18, 2022
:bullettrain_side: High-performance web server for Go.
:bullettrain_side: High-performance web server for Go.

Aero is a high-performance web server with a clean API. Installation go get -u github.com/aerogo/aero/... Usage Run this in an empty directory: aero -

Dec 8, 2022
package for building REST-style Web Services using Go

go-restful package for building REST-style Web Services using Google Go Code examples using v3 REST asks developers to use HTTP methods explicitly and

Jan 1, 2023