From 0ae5afa452bf71cb8d9d3a5fb61a3590e6b9a4a6 Mon Sep 17 00:00:00 2001 From: dtookey Date: Tue, 1 Aug 2023 12:53:09 -0400 Subject: [PATCH] added readme --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++ servlet/Router.go | 21 +++++++++++++++--- servlet/config.go | 3 ++- servlet/server.go | 38 +++++++++++++++++++++++++++++---- servlet/server_test.go | 6 +++--- vinegarUtil/webLRU.go | 3 +-- 6 files changed, 106 insertions(+), 13 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..61e7373 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# Vinegar + +### Vinegar is a simple web application framework written in Go. + +## Features +Routing with support for static files, templates, APIs +Template rendering +Caching +Gzip compression +Custom error pages +Getting Started +Prerequisites + +Go 1.18+ +### Installation +```go get geniuscartel.xyz/vinegar``` + +### Usage + +Create a config.json file to define routes and options + +### Import Vinegar and create a new server instance: +``` +import "geniuscartel.xyz/vinegar" + +func main(){ + server := vinegar.NewServer(config) + Add handlers for API routes: + api := server.NewApiRoute("/api") + api.Get(handleGet) + Start the server: + server.Start(":8080") +} +``` + +See the examples folder for more usage examples. + +## Configuration +The config.json file defines the routes and options for Vinegar. It supports the following route types: + +* static - Map a URL pattern to a static file or directory +* template - Render a Go template on a route +* api - Add API routes with custom handlers +* See config_example.json for a sample configuration file. + + +## License +Vinegar is closed source. Copyright 2023 David Tookey. All rights reserved \ No newline at end of file diff --git a/servlet/Router.go b/servlet/Router.go index b51c58b..05855c0 100644 --- a/servlet/Router.go +++ b/servlet/Router.go @@ -1,8 +1,10 @@ package servlet import ( + "encoding/json" "errors" "geniuscartel.xyz/vinegar/vinegarUtil" + "log" "net/http" "regexp" ) @@ -28,12 +30,25 @@ func (r *VinegarWebRouter) RouteRequest(w http.ResponseWriter, req *http.Request path := req.URL.Path for _, route := range r.Routes { if route.Pattern.MatchString(path) { - //fmt.Printf("SERVING: [%s]=>{%s}\n", path, route.Pattern.String()) + log.Printf("SERVING: [%s]=>{%s}\n", path, route.Pattern.String()) go route.Handler(w, req) return nil - } } - + notFoundHandler(w, req) return errors.New("failed to match route for [" + path + "]") } + +func notFoundHandler(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotFound) + + errorResponse := ErrorResponse{ + Code: http.StatusNotFound, + Message: "The requested resource could not be found.", + } + + jsonBytes, _ := json.Marshal(errorResponse) + + w.Header().Add("Content-Type", "application/json") + w.Write(jsonBytes) +} diff --git a/servlet/config.go b/servlet/config.go index 82dc5f5..53f89c3 100644 --- a/servlet/config.go +++ b/servlet/config.go @@ -6,6 +6,7 @@ import ( "fmt" "geniuscartel.xyz/vinegar/vinegarUtil" "io/ioutil" + "log" ) type ( @@ -150,7 +151,7 @@ func GenerateBlankConfig() error { if err != nil { return err } - fmt.Println("Generating a blank configuration file at ") + log.Println("Generating a blank configuration file at ") ioutil.WriteFile(defaultTemplateFileName, content, 0755) return nil } diff --git a/servlet/server.go b/servlet/server.go index 6209b55..dbe4207 100644 --- a/servlet/server.go +++ b/servlet/server.go @@ -26,8 +26,32 @@ type ErrorResponse struct { type ( VinegarWebServlet struct { - Port string - Router VinegarWebRouter + Port string + Router VinegarWebRouter + + // ErrorRoutes is a map that stores TemplateRoute pointers + // for different HTTP status code error pages. + // + // It gets populated when custom error routes are defined via AddErrorRoute. + // For example: + // + // err404 := NewTemplateRoute(/*...*/) + // srv.AddErrorRoute(404, err404) + // + // This will add err404 to ErrorRoutes mapped to 404. + // + // Routes can then be rendered like: + // + // tmpl, exists := s.ErrorRoutes[404] + // if exists { + // // render tmpl + // } + // + // So ErrorRoutes provides a lookup from status code + // to the associated custom error route. + // + // It allows easily rendering custom error pages + // for different status codes. ErrorRoutes map[int]*TemplateRoute interrupts chan struct{} errors chan error @@ -107,9 +131,10 @@ func (r *VinegarWebRoute) Announce() { } func (s *VinegarWebServlet) SendError(w http.ResponseWriter, req *http.Request, code int, msg string, aErr error) { - fmt.Printf("[%d][%s]. Rendering template for code %d with message: %s\n", code, req.URL.Path, code, msg) + log.Printf("[%d][%s]. Rendering template for code %d with message: %s\n", code, req.URL.Path, code, msg) out, _ := json.Marshal(aErr) - fmt.Println(string(out)) + + log.Println(string(out)) tmpl, exists := s.ErrorRoutes[code] if exists { err := tmpl.TemplateManager.AddMixin("code", strconv.Itoa(code)) @@ -124,6 +149,11 @@ func (s *VinegarWebServlet) SendError(w http.ResponseWriter, req *http.Request, } w.WriteHeader(code) bitties, err := tmpl.TemplateManager.RenderTemplate(fmt.Sprintf("%d.html", code)) + if err != nil { + http.Error(w, msg, code) + return + } + _, err = w.Write([]byte(bitties)) if err != nil { http.Error(w, msg, code) diff --git a/servlet/server_test.go b/servlet/server_test.go index a58891f..4e62691 100644 --- a/servlet/server_test.go +++ b/servlet/server_test.go @@ -1,7 +1,6 @@ package servlet import ( - "fmt" "io" "log" "net/http" @@ -38,10 +37,11 @@ func TestServletServeTraffic(t *testing.T) { srv.Router.AddRoute(&VinegarWebRoute{ Pattern: regexp.MustCompile("/test"), Handler: func(w http.ResponseWriter, r *http.Request) { - fmt.Printf("We got into the handler\n") + log.Printf("We got into the handler\n") msg := []byte("hello") l, err := w.Write(msg) - fmt.Printf("Wrote %d bytes\n", l) + + log.Printf("Wrote %d bytes\n", l) if err != nil { srv.errors <- err } diff --git a/vinegarUtil/webLRU.go b/vinegarUtil/webLRU.go index 6c714a5..1bed315 100644 --- a/vinegarUtil/webLRU.go +++ b/vinegarUtil/webLRU.go @@ -1,7 +1,6 @@ package vinegarUtil import ( - "fmt" "log" "strings" "time" @@ -61,7 +60,7 @@ func NewSingleFileCache(pathlike string) (*SingleFileCache, error) { func (l *Lru) Get(key string) (*LruEntry, bool) { entry, exists := (*l.entries)[key] if exists && entry.expires.Before(time.Now()) { - fmt.Println("Cache miss due to expired content") + log.Println("Cache miss due to expired content") err := entry.Reload() if err != nil { return nil, false