diff options
Diffstat (limited to 'http.go')
-rw-r--r-- | http.go | 124 |
1 files changed, 124 insertions, 0 deletions
@@ -0,0 +1,124 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "context" | ||
5 | "encoding/json" | ||
6 | "fmt" | ||
7 | "net" | ||
8 | "net/http" | ||
9 | "reflect" | ||
10 | "strings" | ||
11 | "time" | ||
12 | |||
13 | jww "github.com/spf13/jwalterweatherman" | ||
14 | ) | ||
15 | |||
16 | const ( | ||
17 | APP_CTX_KEY = "app" | ||
18 | ) | ||
19 | |||
20 | // tcpKeepAliveListener sets TCP keep-alive timeouts on accepted | ||
21 | // connections. It's used by ListenAndServe and ListenAndServeTLS so | ||
22 | // dead TCP connections (e.g. closing laptop mid-download) eventually | ||
23 | // go away. | ||
24 | type tcpKeepAliveListener struct { | ||
25 | *net.TCPListener | ||
26 | } | ||
27 | |||
28 | func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { | ||
29 | tc, err := ln.AcceptTCP() | ||
30 | if err != nil { | ||
31 | return | ||
32 | } | ||
33 | tc.SetKeepAlive(true) | ||
34 | tc.SetKeepAlivePeriod(3 * time.Minute) | ||
35 | return tc, nil | ||
36 | } | ||
37 | |||
38 | // Do the same thing ListenAndServe does but allow passing in the listener | ||
39 | // instead of the address so that we can bind privileged ports before dropping | ||
40 | // permissions to start the server itself. | ||
41 | func ListenAndServeRaw(ln net.Listener, handler http.Handler) error { | ||
42 | server := &http.Server{Addr: ln.Addr().String(), Handler: handler} | ||
43 | return server.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) | ||
44 | } | ||
45 | |||
46 | // Handler that pushes the application context onto the request context stack | ||
47 | // so that it's available to all other handlers in the stack. | ||
48 | type ContextAwareHandler struct { | ||
49 | ctx *appContext | ||
50 | handler http.HandlerFunc | ||
51 | } | ||
52 | |||
53 | func (h ContextAwareHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
54 | defaultHeaders(w) | ||
55 | h.handler(w, r.WithContext(context.WithValue(r.Context(), APP_CTX_KEY, h.ctx))) | ||
56 | } | ||
57 | |||
58 | // Handler that is able to inspect the application context and print out the | ||
59 | // value of specific fields. | ||
60 | type ContextPrintingHandler struct { | ||
61 | ctx *appContext | ||
62 | field string | ||
63 | } | ||
64 | |||
65 | func (h ContextPrintingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
66 | defaultHeaders(w) | ||
67 | |||
68 | c := reflect.ValueOf(h.ctx) | ||
69 | f := reflect.Indirect(c).FieldByName(h.field) | ||
70 | |||
71 | switch i := f.Interface().(type) { | ||
72 | case fmt.Stringer: | ||
73 | fmt.Fprintln(w, i.String()) | ||
74 | case *string: | ||
75 | if i == nil { | ||
76 | fmt.Fprintln(w, "") | ||
77 | } else { | ||
78 | fmt.Fprintln(w, *i) | ||
79 | } | ||
80 | default: | ||
81 | fmt.Fprintln(w, i) | ||
82 | } | ||
83 | } | ||
84 | |||
85 | // Print default headers for JSON data | ||
86 | func defaultHeaders(w http.ResponseWriter) { | ||
87 | w.Header().Set("Content-Type", "text/plain") | ||
88 | w.Header().Set("Server", "EC2ws") | ||
89 | w.Header().Set("Connection", "close") | ||
90 | } | ||
91 | |||
92 | // Write out JSON data in formatted form or an error | ||
93 | func writeHTTPJson(w http.ResponseWriter, data interface{}, name string) { | ||
94 | jd, err := json.MarshalIndent(data, "", " ") | ||
95 | if err != nil { | ||
96 | jww.ERROR.Printf("Error marshaling json in %s: %s", name, err) | ||
97 | http.Error(w, err.Error(), http.StatusInternalServerError) | ||
98 | } | ||
99 | fmt.Fprintf(w, string(jd)) | ||
100 | } | ||
101 | |||
102 | // Get the application context from the request context | ||
103 | func getAppCtx(r *http.Request) *appContext { | ||
104 | return r.Context().Value(APP_CTX_KEY).(*appContext) | ||
105 | } | ||
106 | |||
107 | // Handler that will reject non-local requests in case the daemon gets bound | ||
108 | // incorrectly to a public interface | ||
109 | type SecurityHandler struct { | ||
110 | handler http.Handler | ||
111 | } | ||
112 | |||
113 | func (h SecurityHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
114 | // String is ip:port formatted | ||
115 | ip := strings.Split(r.RemoteAddr, ":")[0] | ||
116 | |||
117 | if ip != "169.254.169.254" && ip != "127.0.0.1" { | ||
118 | jww.ERROR.Printf("Non-local metadata request from %s!", ip) | ||
119 | http.NotFound(w, r) | ||
120 | return | ||
121 | } else { | ||
122 | h.handler.ServeHTTP(w, r) | ||
123 | } | ||
124 | } | ||