diff options
author | Ben Morrison <ben@gbmor.dev> | 2020-05-05 23:43:03 -0400 |
---|---|---|
committer | Ben Morrison <ben@gbmor.dev> | 2020-05-05 23:43:03 -0400 |
commit | a3c1e1c95798046f72e990412d7342789f047f22 (patch) | |
tree | f8e8dd11e10851fab92e48b9d3440deee2c58714 | |
parent | 2e43f76937240083033be0357e7cf25b234a7ce0 (diff) | |
download | api-a3c1e1c95798046f72e990412d7342789f047f22.tar.gz |
stubbing out the HTTP server
Right now it won't do much. Working on parsing the request and routing it to the right place.
-rw-r--r-- | ctx.go | 55 | ||||
-rw-r--r-- | http.go | 37 | ||||
-rw-r--r-- | logging.go | 23 | ||||
-rw-r--r-- | main.go | 22 |
4 files changed, 137 insertions, 0 deletions
diff --git a/ctx.go b/ctx.go new file mode 100644 index 0000000..be4fb9b --- /dev/null +++ b/ctx.go @@ -0,0 +1,55 @@ +package main + +import ( + "context" + "log" + "net" + "net/http" + "strings" +) + +type ipCtxKey int + +const ctxKey ipCtxKey = iota + +// Creates a new context containing the request's originating IP. +// Preference: X-Forwarded-For, X-Real-IP, RemoteAddress +func newCtxUserIP(ctx context.Context, r *http.Request) context.Context { + split := strings.Split(r.RemoteAddr, ":") + uip := split[0] + + xFwdFor := http.CanonicalHeaderKey("X-Forwarded-For") + if _, ok := r.Header[xFwdFor]; ok { + fwdaddr := r.Header[xFwdFor] + split := strings.Split(fwdaddr[len(fwdaddr)-1], ":") + uip = split[0] + + return context.WithValue(ctx, ctxKey, uip) + } + + xRealIP := http.CanonicalHeaderKey("X-Real-IP") + if _, ok := r.Header[xRealIP]; ok { + realip := r.Header[xRealIP] + split := strings.Split(realip[len(realip)-1], ":") + uip = split[0] + } + + return context.WithValue(ctx, ctxKey, uip) +} + +// Yoinks the IP address from the request's context +func getIPFromCtx(ctx context.Context) net.IP { + uip, ok := ctx.Value(ctxKey).(string) + if !ok { + log.Printf("Couldn't retrieve IP From Request\n") + } + return net.ParseIP(uip) +} + +// Middleware to yeet the remote IP address into the request struct +func ipMiddleware(hop http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := newCtxUserIP(r.Context(), r) + hop.ServeHTTP(w, r.WithContext(ctx)) + }) +} diff --git a/http.go b/http.go new file mode 100644 index 0000000..9584c10 --- /dev/null +++ b/http.go @@ -0,0 +1,37 @@ +package main + +import ( + "errors" + "net/http" + "strings" +) + +const mimePlain = "text/plain; charset=utf-8" +const mimeJSON = "application/json; charset=utf-8" + +// Simple HTTP method check +func methodHop(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet && r.Method != http.MethodHead { + errHTTP(w, r, errors.New("405 Method Not Allowed"), http.StatusMethodNotAllowed) + return + } + + formatHop(w, r) +} + +// Makes sure the format requested is either plaintext or JSON +func formatHop(w http.ResponseWriter, r *http.Request) { + split := strings.Split(r.URL.Path[1:], "/") + + if split[0] != "plain" && split[0] != "json" { + errHTTP(w, r, errors.New("400 Bad Request"), http.StatusBadRequest) + return + } + + routingHop(w, r, split[0]) +} + +// Chooses next hop based on the endpoint +func routingHop(w http.ResponseWriter, r *http.Request, format string) { + +} diff --git a/logging.go b/logging.go new file mode 100644 index 0000000..26eb416 --- /dev/null +++ b/logging.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + "log" + "net/http" +) + +// Appends a 200 OK to the request log +func log200(r *http.Request) { + useragent := r.Header["User-Agent"] + uip := getIPFromCtx(r.Context()) + log.Printf("*** %v :: 200 :: %v %v :: %v\n", uip, r.Method, r.URL, useragent) +} + +// Appends a request of a given status code to the request +// log. Intended for errors. +func errHTTP(w http.ResponseWriter, r *http.Request, err error, code int) { + useragent := r.Header["User-Agent"] + uip := getIPFromCtx(r.Context()) + log.Printf("*** %v :: %v :: %v %v :: %v :: %v\n", uip, code, r.Method, r.URL, useragent, err.Error()) + http.Error(w, fmt.Sprintf("Error %v: %v", code, err.Error()), code) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..4fec978 --- /dev/null +++ b/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "log" + "net/http" + "time" +) + +func main() { + mux := http.NewServeMux() + + mux.HandleFunc("/", methodHop) + + server := &http.Server{ + Addr: ":9999", + Handler: ipMiddleware(mux), + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + log.Fatalf("%s", server.ListenAndServe()) +} |