-
Notifications
You must be signed in to change notification settings - Fork 0
/
hash-o-matic.go
159 lines (126 loc) · 3.71 KB
/
hash-o-matic.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
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"
)
const DEFAULT_LISTEN_ADDR = ":8080"
var (
wg sync.WaitGroup
mutex sync.Mutex
shutdownChan chan bool
interruptChan chan os.Signal
HashCount int64
Hashes map[int64]string
)
type redirect struct {
target string
code int
}
func main() {
serverAddress := DEFAULT_LISTEN_ADDR
if len(os.Getenv("PORT")) > 0 {
serverAddress = ":" + os.Getenv("PORT")
}
Hashes = make(map[int64]string)
// handlers for configured endpoints
http.HandleFunc("/", RedirectHandler)
http.HandleFunc("/hash", HashPostHandler)
http.HandleFunc("/hash/", HashGetHandler)
http.HandleFunc("/shutdown", ShutdownHandler)
// basic logging of connections
handler := LogHandler(http.DefaultServeMux)
// create the server, used in SelectChannel for graceful shutdown
server := &http.Server{Addr: serverAddress, Handler: handler}
// Create a channel and signal notifier to catch OS level interrupts (i.e. ^C)
interruptChan = make(chan os.Signal)
signal.Notify(interruptChan, os.Interrupt, syscall.SIGTERM)
// Create a channel and associated handler for PUTs to /shutdown
shutdownChan = make(chan bool)
// setup to wait for an interrupt or shutdown call
go SelectChannel(server, interruptChan, shutdownChan)
log.Printf("Server listening on: %s", server.Addr)
// add server to waitgroup
wg.Add(1)
go server.ListenAndServe()
// wait for everything to finish
wg.Wait()
log.Println("Shutdown complete.")
}
func SelectChannel(
server *http.Server,
interruptChan chan os.Signal,
shutdownChan chan bool) {
select {
case n := <-interruptChan:
log.Printf("Received signal %s; shutting down\n", n.String())
StopServer(server)
case _ = <-shutdownChan:
log.Printf("Received call to /shutdown, shutting down\n")
StopServer(server)
}
}
func StopServer(server *http.Server) {
defer wg.Done()
log.Println("Stopping server...")
// create context with a max timeout of 5 seconds
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err := server.Shutdown(ctx)
if err != nil {
log.Fatalf("Error while shutting down: %s", err)
}
}
func ShutdownHandler(res http.ResponseWriter, req *http.Request) {
// PUT because shutdown is a modification
if req.Method == http.MethodPut {
// shutdown issued here
res.WriteHeader(http.StatusAccepted)
res.Write([]byte("shutting down..."))
shutdownChan <- true
} else {
// no more supported methods to match return 404, write header first
res.WriteHeader(http.StatusMethodNotAllowed)
res.Write([]byte("Method not allowed\n"))
}
}
func LogHandler(handler http.Handler) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
req.ParseForm()
log.Printf("Incoming %s request from %s on %s using %s\n",
req.Method, req.RemoteAddr, req.URL, req.UserAgent())
handler.ServeHTTP(res, req)
log.Printf("%s Request from %s Complete\n", req.Method, req.RemoteAddr)
})
}
func getRedirectTargets() (redirectMap map[string]redirect) {
redirectMap = make(map[string]redirect)
redirectMap["/"] = redirect{
"https://github.com/dbyington/hash-o-matic#readme",
http.StatusFound,
}
return redirectMap
}
func mapRedirect(requestUrl string) (redirectUrl redirect) {
var redir redirect
var err error
var ok bool
redirectMap := getRedirectTargets()
if redir, ok = redirectMap[requestUrl]; ok {
if err != nil {
log.Printf("Error parsing redirect url %s, got %s", redir.target, err.Error())
}
}
return redir
}
func RedirectHandler(res http.ResponseWriter, req *http.Request) {
redirectTo := mapRedirect(req.RequestURI)
if redirectTo.code > 0 {
http.Redirect(res, req, redirectTo.target, redirectTo.code)
}
}