Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 149 lines (121 sloc) 3.507 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
// Mango is a modular web-application framework for Go, inspired by Rack and PEP333.
package mango

import (
"fmt"
"log"
"net/http"
"net/textproto"
"os"
)

type Request struct {
*http.Request
}

type Status int
type Body string

type Headers http.Header

func (h Headers) Add(key, value string) {
textproto.MIMEHeader(h).Add(key, value)
}

func (h Headers) Set(key, value string) {
textproto.MIMEHeader(h).Set(key, value)
}

func (h Headers) Get(key string) string {
return textproto.MIMEHeader(h).Get(key)
}

func (h Headers) Del(key string) {
textproto.MIMEHeader(h).Del(key)
}

type Env map[string]interface{}

func (this Env) Logger() *log.Logger {
if this["mango.logger"] == nil {
this["mango.logger"] = log.New(os.Stdout, "", log.Ldate|log.Ltime)
}

return this["mango.logger"].(*log.Logger)
}

func (this Env) Request() *Request {
return this["mango.request"].(*Request)
}

func (this Env) Session() map[string]interface{} {
return this["mango.session"].(map[string]interface{})
}

// This is the core app the user has written
type App func(Env) (Status, Headers, Body)

// These are pieces of middleware,
// which 'wrap' around the core App
// (and each other)
type Middleware func(Env, App) (Status, Headers, Body)

// Bundle a given list of Middleware pieces into a App
func bundle(r ...Middleware) App {
if len(r) <= 1 {
// Terminate the innermost piece of Middleware
// Basically stops it from recursing any further.
return func(input Env) (Status, Headers, Body) {
return r[0](input, func(Env) (Status, Headers, Body) {
panic("Core Mango App should never call it's upstream function.")
})
}
}
return wrap(r[0], bundle(r[1:]...))
}

// Attach a piece of Middleware to the outside
// of a App. This wraps the inner App
// inside the outer Middleware.
func wrap(middleware Middleware, app App) App {
return func(input Env) (Status, Headers, Body) {
return middleware(input, app)
}
}

// Convert a App into Middleware
// We convert the core app into a Middleware
// so we can pass it to Bundle as part of the
// stack. Because the App does not call its
// upstream method, the resulting Middleware
// will just ignore any upstream passed to it.
func middlewareify(app App) Middleware {
return func(input Env, upstream App) (Status, Headers, Body) {
return app(input)
}
}

type Stack struct {
Address string
middleware []Middleware
app App
}

func Version() []int {
return []int{0, 5, 0}
}

func VersionString() string {
v := Version()
return fmt.Sprintf("%d.%02d.%02d", v[0], v[1], v[2])
}

func (this *Stack) Middleware(middleware ...Middleware) {
this.middleware = middleware
}

func (this *Stack) Compile(app App) App {
this.app = app
return bundle(append(this.middleware, middlewareify(this.app))...)
}

func (this *Stack) HandlerFunc(app App) http.HandlerFunc {
compiled_app := this.Compile(app)
return func(w http.ResponseWriter, r *http.Request) {
env := make(map[string]interface{})
env["mango.request"] = &Request{r}
env["mango.version"] = Version()

status, headers, body := compiled_app(env)

for key, values := range headers {
for _, value := range values {
w.Header().Add(key, value)
}
}
w.WriteHeader(int(status))
w.Write([]byte(body))
}
}

func (this *Stack) Run(app App) error {
if this.Address == "" {
this.Address = "0.0.0.0:8000"
}
fmt.Println("Starting Mango Stack On:", this.Address)
http.HandleFunc("/", this.HandlerFunc(app))
return http.ListenAndServe(this.Address, nil)
}
Something went wrong with that request. Please try again.