-
Notifications
You must be signed in to change notification settings - Fork 38
/
mango.go
148 lines (121 loc) · 3.42 KB
/
mango.go
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)
}