From cd66946020d9fedce0b886557f97499e5bb4e1d8 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 18 Nov 2023 01:42:04 +0800 Subject: [PATCH] :sparkles: Static Files Hosting --- README.md | 8 +- config/example.json | 8 +- pkg/cmd/main.go | 8 +- pkg/configurator/router.go | 41 ---------- pkg/hypertext/proxies.go | 53 ++++++------- .../main.go => sign/configurator.go} | 2 +- pkg/{configurator => sign}/deserializer.go | 2 +- pkg/sign/router.go | 78 +++++++++++++++++++ pkg/{configurator => sign}/transformer.go | 2 +- pkg/{configurator => sign}/upstream.go | 11 ++- 10 files changed, 126 insertions(+), 87 deletions(-) delete mode 100644 pkg/configurator/router.go rename pkg/{configurator/main.go => sign/configurator.go} (98%) rename pkg/{configurator => sign}/deserializer.go (88%) create mode 100644 pkg/sign/router.go rename pkg/{configurator => sign}/transformer.go (98%) rename pkg/{configurator => sign}/upstream.go (81%) diff --git a/README.md b/README.md index e27a8f7..affc81a 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,12 @@ A blazing fast reverse proxy with a lot of shining features. 1. Reverse proxy 2. Static file hosting -3. Analytics and Metrics +3. ~~Analytics and Metrics~~ 4. Integrate with CI/CD -5. Webhook integration -6. Web management panel +5. ~~Webhook integration~~ +6. ~~Web management panel~~ 7. **Blazing fast ⚡** +> Deleted item means under construction, check out our roadmap! + ### How fast is it? \ No newline at end of file diff --git a/config/example.json b/config/example.json index 6a8a34c..042c612 100644 --- a/config/example.json +++ b/config/example.json @@ -6,22 +6,22 @@ "localhost" ], "path": [ - "/" + "/site1" ] } ], "upstreams": [ { "name": "Example Upstream", - "uri": "https://disk.smartsheep.studio" + "uri": "file:///site" } ], "transformers": [ { "type": "replacePath", "options": { - "pattern": "/p/123", - "value": "/p/%E5%B7%A5%E4%BD%9C%E5%AE%A4/icon.png" + "pattern": "/site1", + "value": "/" } } ] diff --git a/pkg/cmd/main.go b/pkg/cmd/main.go index 738aa4f..1b7d43e 100644 --- a/pkg/cmd/main.go +++ b/pkg/cmd/main.go @@ -3,8 +3,8 @@ package main import ( roadsign "code.smartsheep.studio/goatworks/roadsign/pkg" "code.smartsheep.studio/goatworks/roadsign/pkg/administration" - "code.smartsheep.studio/goatworks/roadsign/pkg/configurator" "code.smartsheep.studio/goatworks/roadsign/pkg/hypertext" + "code.smartsheep.studio/goatworks/roadsign/pkg/sign" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/spf13/viper" @@ -30,11 +30,11 @@ func main() { log.Panic().Err(err).Msg("An error occurred when loading settings.") } - // Load configurations - if err := configurator.ReadInConfig(viper.GetString("paths.configs")); err != nil { + // Load & init sign + if err := sign.ReadInConfig(viper.GetString("paths.configs")); err != nil { log.Panic().Err(err).Msg("An error occurred when loading configurations.") } else { - log.Debug().Any("sites", configurator.C).Msg("All configuration has been loaded.") + log.Debug().Any("sites", sign.C).Msg("All configuration has been loaded.") } // Init hypertext server diff --git a/pkg/configurator/router.go b/pkg/configurator/router.go deleted file mode 100644 index 754089a..0000000 --- a/pkg/configurator/router.go +++ /dev/null @@ -1,41 +0,0 @@ -package configurator - -import ( - "math/rand" -) - -type AppConfig struct { - Sites []SiteConfig `json:"sites"` -} - -func (v *AppConfig) LoadBalance(site SiteConfig) *UpstreamConfig { - idx := rand.Intn(len(site.Upstreams)) - - switch site.Upstreams[idx].GetType() { - case UpstreamTypeHypertext: - return &site.Upstreams[idx] - case UpstreamTypeFile: - // TODO Make this into hypertext configuration - return &site.Upstreams[idx] - default: - // Give him the null value when this configuration is invalid. - // Then we can print a log in the console to warm him. - return nil - } -} - -type SiteConfig struct { - ID string `json:"id"` - - Name string `json:"name"` - Rules []RouterRuleConfig `json:"rules"` - Transformers []RequestTransformerConfig `json:"transformers"` - Upstreams []UpstreamConfig `json:"upstreams"` -} - -type RouterRuleConfig struct { - Host []string `json:"host"` - Path []string `json:"path"` - Queries map[string]string `json:"query"` - Headers map[string][]string `json:"headers"` -} diff --git a/pkg/hypertext/proxies.go b/pkg/hypertext/proxies.go index 1d6583c..2cb1141 100644 --- a/pkg/hypertext/proxies.go +++ b/pkg/hypertext/proxies.go @@ -1,15 +1,10 @@ package hypertext import ( - "code.smartsheep.studio/goatworks/roadsign/pkg/configurator" + "code.smartsheep.studio/goatworks/roadsign/pkg/sign" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/proxy" - "github.com/rs/zerolog/log" "github.com/samber/lo" - "github.com/spf13/viper" - "github.com/valyala/fasthttp" "regexp" - "time" ) func UseProxies(app *fiber.App) { @@ -20,21 +15,28 @@ func UseProxies(app *fiber.App) { headers := ctx.GetReqHeaders() // Filtering sites - for _, site := range configurator.C.Sites { + for _, site := range sign.C.Sites { // Matching rules for _, rule := range site.Rules { if !lo.Contains(rule.Host, host) { continue - } else if !lo.ContainsBy(rule.Path, func(item string) bool { - matched, err := regexp.MatchString(item, path) - return matched && err == nil - }) { + } + + if !func() bool { + flag := false + for _, pattern := range rule.Path { + if ok, _ := regexp.MatchString(pattern, path); ok { + flag = true + break + } + } + return flag + }() { continue } - flag := true - // Filter query strings + flag := true for rk, rv := range rule.Queries { for ik, iv := range queries { if rk != ik && rv != iv { @@ -72,11 +74,11 @@ func UseProxies(app *fiber.App) { if !flag { continue } - } - // Passing all the rules means the site is what we are looking for. - // Let us respond to our client! - return makeResponse(ctx, site) + // Passing all the rules means the site is what we are looking for. + // Let us respond to our client! + return makeResponse(ctx, site) + } } // There is no site available for this request. @@ -86,25 +88,14 @@ func UseProxies(app *fiber.App) { }) } -func makeResponse(ctx *fiber.Ctx, site configurator.SiteConfig) error { - // Load balance - upstream := configurator.C.LoadBalance(site) - if upstream == nil { - log.Warn().Str("id", site.ID).Msg("There is no available upstream for this request.") - return fiber.ErrBadGateway - } - +func makeResponse(ctx *fiber.Ctx, site sign.SiteConfig) error { // Modify request for _, transformer := range site.Transformers { transformer.TransformRequest(ctx) } - // Perform forward - timeout := time.Duration(viper.GetInt64("performance.network_timeout")) * time.Millisecond - err := proxy.Do(ctx, upstream.MakeURI(ctx), &fasthttp.Client{ - ReadTimeout: timeout, - WriteTimeout: timeout, - }) + // Forward + err := sign.C.Forward(ctx, site) // Modify response for _, transformer := range site.Transformers { diff --git a/pkg/configurator/main.go b/pkg/sign/configurator.go similarity index 98% rename from pkg/configurator/main.go rename to pkg/sign/configurator.go index 6378bde..36ef9ef 100644 --- a/pkg/configurator/main.go +++ b/pkg/sign/configurator.go @@ -1,4 +1,4 @@ -package configurator +package sign import ( "encoding/json" diff --git a/pkg/configurator/deserializer.go b/pkg/sign/deserializer.go similarity index 88% rename from pkg/configurator/deserializer.go rename to pkg/sign/deserializer.go index 66fd911..a0f0284 100644 --- a/pkg/configurator/deserializer.go +++ b/pkg/sign/deserializer.go @@ -1,4 +1,4 @@ -package configurator +package sign import "encoding/json" diff --git a/pkg/sign/router.go b/pkg/sign/router.go new file mode 100644 index 0000000..3694d7d --- /dev/null +++ b/pkg/sign/router.go @@ -0,0 +1,78 @@ +package sign + +import ( + "errors" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/filesystem" + "github.com/gofiber/fiber/v2/middleware/proxy" + "github.com/samber/lo" + "github.com/spf13/viper" + "github.com/valyala/fasthttp" + "math/rand" + "net/http" + "strconv" + "time" +) + +type AppConfig struct { + Sites []SiteConfig `json:"sites"` +} + +func (v *AppConfig) Forward(ctx *fiber.Ctx, site SiteConfig) error { + if len(site.Upstreams) == 0 { + return errors.New("invalid configuration") + } + + idx := rand.Intn(len(site.Upstreams)) + upstream := &site.Upstreams[idx] + + switch upstream.GetType() { + case UpstreamTypeHypertext: + timeout := time.Duration(viper.GetInt64("performance.network_timeout")) * time.Millisecond + return proxy.Do(ctx, upstream.MakeURI(ctx), &fasthttp.Client{ + ReadTimeout: timeout, + WriteTimeout: timeout, + }) + case UpstreamTypeFile: + uri, queries := upstream.GetRawURI() + fs := filesystem.New(filesystem.Config{ + Root: http.Dir(uri), + ContentTypeCharset: queries.Get("charset"), + Index: func() string { + val := queries.Get("index") + return lo.Ternary(len(val) > 0, val, "index.html") + }(), + NotFoundFile: func() string { + val := queries.Get("fallback") + return lo.Ternary(len(val) > 0, val, "404.html") + }(), + Browse: func() bool { + browse, err := strconv.ParseBool(queries.Get("browse")) + return lo.Ternary(err == nil, browse, false) + }(), + MaxAge: func() int { + age, err := strconv.Atoi(queries.Get("maxAge")) + return lo.Ternary(err == nil, age, 3600) + }(), + }) + return fs(ctx) + default: + return fiber.ErrBadGateway + } +} + +type SiteConfig struct { + ID string `json:"id"` + + Name string `json:"name"` + Rules []RouterRuleConfig `json:"rules"` + Transformers []RequestTransformerConfig `json:"transformers"` + Upstreams []UpstreamConfig `json:"upstreams"` +} + +type RouterRuleConfig struct { + Host []string `json:"host"` + Path []string `json:"path"` + Queries map[string]string `json:"query"` + Headers map[string][]string `json:"headers"` +} diff --git a/pkg/configurator/transformer.go b/pkg/sign/transformer.go similarity index 98% rename from pkg/configurator/transformer.go rename to pkg/sign/transformer.go index 461f746..2ba9afb 100644 --- a/pkg/configurator/transformer.go +++ b/pkg/sign/transformer.go @@ -1,4 +1,4 @@ -package configurator +package sign import ( "github.com/gofiber/fiber/v2" diff --git a/pkg/configurator/upstream.go b/pkg/sign/upstream.go similarity index 81% rename from pkg/configurator/upstream.go rename to pkg/sign/upstream.go index f7ba5d0..2abf968 100644 --- a/pkg/configurator/upstream.go +++ b/pkg/sign/upstream.go @@ -1,9 +1,10 @@ -package configurator +package sign import ( "fmt" "github.com/gofiber/fiber/v2" "github.com/samber/lo" + "net/url" "strings" ) @@ -31,6 +32,14 @@ func (v *UpstreamConfig) GetType() string { return UpstreamTypeUnknown } +func (v *UpstreamConfig) GetRawURI() (string, url.Values) { + uri := strings.SplitN(v.URI, "://", 2)[1] + data := strings.SplitN(uri, "?", 2) + qs, _ := url.ParseQuery(uri) + + return data[0], qs +} + func (v *UpstreamConfig) MakeURI(ctx *fiber.Ctx) string { var queries []string for k, v := range ctx.Queries() {