commit 4a0a8d486973849139edb8f9d367c3727934ed61 Author: Gianni Carafa Date: Wed Nov 1 22:11:27 2023 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a86d8b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# dev binary +ladder \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5e32c6a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,42 @@ +# Building the binary of the App +FROM golang:1.21 AS build + +WORKDIR /go/src/ladder + +# Copy all the Code and stuff to compile everything +COPY . . + +# Downloads all the dependencies in advance (could be left out, but it's more clear this way) +RUN go mod download + +# Builds the application as a staticly linked one, to allow it to run on alpine +#RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o ladder cmd/main.go +#RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o ladder cmd/main.go +#RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -a -o ladder cmd/main.go +#RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags netgo -a -installsuffix cgo -o ladder cmd/main.go +#RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags netgo -a -o ladder cmd/main.go +RUN CGO_ENABLED=0 GOOS=linux go build -o ladder cmd/main.go + + +# Moving the binary to the 'final Image' to make it smaller +FROM debian:12-slim as release +#FROM debian:latest as release +#FROM ubuntu:latest as release +#FROM golang:bookworm as release + +WORKDIR /app + +# Create the `public` dir and copy all the assets into it +RUN mkdir ./public +COPY ./public ./public + +COPY --from=build /go/src/ladder/ladder . +RUN chmod +x /app/ladder + +RUN apt update && apt install -y ca-certificates && rm -rf /var/lib/apt/lists/* + +# Exposes port 2000 and 3000 because our program listens on that port +#EXPOSE 2000 +#EXPOSE 3000 + +#ENTRYPOINT ["/usr/bin/dumb-init", "--"] \ No newline at end of file diff --git a/Dockerfile.alpine b/Dockerfile.alpine new file mode 100644 index 0000000..f2faf91 --- /dev/null +++ b/Dockerfile.alpine @@ -0,0 +1,37 @@ +# Building the binary of the App +FROM golang:1.21 AS build + +WORKDIR /go/src/ladder + +# Copy all the Code and stuff to compile everything +COPY . . + +# Downloads all the dependencies in advance (could be left out, but it's more clear this way) +RUN go mod download + +# Builds the application as a staticly linked one, to allow it to run on alpine +#RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o ladder cmd/main.go +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags netgo -a -installsuffix cgo -o ladder cmd/main.go + + +# Moving the binary to the 'final Image' to make it smaller +FROM alpine:latest as release + +WORKDIR /app + +# Create the `public` dir and copy all the assets into it +RUN mkdir ./public +COPY ./public ./public + +COPY --from=build /go/src/ladder/ladder . + +# Add packages +RUN apk -U upgrade \ + && apk add --no-cache dumb-init ca-certificates \ + && chmod +x /app/ladder + +# Exposes port 2000 and 3000 because our program listens on that port +#EXPOSE 2000 +#EXPOSE 3000 + +#ENTRYPOINT ["/usr/bin/dumb-init", "--"] \ No newline at end of file diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 0000000..ddfd7d8 --- /dev/null +++ b/Dockerfile.test @@ -0,0 +1,46 @@ +# Building the binary of the App +FROM golang:bookworm AS build + +WORKDIR /go/src/ladder + +# Copy all the Code and stuff to compile everything +COPY . . + +# Downloads all the dependencies in advance (could be left out, but it's more clear this way) +RUN go mod download + +# Builds the application as a staticly linked one, to allow it to run on alpine +#RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o ladder cmd/main.go +#RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o ladder cmd/main.go +#RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -a -o ladder cmd/main.go +#RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags netgo -a -installsuffix cgo -o ladder cmd/main.go +#RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -tags netgo -a -o ladder cmd/main.go +RUN CGO_ENABLED=0 GOOS=linux go build -o ladder cmd/main.go + + +# Moving the binary to the 'final Image' to make it smaller +FROM debian:12-slim as release +#FROM debian:latest as release +#FROM ubuntu:latest as release +#FROM golang:bookworm as release + +WORKDIR /app + +RUN apt update && apt install -y ca-certificates + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o ladder cmd/main.go + +# Create the `public` dir and copy all the assets into it +RUN mkdir ./public +COPY ./public ./public + +COPY --from=build /go/src/ladder/ladder . +RUN chmod +x /app/ladder + +RUN rm -rf /var/lib/apt/lists/* + +# Exposes port 2000 and 3000 because our program listens on that port +#EXPOSE 2000 +#EXPOSE 3000 + +#ENTRYPOINT ["/usr/bin/dumb-init", "--"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2028d26 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# paywall-ladder diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..8a2fae1 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" + "paywall-ladder/handlers" + "syscall" + + "github.com/elazarl/goproxy" + "github.com/gofiber/fiber/v2" +) + +func main() { + + //os.Setenv("HTTP_PROXY", "http://localhost:3000") + proxyForkID, _, _ := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0) // LINUX + //_, proxyForkID, _ := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0) // MAC + + fmt.Println("Proxy fork id", proxyForkID) + + if proxyForkID == 0 { + + app := fiber.New() + app.Static("/", "./public") + + app.Get("/proxy/*", handlers.FetchSite) + app.Get("debug/*", handlers.Debug) + app.Get("/*", handlers.ProxySite) + + port := os.Getenv("PORT") + if os.Getenv("PORT") == "" { + port = "2000" + } + app.Listen(":" + port) + } else { + proxy := goproxy.NewProxyHttpServer() + proxy.Verbose = true + proxy.OnRequest().DoFunc( + func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { + req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)") + req.Header.Set("X-Forwarded-For", "66.249.66.1") + return req, nil + }) + + proxyport := os.Getenv("PORT_PROXY") + if os.Getenv("PORT_PROXY") == "" { + proxyport = "3000" + } + log.Println("Proxy listening on port " + proxyport) + log.Fatal(http.ListenAndServe(":"+proxyport, proxy)) + } + +} diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..b5455c2 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,15 @@ +version: '3' +services: + ladder: + build: . + container_name: ladder + #restart: always + command: tail -f /dev/null + #command: ./ladder + environment: + - PORT=2000 + - PORT_PROXY=3000 + - GODEBUG=netdns=go+4 + ports: + - "2000:2000" + - "8080:3000" \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d445000 --- /dev/null +++ b/go.mod @@ -0,0 +1,29 @@ +module paywall-ladder + +go 1.21.1 + +require ( + github.com/elazarl/goproxy v0.0.0-20231031074852-3ec07828be7a + github.com/gojek/heimdall/v7 v7.0.2 +) + +require ( + github.com/andybalholm/brotli v1.0.6 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gofiber/fiber/v2 v2.50.0 // indirect + github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/klauspost/compress v1.17.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/stretchr/objx v0.3.0 // indirect + github.com/stretchr/testify v1.3.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.50.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/sys v0.13.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bc6b56b --- /dev/null +++ b/go.sum @@ -0,0 +1,61 @@ +github.com/DataDog/datadog-go v3.7.1+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/elazarl/goproxy v0.0.0-20231031074852-3ec07828be7a h1:r72lWG/xCv9MLpRTss5BQVHDURXaaD6OwS2HkI5/+Ls= +github.com/elazarl/goproxy v0.0.0-20231031074852-3ec07828be7a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= +github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw= +github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw= +github.com/gojek/heimdall/v7 v7.0.2 h1:+YutGXZ8oEWbCJIwjRnkKmoTl+Oxt1Urs3hc/FR0sxU= +github.com/gojek/heimdall/v7 v7.0.2/go.mod h1:Z43HtMid7ysSjmsedPTXAki6jcdcNVnjn5pmsTyiMic= +github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf h1:5xRGbUdOmZKoDXkGx5evVLehuCMpuO1hl701bEQqXOM= +github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf/go.mod h1:QzhUKaYKJmcbTnCYCAVQrroCOY7vOOI8cSQ4NbuhYf0= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= +github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= +github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/handlers/debug.go b/handlers/debug.go new file mode 100644 index 0000000..77b457c --- /dev/null +++ b/handlers/debug.go @@ -0,0 +1,38 @@ +package handlers + +import ( + "io/ioutil" + "net/http" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/gojek/heimdall/v7/httpclient" +) + +func Debug(c *fiber.Ctx) error { + //url := c.Params("*") + + timeout := 1000 * time.Millisecond + client := httpclient.NewClient(httpclient.WithHTTPTimeout(timeout)) + + headers := http.Header{} + headers.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)") + headers.Set("X-Forwarded-For", "66.249.66.1") + res, err := client.Get("http://google.com", headers) + if err != nil { + panic(err) + } + + // Heimdall returns the standard *http.Response object + body, err := ioutil.ReadAll(res.Body) + //fmt.Println(string(body)) + /* + resp, err := http.Get(url) + if err != nil { + log.Fatal(err) + } + defer resp.Body.Close() + + */ + return c.SendString(string(body)) +} diff --git a/handlers/proxy.go b/handlers/proxy.go new file mode 100644 index 0000000..549760e --- /dev/null +++ b/handlers/proxy.go @@ -0,0 +1,175 @@ +package handlers + +import ( + "fmt" + "io" + "log" + "net/http" + "net/http/httputil" + "net/url" + "regexp" + "strings" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/gojek/heimdall/v7/httpclient" +) + +type loggingTransport struct{} + +func (s *loggingTransport) RoundTrip(r *http.Request) (*http.Response, error) { + + bytes, _ := httputil.DumpRequestOut(r, true) + + resp, err := http.DefaultTransport.RoundTrip(r) + // err is returned after dumping the response + + respBytes, _ := httputil.DumpResponse(resp, false) + bytes = append(bytes, respBytes...) + + fmt.Printf("%s\n", bytes) + + return resp, err +} + +func FetchSite(c *fiber.Ctx) error { + // Get the url from the URL + urlQuery := c.Params("*") + + u, err := url.Parse(urlQuery) + if err != nil { + log.Fatal(err) + } + + log.Println(u.String()) + + if u.Scheme == "" { + u.Scheme = "https" + } + + // Fetch the site + //resp, err := http.Get(url) + req, _ := http.NewRequest("GET", u.String(), nil) + req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)") + req.Header.Set("X-Forwarded-For", "66.249.66.1") + //fmt.Println(c.GetReqHeaders()["Cookie"]) + //req.Header.Set("Cookie", fmt.Sprint(c.GetReqHeaders()["Cookie"])) + + //resp, err := http.DefaultClient.Do(req) + //client := &http.Client{} + //client.Timeout = time.Duration(5 * time.Second) + + client := http.Client{} + client.Transport = &loggingTransport{} + + fmt.Println("DEBUG1") + resp, err := client.Do(req) + fmt.Println("DEBUG2") + + /* + timeout := 1000 * time.Millisecond + client := httpclient.NewClient(httpclient.WithHTTPTimeout(timeout)) + + headers := http.Header{} + headers.Set("User-Agent", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)") + headers.Set("X-Forwarded-For", "66.249.66.1") + + fmt.Println("DEBUG1") + resp, err := client.Get(u.String(), headers) + if err != nil { + panic(err) + } + fmt.Println("DEBUG2") + */ + if err != nil { + return c.SendString(err.Error()) + } + defer resp.Body.Close() + + bodyB, err := io.ReadAll(resp.Body) + if err != nil { + //log.Fatalln(err) + return c.SendString(err.Error()) + } + body := string(bodyB) + + imagePattern := `]*\s+)?src="(/)([^"]*)"` + re := regexp.MustCompile(imagePattern) + body = re.ReplaceAllString(body, fmt.Sprintf(`]*\s+)?src="(/)([^"]*)"` + reScript := regexp.MustCompile(scriptPattern) + body = reScript.ReplaceAllString(body, fmt.Sprintf(` + + + +