-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathserver.go
More file actions
145 lines (121 loc) · 3.76 KB
/
server.go
File metadata and controls
145 lines (121 loc) · 3.76 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
package engine
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/pkg/errors"
)
// Server is a server capable of answering requests to multiple engines
type Server struct {
router *mux.Router
handlers []Handler
}
// NewServer makes a new server
func NewServer() (server *Server) {
server = &Server{
router: mux.NewRouter(),
}
server.init()
return
}
// init intializes the server
func (server *Server) init() {
// add the root route
server.router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if !server.ensureMethod(w, r, "GET") {
return
}
server.jsonResponse(w, r, http.StatusOK, server.Status())
})
// add a custom 404 route
server.router.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
server.errorResponse(w, r, http.StatusNotFound, errors.Errorf("Not found"))
})
}
// StatusResponse is the response for a status request
type StatusResponse struct {
// Name of this server
Name string `json:"name"`
// Server tagline
TagLine string `json:"tagline"`
// List of engines that are enabled on this server.
Engines map[string]bool `json:"engines"`
}
// Status returns the current status
func (server *Server) Status() *StatusResponse {
engines := make(map[string]bool)
for _, h := range server.handlers {
engines[h.Name()] = h.Enabled()
}
return &StatusResponse{
Name: "mwsapid",
TagLine: "You know, for math",
Engines: engines,
}
}
// AddHandler adds a handler to the server
func (server *Server) AddHandler(handler Handler) {
// store the handler
server.handlers = append(server.handlers, handler)
// and add an actual route
server.router.HandleFunc("/"+handler.Name()+"/", func(w http.ResponseWriter, r *http.Request) {
if !server.ensureMethod(w, r, "POST") {
return
}
code, res, err := handler.ServeHTTP(w, r)
if code == 0 {
code = 200
}
if err != nil {
server.errorResponse(w, r, code, err)
} else {
server.jsonResponse(w, r, code, res)
}
})
}
// ListenAndServe starts serving requests on the given port
func (server *Server) ListenAndServe(host string, port int) error {
return http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), server.logRequest(server.router))
}
// ensureMethod ensures that the method `method` is used in the request
// returns true if it is so
// returns false and sends an error response if it is not so
func (server *Server) ensureMethod(w http.ResponseWriter, r *http.Request, method string) bool {
if r.Method != method {
server.errorResponse(w, r, http.StatusMethodNotAllowed, errors.Errorf("Invalid request type: Expected %s but got %s", method, r.Method))
return false
}
return true
}
// jsonResponse returns a json or jsonp response
func (server *Server) jsonResponse(w http.ResponseWriter, r *http.Request, code int, response interface{}) {
// marshal the response
data, err := json.Marshal(response)
if err != nil {
data = []byte(`{"error":true,"message":"Unknown error"}`)
code = http.StatusInternalServerError
}
// write the code
w.WriteHeader(code)
// check if we have a JSONP callback parameter
callback := r.URL.Query().Get("callback")
if callback == "" {
w.Header().Set("Content-Type", "application/json")
} else {
w.Header().Set("Content-Type", "application/javascript")
data = []byte(fmt.Sprintf("%s(%s);", callback, data))
}
w.Write(data)
}
// errorResponse returns an error response
func (server *Server) errorResponse(w http.ResponseWriter, r *http.Request, code int, err error) {
server.jsonResponse(w, r, code, fmt.Sprintf("%v", err))
}
func (server *Server) logRequest(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL)
handler.ServeHTTP(w, r)
})
}