Skip to content

Commit

Permalink
Merge pull request #340 from hearchco/as/fix/imageproxy
Browse files Browse the repository at this point in the history
fix(imageproxy): switch to get and copy body instead of reverse proxy
  • Loading branch information
aleksasiriski authored Jun 27, 2024
2 parents e030a19 + ef84062 commit 511c742
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 101 deletions.
6 changes: 1 addition & 5 deletions src/config/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ func New() Config {
},
},
ImageProxy: ImageProxy{
Timeouts: ImageProxyTimeouts{
Dial: 3 * time.Second,
KeepAlive: 3 * time.Second,
TLSHandshake: 2 * time.Second,
},
Timeout: 3 * time.Second,
},
},
Categories: map[category.Name]Category{
Expand Down
16 changes: 4 additions & 12 deletions src/config/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,8 @@ func (c Config) getReader() ReaderConfig {
Redis: c.Server.Cache.Redis,
},
ImageProxy: ReaderImageProxy{
Salt: c.Server.ImageProxy.Salt,
Timeouts: ReaderImageProxyTimeouts{
Dial: moretime.ConvertToFancyTime(c.Server.ImageProxy.Timeouts.Dial),
KeepAlive: moretime.ConvertToFancyTime(c.Server.ImageProxy.Timeouts.KeepAlive),
TLSHandshake: moretime.ConvertToFancyTime(c.Server.ImageProxy.Timeouts.TLSHandshake),
},
Salt: c.Server.ImageProxy.Salt,
Timeout: moretime.ConvertToFancyTime(c.Server.ImageProxy.Timeout),
},
},
// Initialize the categories map.
Expand Down Expand Up @@ -153,12 +149,8 @@ func (c *Config) fromReader(rc ReaderConfig) {
Redis: rc.Server.Cache.Redis,
},
ImageProxy: ImageProxy{
Salt: rc.Server.ImageProxy.Salt,
Timeouts: ImageProxyTimeouts{
Dial: moretime.ConvertFromFancyTime(rc.Server.ImageProxy.Timeouts.Dial),
KeepAlive: moretime.ConvertFromFancyTime(rc.Server.ImageProxy.Timeouts.KeepAlive),
TLSHandshake: moretime.ConvertFromFancyTime(rc.Server.ImageProxy.Timeouts.TLSHandshake),
},
Salt: rc.Server.ImageProxy.Salt,
Timeout: moretime.ConvertFromFancyTime(rc.Server.ImageProxy.Timeout),
},
},
// Initialize the categories map.
Expand Down
24 changes: 6 additions & 18 deletions src/config/structs_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,26 +76,14 @@ type Redis struct {
}

// ReaderProxy is format in which the config is read from the config file and environment variables.
type ReaderImageProxy struct {
Salt string `koanf:"salt"`
Timeouts ReaderImageProxyTimeouts `koanf:"timeouts"`
}
type ImageProxy struct {
Salt string
Timeouts ImageProxyTimeouts
}

// ReaderProxyTimeouts is format in which the config is read from the config file and environment variables.
// In <number><unit> format.
// Example: 1s, 1m, 1h, 1d, 1w, 1M, 1y.
// If unit is not specified, it is assumed to be milliseconds.
type ReaderImageProxyTimeouts struct {
Dial string `koanf:"dial"`
KeepAlive string `koanf:"keepalive"`
TLSHandshake string `koanf:"tlshandshake"`
type ReaderImageProxy struct {
Salt string `koanf:"salt"`
Timeout string `koanf:"timeout"`
}
type ImageProxyTimeouts struct {
Dial time.Duration
KeepAlive time.Duration
TLSHandshake time.Duration
type ImageProxy struct {
Salt string
Timeout time.Duration
}
45 changes: 0 additions & 45 deletions src/router/routes/responses.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
package routes

import (
"encoding/json"
"fmt"
"net/http"

"github.com/hearchco/agent/src/search/result"
)

Expand All @@ -29,44 +25,3 @@ type SuggestionsResponse struct {

Suggestions []result.Suggestion `json:"suggestions"`
}

func writeResponse(w http.ResponseWriter, status int, body string) error {
w.WriteHeader(status)
_, err := w.Write([]byte(body))
return err
}

func writeResponseJSON(w http.ResponseWriter, status int, body any) error {
res, err := json.Marshal(body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, werr := w.Write([]byte("internal server error"))
if werr != nil {
return fmt.Errorf("%w: %w", werr, err)
}
return err
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_, err = w.Write(res)
return err
}

func writeResponseSuggestions(w http.ResponseWriter, status int, query string, suggestions []string) error {
jsonStruct := [...]any{query, suggestions}
res, err := json.Marshal(jsonStruct)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, werr := w.Write([]byte("internal server error"))
if werr != nil {
return fmt.Errorf("%w: %w", werr, err)
}
return err
}

w.Header().Set("Content-Type", "application/x-suggestions+json")
w.WriteHeader(status)
_, err = w.Write(res)
return err
}
54 changes: 34 additions & 20 deletions src/router/routes/route_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ package routes

import (
"fmt"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"strings"
"time"

"github.com/rs/zerolog/log"

"github.com/hearchco/agent/src/config"
"github.com/hearchco/agent/src/search/useragent"
"github.com/hearchco/agent/src/utils/anonymize"
"github.com/hearchco/agent/src/utils/moreurls"
)

func routeProxy(w http.ResponseWriter, r *http.Request, salt string, timeouts config.ImageProxyTimeouts) error {
func routeProxy(w http.ResponseWriter, r *http.Request, salt string, timeout time.Duration) error {
// Parse the form.
err := r.ParseForm()
if err != nil {
Expand Down Expand Up @@ -93,15 +92,23 @@ func routeProxy(w http.ResponseWriter, r *http.Request, salt string, timeouts co
Msg("Created a new anon request")

// Create reverse proxy with timeout.
rp := createReverseProxy(timeouts)

// Proxy the request.
log.Debug().
Str("url", target.String()).
Msg("Proxying request")
rp.ServeHTTP(w, &nr) // Use the new request.

return nil
// Use the new request.
resp, err := requestResponse(&nr, timeout)
if err != nil {
// Server error.
log.Debug().
Err(err).
Str("url", target.String()).
Msg("Failed to proxy request")
return writeResponse(w, http.StatusInternalServerError, fmt.Sprintf("failed to proxy request: %v", err))
}

// Proxy the response.
return writeResponseImageProxy(w, resp)
}

func getUrlToVerifyAndToProxy(urlParam string, favicon bool) (string, string, error) {
Expand Down Expand Up @@ -150,7 +157,6 @@ func createAnonRequest(r *http.Request, target *url.URL) http.Request {
Method: http.MethodGet,
URL: target,
Host: target.Host,
RequestURI: target.RequestURI(),
Proto: "HTTP/2",
ProtoMajor: 2,
ProtoMinor: 0,
Expand All @@ -169,15 +175,23 @@ func createAnonRequest(r *http.Request, target *url.URL) http.Request {
}
}

func createReverseProxy(timeouts config.ImageProxyTimeouts) httputil.ReverseProxy {
// Create reverse proxy with timeout.
rp := httputil.ReverseProxy{Director: func(r *http.Request) {}}
rp.Transport = &http.Transport{
DialContext: (&net.Dialer{
Timeout: timeouts.Dial,
KeepAlive: timeouts.KeepAlive,
}).DialContext,
TLSHandshakeTimeout: timeouts.TLSHandshake,
func requestResponse(r *http.Request, timeout time.Duration) (*http.Response, error) {
// Create a reverse proxy.
client := http.Client{Timeout: timeout}
resp, err := client.Do(r)
if err != nil {
return nil, err
}

// Check if the response is OK.
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("response status code is not OK: %v", resp.StatusCode)
}
return rp

// Check if the response is of image type.
if !strings.HasPrefix(resp.Header.Get("Content-Type"), "image/") {
return nil, fmt.Errorf("response content type is not an image: %v", resp.Header.Get("Content-Type"))
}

return resp, nil
}
2 changes: 1 addition & 1 deletion src/router/routes/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func Setup(mux *chi.Mux, ver string, db cache.DB, conf config.Config) {

// /proxy
mux.Get("/proxy", func(w http.ResponseWriter, r *http.Request) {
err := routeProxy(w, r, conf.Server.ImageProxy.Salt, conf.Server.ImageProxy.Timeouts)
err := routeProxy(w, r, conf.Server.ImageProxy.Salt, conf.Server.ImageProxy.Timeout)
if err != nil {
log.Error().
Err(err).
Expand Down
58 changes: 58 additions & 0 deletions src/router/routes/writers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package routes

import (
"encoding/json"
"fmt"
"io"
"net/http"
)

func writeResponse(w http.ResponseWriter, status int, body string) error {
w.WriteHeader(status)
_, err := w.Write([]byte(body))
return err
}

func writeResponseJSON(w http.ResponseWriter, status int, body any) error {
res, err := json.Marshal(body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, werr := w.Write([]byte("internal server error"))
if werr != nil {
return fmt.Errorf("%w: %w", werr, err)
}
return err
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_, err = w.Write(res)
return err
}

func writeResponseSuggestions(w http.ResponseWriter, status int, query string, suggestions []string) error {
jsonStruct := [...]any{query, suggestions}
res, err := json.Marshal(jsonStruct)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, werr := w.Write([]byte("internal server error"))
if werr != nil {
return fmt.Errorf("%w: %w", werr, err)
}
return err
}

w.Header().Set("Content-Type", "application/x-suggestions+json")
w.WriteHeader(status)
_, err = w.Write(res)
return err
}

func writeResponseImageProxy(w http.ResponseWriter, resp *http.Response) error {
w.Header().Set("Content-Encoding", resp.Header.Get("Content-Encoding"))
w.Header().Set("Content-Length", resp.Header.Get("Content-Length"))
w.Header().Set("Content-Type", resp.Header.Get("Content-Type"))
w.WriteHeader(resp.StatusCode)
_, err := io.Copy(w, resp.Body)
return err
}

0 comments on commit 511c742

Please sign in to comment.