diff --git a/.gitignore b/.gitignore index 08143ac6ce31bc0b5f50b8bb04bde7be06f9e52e..83db01e3cbca3a0a971c9ab520d34dfc581fb00a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ shared/pages/.update /.GOPATH /.cover /bin +.idea +.DS_Store \ No newline at end of file diff --git a/go.mod b/go.mod index 72b81a0bcd66fd131f573b13213f77a788fc522a..9392a3173dd7fdda9d626642b6d2d27faa87b966 100644 --- a/go.mod +++ b/go.mod @@ -58,6 +58,7 @@ require ( github.com/prometheus/procfs v0.7.3 // indirect github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a // indirect github.com/tj/assert v0.0.3 // indirect + gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230131155618-ff61380d99ac // indirect go.opencensus.io v0.23.0 // indirect golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a // indirect golang.org/x/text v0.3.8 // indirect @@ -67,4 +68,5 @@ require ( google.golang.org/grpc v1.40.0 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c // indirect + rogchap.com/v8go v0.7.0 // indirect ) diff --git a/go.sum b/go.sum index 594e26cfc5132d29bf9d8b437d0670909dfa7178..fb172d1629cfbe0cc0db40feea5bfc840d1d1614 100644 --- a/go.sum +++ b/go.sum @@ -303,6 +303,18 @@ gitlab.com/gitlab-org/go-mimedb v1.45.0 h1:PO8dx6HEWzPYU6MQTYnCbpQEJzhJLW/Bh43+2 gitlab.com/gitlab-org/go-mimedb v1.45.0/go.mod h1:wa9y/zOSFKmTXLyBs4clz2FNVhZQmmEQM9TxslPAjZ0= gitlab.com/gitlab-org/golang-archive-zip v0.1.1 h1:35k9giivbxwF03+8A05Cm8YoxoakU8FBCj5gysjCTCE= gitlab.com/gitlab-org/golang-archive-zip v0.1.1/go.mod h1:ZDtqpWPGPB9qBuZnZDrKQjIdJtkN7ZAoVwhT6H2o2kE= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230124085716-a5bdf3becb5d h1:YbHuaCMslxItOg4E3yE6HfkMN1HsBINgyeqrwsb710A= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230124085716-a5bdf3becb5d/go.mod h1:WCAxQX5/iOtlnBi9FqjOB/eW3sqm+bdEeogin53Q2Ow= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230125132234-ded6248903da h1:SPnfwCGVBG7MeWdlobTHoRS4Ue21K6xo55tgahqW4rg= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230125132234-ded6248903da/go.mod h1:WCAxQX5/iOtlnBi9FqjOB/eW3sqm+bdEeogin53Q2Ow= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230126142454-dcf3568027b2 h1:2X5eOnEujYhbfzPRaeIxuklDflsFqI4NJ2xP3SvunAA= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230126142454-dcf3568027b2/go.mod h1:WCAxQX5/iOtlnBi9FqjOB/eW3sqm+bdEeogin53Q2Ow= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230127133932-91d35df74096 h1:EqH9XQGIN74j2PPHqhzR0dtjUZVJMEvy3/yB44APLbA= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230127133932-91d35df74096/go.mod h1:WCAxQX5/iOtlnBi9FqjOB/eW3sqm+bdEeogin53Q2Ow= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230131141315-88146c73d8c3 h1:cAqY3IJTws6VdkWrzHFIEPfc90xGgQ6RZthrBLtYhq4= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230131141315-88146c73d8c3/go.mod h1:EWi+TfXFsmjxFNtg/Biydfv4o91g6oWpIGpW5FyUdSs= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230131155618-ff61380d99ac h1:isDB7iamCpwadQt4rtPi/vP6OhWTelQTJiQRhrL0pGk= +gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter v0.0.0-20230131155618-ff61380d99ac/go.mod h1:EWi+TfXFsmjxFNtg/Biydfv4o91g6oWpIGpW5FyUdSs= gitlab.com/gitlab-org/labkit v1.16.0 h1:Vm3NAMZ8RqAunXlvPWby3GJ2R35vsYGP6Uu0YjyMIlY= gitlab.com/gitlab-org/labkit v1.16.0/go.mod h1:bcxc4ZpAC+WyACgyKl7FcvT2XXAbl8CrzN6UY+w8cMc= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -701,6 +713,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rogchap.com/v8go v0.7.0 h1:kgjbiO4zE5itA962ze6Hqmbs4HgZbGzmueCXsZtremg= +rogchap.com/v8go v0.7.0/go.mod h1:MxgP3pL2MW4dpme/72QRs8sgNMmM0pRc8DPhcuLWPAs= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/serving/disk/reader.go b/internal/serving/disk/reader.go index f250a0d6764f46473d3369dbb3088816ca38412d..b3b2998be0bcb23ce293a246b1017f66df1ac4fe 100644 --- a/internal/serving/disk/reader.go +++ b/internal/serving/disk/reader.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + v8 "gitlab.com/gitlab-org/gitlab-pages/internal/v8" "io" "io/fs" "net/http" @@ -27,6 +28,7 @@ import ( type Reader struct { fileSizeMetric *prometheus.HistogramVec vfs vfs.VFS + scriptManager v8.ScriptManager } // Show the user some validation messages for their _redirects file @@ -68,6 +70,47 @@ func (reader *Reader) tryRedirects(h serving.Handler) bool { return true } +func (reader *Reader) tryRunScript(h serving.Handler) bool { + + filename := v8.ScriptFilename + ctx := h.Request.Context() + + root, served := reader.root(h) + if root == nil { + return served + } + + s := reader.scriptManager.RetrieveScript(h.LookupPath.Path) + + if s == nil { + fullPath, err := reader.resolvePath(ctx, root, filename) + + if err != nil { + // We assume that this is mostly missing file type of the error + // and additional handlers should try to process the request + return false + } + + f, err := root.Open(ctx, fullPath) + defer f.Close() + + contentType, err := reader.detectContentType(ctx, root, fullPath) + + if err != nil || contentType != "application/javascript" { + return false + } + + // TODO: is Path the best ID? + s, err = reader.scriptManager.LoadScript(h.LookupPath.Path, f) + + if err != nil { + return false + } + } + + return s.HandleRequest(h.Writer, h.Request) +} + // tryFile returns true if it successfully handled request func (reader *Reader) tryFile(h serving.Handler) bool { ctx := h.Request.Context() @@ -196,6 +239,17 @@ func (reader *Reader) resolvePath(ctx context.Context, root vfs.Root, subPath .. return fullPath, nil } +func (reader *Reader) readFile(ctx context.Context, root vfs.Root, origPath string, next func(f vfs.File)) error { + file, err := root.Open(ctx, origPath) + if err != nil { + return err + } + + next(file) + + return file.Close() +} + func (reader *Reader) serveFile(ctx context.Context, w http.ResponseWriter, r *http.Request, root vfs.Root, origPath, sha string, accessControl bool) bool { fullPath := reader.handleContentEncoding(ctx, w, r, root, origPath) diff --git a/internal/serving/disk/serving.go b/internal/serving/disk/serving.go index 635498aafe25b7068f99256e7414ea1f167b6f96..3599f3ac33340931f06bb529cec20f4532be4af3 100644 --- a/internal/serving/disk/serving.go +++ b/internal/serving/disk/serving.go @@ -4,6 +4,7 @@ import ( "gitlab.com/gitlab-org/gitlab-pages/internal/config" "gitlab.com/gitlab-org/gitlab-pages/internal/httperrors" "gitlab.com/gitlab-org/gitlab-pages/internal/serving" + v8 "gitlab.com/gitlab-org/gitlab-pages/internal/v8" "gitlab.com/gitlab-org/gitlab-pages/internal/vfs" "gitlab.com/gitlab-org/gitlab-pages/metrics" ) @@ -24,6 +25,10 @@ func (s *Disk) ServeFileHTTP(h serving.Handler) bool { return true } + if s.reader.tryRunScript(h) { + return true + } + return false } @@ -49,6 +54,7 @@ func New(vfs vfs.VFS) serving.Serving { reader: Reader{ fileSizeMetric: metrics.DiskServingFileSize, vfs: vfs, + scriptManager: v8.NewManager(), }, } } diff --git a/internal/v8/manager.go b/internal/v8/manager.go new file mode 100644 index 0000000000000000000000000000000000000000..8fa2cf50e7cb6de2bed6306665d7762a054d2976 --- /dev/null +++ b/internal/v8/manager.go @@ -0,0 +1,46 @@ +package v8 + +import ( + "gitlab.com/gitlab-org/incubation-engineering/jamstack/go-http-v8-adapter/script" + "io" +) + +type ScriptManager struct { + store map[string]*script.Script +} + +const ScriptFilename = "request.js" + +func (m *ScriptManager) LoadScript(id string, f io.Reader) (*script.Script, error) { + + s, _ := script.New(ScriptFilename) + + err := s.LoadScriptData(f) + + if err != nil { + return nil, err + } + + s.CreateIsolate() + err = s.Compile() + + m.store[id] = s + + return s, nil +} + +func (m *ScriptManager) RetrieveScript(id string) *script.Script { + s, exists := m.store[id] + + if !exists { + return nil + } + + return s +} + +func NewManager() ScriptManager { + return ScriptManager{ + store: make(map[string]*script.Script), + } +}