-
Notifications
You must be signed in to change notification settings - Fork 11
/
main.go
executable file
·184 lines (154 loc) · 4.5 KB
/
main.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//
// Copyright © 2018 Roberto Della Fornace
// Implement a modular web server in Go
// License: MIT included in repository
//
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"plugin"
"strconv"
"time"
)
//source routes configuration struct to load from the json configuration file
type routes struct {
Endpoints []struct {
Controller string `json:"controller"`
Middlewares []struct {
Handler string `json:"handler"`
Params string `json:"params"`
} `json:"middlewares"`
Path string `json:"path"`
} `json:"endpoints"`
}
var RoutesConf routes
//source server configuration struct to load from json configuration file
type server struct {
Listento string `json:"listento"`
Readtimeout string `json:"readtimeout"`
Writetimeout string `json:"writetimeout"`
}
var ServerConf server
/* PLUGINS */
//Controller is a local hanlder plugin interface
type Controller interface {
Fire(w http.ResponseWriter, r *http.Request)
}
/* MIDDLEWARES */
//Middleware is local handler plugin interface, it will return a Gate compatible function
type Middleware interface {
Pass(args string) func(http.HandlerFunc) http.HandlerFunc
}
//Gate is a type that describe the middleware functions that will be chained to the route
type Gate func(http.HandlerFunc) http.HandlerFunc
//Chain function concatenate the middlewares (typed as Gate function)
func Chain(f http.HandlerFunc, middlewares ...Gate) http.HandlerFunc {
for _, m := range middlewares {
f = m(f)
}
return f
}
/* HELPER FUNCTIONS */
//kill function print the message and then exit
func kill(msg interface{}) {
fmt.Println(msg)
os.Exit(1)
}
//must function check if there is an error
func must(err error) {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
//ReadFromJSON function load a json file into a struct or return error
func ReadFromJSON(t interface{}, filename string) error {
jsonFile, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
err = json.Unmarshal([]byte(jsonFile), t)
if err != nil {
log.Fatalf("error: %v", err)
return err
}
return nil
}
//start point
func main() {
//load configurations from files
must(ReadFromJSON(&ServerConf, "configurations/server.json"))
must(ReadFromJSON(&RoutesConf, "configurations/routes.json"))
//convert source json strings to integer where needed
readtimeout, err := strconv.Atoi(ServerConf.Readtimeout)
if err != nil {
kill(err)
}
writetimeout, err := strconv.Atoi(ServerConf.Writetimeout)
if err != nil {
kill(err)
}
//set server configurations
srv := &http.Server{
ReadTimeout: time.Duration(readtimeout) * time.Second,
WriteTimeout: time.Duration(writetimeout) * time.Second,
Addr: ServerConf.Listento,
}
// based on the source confguration routes, loop on every configuration and load relative plugins
// plugin.Open: If a path has already been opened, then the existing *Plugin is returned.
// It is safe for concurrent use by multiple goroutines.
for _, v := range RoutesConf.Endpoints {
// load module:
plug, err := plugin.Open(v.Controller)
if err != nil {
kill(err)
}
// look up for an exported Controller method
symController, err := plug.Lookup("Controller")
if err != nil {
kill(err)
}
// check that loaded symbol is type Controller
var controller Controller
controller, ok := symController.(Controller)
if !ok {
kill("The Controller module have wrong type")
}
//define new middleware chain
var chain []Gate
// foreach middleware configured for the actual routepath
for _, mid := range v.Middlewares {
// load middleware plugin
plug, midErr := plugin.Open(mid.Handler)
if midErr != nil {
kill(midErr)
}
// look up the Pass function
symMiddleware, midErr := plug.Lookup("Middleware")
if midErr != nil {
kill(midErr)
}
// check that loaded symbol is type Middleware
var middleware Middleware
middleware, ok := symMiddleware.(Middleware)
if !ok {
kill("The middleware module have wrong type")
}
// build the gate function that contain the middleware instance
nmid := Gate(middleware.Pass(mid.Params))
// append to the middlewares chain
chain = append(chain, nmid)
}
// Use all the modules to handle the request
http.HandleFunc(v.Path, Chain(controller.Fire, chain...))
}
log.Println("start HTTP listening on ", ServerConf.Listento)
//SERVER START AND ERROR MANAGEMENT
//best practise: start a local istance of server mux to avoid imported lib to define malicious handler
log.Fatal(srv.ListenAndServe(), http.NewServeMux())
}