forked from dpapathanasiou/go-api
-
Notifications
You must be signed in to change notification settings - Fork 1
/
http-api-server.go
74 lines (64 loc) · 2.7 KB
/
http-api-server.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
// Package api provides a framework for creating HTTP servers in Go (http://golang.org/) to handle API requests capable of replying in xml, json, or any other valid content type.
package api
import (
"crypto/hmac"
"crypto/sha1"
"fmt"
"log"
"net/http"
"os"
"time"
)
type Server struct {
mux *http.ServeMux
s *http.Server
Logger *log.Logger
}
var (
Srv *Server
DefaultServerReadTimeout = 30 // in seconds
)
// DigestMatches is an optional hmac check that can be applied to any or all api queries.
// DigestMatches takes three strings: a private key, a query term, and a sha1 digest of the
// query term string using the shared private key known only by authorized api clients
// and this server (see http://en.wikipedia.org/wiki/Hmac for more details on how it works).
// DigestMatches returns a boolean if the hmac digest is correct or not.
func DigestMatches(privateKey string, queryTerm string, queryTermDigest string) bool {
h := hmac.New(sha1.New, []byte(privateKey))
h.Write([]byte(queryTerm))
hashed := fmt.Sprintf("%x", h.Sum(nil))
return (hashed == queryTermDigest)
}
// Respond accepts an HTTP media type, charset, and a response function which returns a string.
// Respond wraps the server reply in the correct Content-type, charset, and Content-length,
// returning an http.HandlerFunc invoked by the HTTP multiplexer in reponse to the particular url pattern
// associated with this response function.
func Respond(mediaType string, charset string, fn func(w http.ResponseWriter, r *http.Request) string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", fmt.Sprintf("%s; charset=%s", mediaType, charset))
data := fn(w, r)
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(data)))
fmt.Fprintf(w, data)
}
}
// NewServer takes a port number, read timeout (in secords), along with a map defining url string patterns,
// and their corresponding response functions. NewServer sets each map entry into the HTTP multiplexer,
// then starts the HTTP server on the given port. The api.Server struct also provides a Logger for each
// response function to use, to log warnings, errors, and other information.
func NewServer(bind string, timeout int, handlers map[string]func(http.ResponseWriter, *http.Request)) {
mux := http.NewServeMux()
for pattern, handler := range handlers {
mux.Handle(pattern, http.HandlerFunc(handler))
}
s := &http.Server{
Addr: bind,
Handler: mux,
ReadTimeout: time.Duration(timeout) * time.Second, // to prevent abuse of "keep-alive" requests by clients
}
Srv = &Server{
mux: mux,
s: s,
Logger: log.New(os.Stdout, "", log.Ldate|log.Ltime),
}
Srv.s.ListenAndServe()
}