commit 1f679f4c0696b14760a39efabf77c8a0012906c8 Author: Bel LaPointe Date: Thu Mar 14 14:43:02 2019 -0600 Initial dumb servers diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec31065 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +.*.go +vendor +*.swp +*.swo +testdata +server/server +client/client +*.db +*.ldb +*.log +*.bak +CURRENT +LOCK +LOG +MANIFEST-* diff --git a/server/config/config.go b/server/config/config.go new file mode 100644 index 0000000..c320baf --- /dev/null +++ b/server/config/config.go @@ -0,0 +1,34 @@ +package config + +import ( + "fmt" + "local/storage" + "sync" +) + +var config Config +var lock = &sync.RWMutex{} + +type Config struct { + db string + DB storage.DB + Port string + Addr string + Username string + Password string +} + +func Values() Config { + lock.RLock() + defer lock.RUnlock() + return config +} + +func (c Config) String() string { + return fmt.Sprintf( + "port:%v db:%v addr:%v user:*** pass:***", + c.Port, + c.db, + c.Addr, + ) +} diff --git a/server/config/new.go b/server/config/new.go new file mode 100644 index 0000000..a8734e7 --- /dev/null +++ b/server/config/new.go @@ -0,0 +1,28 @@ +package config + +import ( + "local/storage" + "os" +) + +func New() error { + config = Config{ + db: orEnv(storage.MAP.String(), "DB", "DATABASE"), + Addr: orEnv("", "ADDR", "FILE"), + Username: orEnv("", "USER", "USERNAME"), + Password: orEnv("", "PASS", "PASSWORD"), + Port: orEnv("21412", "PORT", "LISTEN"), + } + DB, err := storage.New(storage.TypeFromString(config.db), config.Addr, config.Username, config.Password) + config.DB = DB + return err +} + +func orEnv(def, key string, keys ...string) string { + for _, key := range append(keys, key) { + if v, ok := os.LookupEnv(key); ok { + return v + } + } + return def +} diff --git a/server/config/new_test.go b/server/config/new_test.go new file mode 100644 index 0000000..f4d7baa --- /dev/null +++ b/server/config/new_test.go @@ -0,0 +1,37 @@ +package config + +import ( + "os" + "testing" +) + +func TestNew(t *testing.T) { + invalidEnv := "&&&&&&" + keys := []string{ + "DB", + "DATABASE", + "ADDR", + "PATH", + "USER", + "USERNAME", + "PASS", + "PASSWORD", + } + was := make(map[string]string) + for _, k := range keys { + v, ok := os.LookupEnv(k) + if !ok { + v = invalidEnv + } + was[k] = v + } + defer func() { + for k, v := range was { + if v != invalidEnv { + os.Setenv(k, v) + } else { + os.Unsetenv(k) + } + } + }() +} diff --git a/server/main.go b/server/main.go new file mode 100644 index 0000000..b79600d --- /dev/null +++ b/server/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "local/dynamodb/server/config" + "local/dynamodb/server/serve" + "log" +) + +func main() { + if err := config.New(); err != nil { + panic(err) + } + + log.Printf("config: %s", config.Values().String()) + + server := serve.New() + if err := server.Routes(); err != nil { + panic(err) + } + + if err := server.Run(); err != nil { + panic(err) + } +} diff --git a/server/serve/new.go b/server/serve/new.go new file mode 100644 index 0000000..12d6446 --- /dev/null +++ b/server/serve/new.go @@ -0,0 +1,20 @@ +package serve + +import ( + "local/dynamodb/server/config" + "local/s2sa/s2sa/server/router" + "strings" +) + +type Server struct { + port string + router *router.Router +} + +func New() *Server { + config := config.Values() + return &Server{ + port: ":" + strings.TrimPrefix(config.Port, ":"), + router: router.New(), + } +} diff --git a/server/serve/new_test.go b/server/serve/new_test.go new file mode 100644 index 0000000..e1a708f --- /dev/null +++ b/server/serve/new_test.go @@ -0,0 +1,11 @@ +package serve + +import ( + "os" + "testing" +) + +func TestNew(t *testing.T) { + os.Setenv("DB", "MAP") + New() +} diff --git a/server/serve/routes.go b/server/serve/routes.go new file mode 100644 index 0000000..a91d7cc --- /dev/null +++ b/server/serve/routes.go @@ -0,0 +1,62 @@ +package serve + +import ( + "io/ioutil" + "local/dynamodb/server/config" + "local/s2sa/s2sa/server/router" + "local/storage" + "net/http" + "strings" +) + +func (s *Server) Routes() error { + if err := s.router.Add("/"+router.Wildcard, s.CatchAll); err != nil { + return err + } + return nil +} + +func (s *Server) CatchAll(w http.ResponseWriter, r *http.Request) { + foo := http.NotFound + switch strings.ToLower(r.Method) { + case "get": + foo = s.get + case "put": + foo = s.put + case "delete": + foo = s.put + } + foo(w, r) +} + +func (s *Server) get(w http.ResponseWriter, r *http.Request) { + config := config.Values() + key := strings.Split(r.URL.Path, "/")[1] + if v, err := config.DB.Get(key); err == storage.ErrNotFound { + w.WriteHeader(http.StatusNotFound) + return + } else if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } else { + w.Write(v) + } +} + +func (s *Server) put(w http.ResponseWriter, r *http.Request) { + config := config.Values() + key := strings.Split(r.URL.Path, "/")[1] + if r == nil || r.Body == nil { + w.WriteHeader(http.StatusBadRequest) + return + } + value, err := ioutil.ReadAll(r.Body) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + if err := config.DB.Set(key, value); err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } +} diff --git a/server/serve/routes_test.go b/server/serve/routes_test.go new file mode 100644 index 0000000..bd10975 --- /dev/null +++ b/server/serve/routes_test.go @@ -0,0 +1,137 @@ +package serve + +import ( + "io/ioutil" + "local/dynamodb/server/config" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" +) + +var validKey = "key" +var validValue = "value" + +func TestRoutes(t *testing.T) { + os.Setenv("DB", "MAP") + config.New() + s := New() + if err := s.Routes(); err != nil { + t.Fatalf("cannot routes(): %v", err) + } + config.Values().DB.Set(validKey, []byte("anything")) + + cases := []struct { + path string + method string + status int + }{ + { + path: "/" + validKey, + method: "GET", + status: http.StatusOK, + }, + { + path: "/" + validKey, + method: "PUT", + status: http.StatusOK, + }, + { + path: "/" + validKey, + method: "DELETE", + status: http.StatusOK, + }, + { + path: "/" + validKey, + method: "POST", + status: http.StatusNotFound, + }, + { + path: "/", + method: "GET", + status: http.StatusNotFound, + }, + { + path: "/", + method: "PUT", + status: http.StatusNotFound, + }, + { + path: "/", + method: "DELETE", + status: http.StatusNotFound, + }, + { + path: "/", + method: "POST", + status: http.StatusNotFound, + }, + { + path: "", + method: "GET", + status: http.StatusNotFound, + }, + { + path: "", + method: "PUT", + status: http.StatusNotFound, + }, + { + path: "", + method: "DELETE", + status: http.StatusNotFound, + }, + { + path: "", + method: "POST", + status: http.StatusNotFound, + }, + } + + for _, c := range cases { + //t.Logf("CASE %v", c) + r, err := http.NewRequest(c.method, c.path, strings.NewReader(validValue)) + if err != nil { + t.Fatalf("err making request: %v", err) + } + w := httptest.NewRecorder() + s.ServeHTTP(w, r) + if w.Code != c.status { + t.Errorf("wrong status for %q on %q: %v, want %v", c.method, c.path, w.Code, c.status) + } + } +} + +func TestPutGet(t *testing.T) { + os.Setenv("DB", "MAP") + config.New() + s := New() + if err := s.Routes(); err != nil { + t.Errorf("cannot routes(): %v", err) + } + + w := httptest.NewRecorder() + r, err := http.NewRequest("PUT", "/"+validKey, strings.NewReader(validValue)) + if err != nil { + t.Fatalf("err making put request: %v", err) + } + s.put(w, r) + if w.Code != http.StatusOK { + t.Fatalf("err status on put: %v", w.Code) + } + + w = httptest.NewRecorder() + r, err = http.NewRequest("GET", "/"+validKey, nil) + if err != nil { + t.Fatalf("err making get request: %v", err) + } + s.get(w, r) + if w.Code != http.StatusOK { + t.Fatalf("err status on get: %v", w.Code) + } else if v, err := ioutil.ReadAll(w.Body); err != nil { + t.Fatalf("err reading response body on get: %v", err) + } else if string(v) != validValue { + t.Fatalf("wrong response body on get: %v", string(v)) + } +} diff --git a/server/serve/server.go b/server/serve/server.go new file mode 100644 index 0000000..7e2a66a --- /dev/null +++ b/server/serve/server.go @@ -0,0 +1,13 @@ +package serve + +import ( + "net/http" +) + +func (s *Server) Run() error { + return http.ListenAndServe(s.port, s) +} + +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.router.ServeHTTP(w, r) +}