summaryrefslogtreecommitdiff
path: root/web/utils.go
diff options
context:
space:
mode:
Diffstat (limited to 'web/utils.go')
-rw-r--r--web/utils.go117
1 files changed, 117 insertions, 0 deletions
diff --git a/web/utils.go b/web/utils.go
new file mode 100644
index 0000000..5467132
--- /dev/null
+++ b/web/utils.go
@@ -0,0 +1,117 @@
1package web
2
3import (
4 "context"
5 "fmt"
6 "log"
7 "net"
8 "net/http"
9 "net/url"
10 "os"
11 "os/signal"
12 "regexp"
13
14 "github.com/gin-gonic/gin"
15)
16
17// Parses an IPv4 or IPv6 address with an optional port on the end. Returns
18// match groups for the addresses. The first match is the IPv6 address and the
19// second the IPv4 address.
20var ipRegexp = regexp.MustCompile(`(?:\[([0-9a-f:]+)\]|(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))(?::\d+)?`)
21
22// TODO: use from a common package
23func ParseIP(s string) net.IP {
24 ips := ipRegexp.FindStringSubmatch(s)
25 if ips == nil {
26 return nil
27 }
28
29 if v6, v4 := ips[1], ips[2]; v6 != "" {
30 return net.ParseIP(v6)
31 } else {
32 return net.ParseIP(v4)
33 }
34}
35
36// TODO: use from a common package
37func GetRequestIP(c *gin.Context) net.IP {
38 if xff := c.Request.Header.Get("X-Forwarded-For"); xff != "" {
39 return ParseIP(xff)
40 }
41 return ParseIP(c.Request.RemoteAddr)
42}
43
44// TODO: use from a common package
45func MakeURL(r *http.Request, path string, subs ...interface{}) *url.URL {
46 scheme := "https"
47 if r.TLS == nil {
48 scheme = "http"
49 }
50
51 // Always defer to whatever the proxy told us it was doing because this
52 // could be a mullet-VIP in either direction.
53 if fwProto := r.Header.Get("X-Forwarded-Proto"); fwProto != "" {
54 scheme = fwProto
55 }
56
57 return &url.URL{
58 Scheme: scheme,
59 Host: r.Host,
60 Path: fmt.Sprintf(path, subs...),
61 }
62}
63
64// TODO: use from a common package
65// Copied from: https://github.com/gin-gonic/gin/blob/59ab588bf597f9f41faee4f217b5659893c2e925/utils.go#L137
66func resolveAddress(addr []string) string {
67 switch len(addr) {
68 case 0:
69 if port := os.Getenv("PORT"); port != "" {
70 log.Printf("Environment variable PORT=\"%s\"", port)
71 return ":" + port
72 }
73 log.Printf("Environment variable PORT is undefined. Using port :8080 by default")
74 return ":8080"
75 case 1:
76 return addr[0]
77 default:
78 panic("too many parameters")
79 }
80}
81
82// TODO: use from a common package
83// Runs a gin.Engine instance in a way that can be canceled by an SIGINT
84func GinRun(e *gin.Engine, debug bool, a ...string) {
85 if debug {
86 gin.SetMode(gin.DebugMode)
87 } else {
88 gin.SetMode(gin.ReleaseMode)
89 }
90
91 srv := &http.Server{
92 Addr: resolveAddress(a),
93 Handler: e,
94 }
95
96 idleConnsClosed := make(chan struct{})
97
98 go func() {
99 sigint := make(chan os.Signal, 1)
100 signal.Notify(sigint, os.Interrupt)
101 <-sigint
102
103 log.Println("Caught SIGINT, shutting down")
104 if err := srv.Shutdown(context.Background()); err != nil {
105 log.Printf("HTTP server Shutdown: %v", err)
106 }
107
108 close(idleConnsClosed)
109 }()
110
111 log.Printf("Listening and serving HTTP on %s\n", srv.Addr)
112 if err := srv.ListenAndServe(); err != http.ErrServerClosed {
113 log.Fatalf("HTTP server ListenAndServe: %v", err)
114 }
115
116 <-idleConnsClosed
117}