about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBen Morrison <ben@gbmor.dev>2020-05-05 23:43:03 -0400
committerBen Morrison <ben@gbmor.dev>2020-05-05 23:43:03 -0400
commita3c1e1c95798046f72e990412d7342789f047f22 (patch)
treef8e8dd11e10851fab92e48b9d3440deee2c58714
parent2e43f76937240083033be0357e7cf25b234a7ce0 (diff)
downloadapi-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.go55
-rw-r--r--http.go37
-rw-r--r--logging.go23
-rw-r--r--main.go22
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())
+}