Skip to content

Commit

Permalink
feat!: replace to httputil.ReverseProxy
Browse files Browse the repository at this point in the history
Closes #1054
  • Loading branch information
ykzts committed Dec 20, 2023
1 parent 50d85c8 commit 9f373ba
Show file tree
Hide file tree
Showing 24 changed files with 831 additions and 1,051 deletions.
1 change: 0 additions & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ jobs:
strategy:
matrix:
go-version:
- '1.19'
- '1.20'
- '1.21'

Expand Down
5 changes: 2 additions & 3 deletions cmd/manael/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@
//
// Usage:
//
// manael [arguments]
// manael [arguments]
//
// Example:
//
// manael -http=:8080 -upstream_url=http://localhost:9000
//
// manael -http=:8080 -upstream_url=http://localhost:9000
package main
17 changes: 11 additions & 6 deletions cmd/manael/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package main // import "manael.org/x/manael/cmd/manael"
package main

import (
"flag"
"fmt"
"log"
"net/http"
"net/url"
"os"

"github.com/gorilla/handlers"
"manael.org/x/manael"
"manael.org/x/manael/v2"
)

const (
Expand Down Expand Up @@ -76,12 +77,16 @@ func main() {
}
}

upstreamURL, err := url.Parse(conf.upstreamURL)
if err != nil {
log.Fatalf("Error: %v", err)
}

var handler http.Handler
handler = manael.NewServeProxy(conf.upstreamURL)
handler = manael.NewServeProxy(upstreamURL)
handler = handlers.CombinedLoggingHandler(os.Stdout, handler)

err := http.ListenAndServe(conf.httpAddr, handler)
if err != nil {
log.Fatal(err)
if err := http.ListenAndServe(conf.httpAddr, handler); err != nil {
log.Fatalf("Error: %v", err)
}
}
2 changes: 1 addition & 1 deletion decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package manael // import "manael.org/x/manael"
package manael

import (
"errors"
Expand Down
2 changes: 1 addition & 1 deletion encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package manael // import "manael.org/x/manael"
package manael

import (
"errors"
Expand Down
22 changes: 7 additions & 15 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,25 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package manael_test // import "manael.org/x/manael"
package manael_test

import (
"fmt"
"log"
"net/http"
"net/url"

"manael.org/x/manael"
"manael.org/x/manael/v2"
)

func ExampleNewServeProxy() {
p := manael.NewServeProxy("http://localhost:9000")

err := http.ListenAndServe(":8080", p)
u, err := url.Parse("http://localhost:9000")
if err != nil {
log.Fatal(err)
}
}

func ExampleNewResponse() {
req, err := http.NewRequest(http.MethodGet, "https://manael.test/", nil)
if err != nil {
p := manael.NewServeProxy(u)

if err := http.ListenAndServe(":8080", p); err != nil {
log.Fatal(err)
}

resp := manael.NewResponse(req, http.StatusOK)

fmt.Println(resp.StatusCode)
// Output: 200
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module manael.org/x/manael
module manael.org/x/manael/v2

go 1.21

Expand Down
2 changes: 1 addition & 1 deletion internal/testutil/testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package testutil // import "manael.org/x/manael/internal/testutil"
package testutil

import (
"image"
Expand Down
4 changes: 2 additions & 2 deletions internal/testutil/testutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
// SOFTWARE.

// Package manael provides HTTP handler for processing images.
package testutil_test // import "manael.org/x/manael/internal/testutil"
package testutil_test

import (
"os"
"testing"

"manael.org/x/manael/internal/testutil"
"manael.org/x/manael/v2/internal/testutil"
)

var testutilTests = []struct {
Expand Down
2 changes: 1 addition & 1 deletion lint-staged.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
'*.go': 'go fmt',
// '*.go': 'go fmt',
'*.{js,jsx,ts,tsx}': 'eslint --fix',
'*.{json,yml}': 'prettier --write'
}
128 changes: 124 additions & 4 deletions manael.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,135 @@
// SOFTWARE.

// Package manael provides HTTP handler for processing images.
package manael // import "manael.org/x/manael"
package manael

import (
"bytes"
"io"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"strconv"
"strings"
)

func avifEnabled(res *http.Response) bool {
contentType := res.Header.Get("Content-Type")

return os.Getenv("MANAEL_ENABLE_AVIF") == "true" && contentType != "image/png"
}

func scanAcceptHeader(res *http.Response) string {
accepts := res.Request.Header.Get("Accept")

for _, v := range strings.Split(accepts, ",") {
t := strings.TrimSpace(v)

if avifEnabled(res) && strings.HasPrefix(t, "image/avif") {
return "image/avif"
} else if strings.HasPrefix(t, "image/webp") {
return "image/webp"
}
}

return "*/*"
}

func check(res *http.Response) string {
if res.Request.Method != http.MethodGet && res.StatusCode != http.StatusOK && res.StatusCode != http.StatusNotModified {
return "*/*"
}

if s := res.Header.Get("Cache-Control"); s != "" {
for _, v := range strings.Split(s, ",") {
if strings.TrimSpace(v) == "no-transform" {
return "*/*"
}
}
}

t := res.Header.Get("Content-Type")

if t != "image/jpeg" && t != "image/png" {
return "*/*"
}

return scanAcceptHeader(res)
}

func convert(src io.Reader, t string) (*bytes.Buffer, error) {
img, err := Decode(src)
if err != nil {
return nil, err
}

buf := bytes.NewBuffer(nil)

err = Encode(buf, img, t)
if err != nil {
return nil, err
}

return buf, nil
}

func modifyResponse(res *http.Response) error {
res.Header.Set("Server", "Manael")

typ := check(res)
if typ == "*/*" {
return nil
}

defer res.Body.Close()

p := bytes.NewBuffer(nil)
b := io.TeeReader(res.Body, p)

buf, err := convert(b, typ)
if err != nil {
body := io.MultiReader(p, res.Body)

res.Body = io.NopCloser(body)
log.Printf("error: %v\n", err)

return nil
}

res.Body = io.NopCloser(buf)

res.Header.Set("Content-Type", typ)
res.Header.Set("Content-Length", strconv.Itoa(buf.Len()))

if res.Header.Get("Accept-Ranges") != "" {
res.Header.Del("Accept-Ranges")
}

keys := []string{"Accept"}
for _, v := range strings.Split(res.Header.Get("Vary"), ",") {
v = strings.TrimSpace(v)

if v != "" && !strings.EqualFold(v, "Accept") {
keys = append(keys, v)
}
}

res.Header.Set("Vary", strings.Join(keys[:], ", "))

return nil
}

// NewServeProxy returns a new Proxy given a upstream URL
func NewServeProxy(u string) *Proxy {
tr := &Transport{u, http.DefaultTransport}
func NewServeProxy(u *url.URL) http.Handler {
return &httputil.ReverseProxy{
Rewrite: func(r *httputil.ProxyRequest) {
r.Out.Header["X-Forwarded-For"] = r.In.Header["X-Forwarded-For"]

return &Proxy{tr}
r.SetXForwarded()
r.SetURL(u)
},
ModifyResponse: modifyResponse,
}
}

0 comments on commit 9f373ba

Please sign in to comment.