package fetch import ( "io" "net/http" "net/url" edgehttp "forge.cadoles.com/arcad/edge/pkg/http" "github.com/go-chi/chi/v5" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) func Mount() func(r chi.Router) { return func(r chi.Router) { r.Get("/api/v1/fetch", handleAppFetch) } } func handleAppFetch(w http.ResponseWriter, r *http.Request) { ctx := r.Context() rawURL := r.URL.Query().Get("url") url, err := url.Parse(rawURL) if err != nil { edgehttp.JSONError(w, http.StatusBadRequest, edgehttp.ErrCodeBadRequest) return } requestMsg := NewFetchRequestEnvelope(ctx, r.RemoteAddr, url) bus, ok := edgehttp.ContextBus(ctx) if !ok { logger.Error(ctx, "could find bus on context") edgehttp.JSONError(w, http.StatusInternalServerError, edgehttp.ErrCodeInternalError) return } reply, err := bus.Request(ctx, requestMsg) if err != nil { logger.Error(ctx, "could not retrieve fetch request reply", logger.CapturedE(errors.WithStack(err))) edgehttp.JSONError(w, http.StatusInternalServerError, edgehttp.ErrCodeInternalError) return } logger.Debug(ctx, "fetch reply", logger.F("reply", reply)) responseMsg, ok := reply.Message().(*FetchResponse) if !ok { logger.Error( ctx, "unexpected fetch response message", logger.F("message", reply), ) edgehttp.JSONError(w, http.StatusInternalServerError, edgehttp.ErrCodeInternalError) return } if !responseMsg.Allow { edgehttp.JSONError(w, http.StatusForbidden, edgehttp.ErrCodeForbidden) return } proxyReq, err := http.NewRequest(http.MethodGet, url.String(), nil) if err != nil { logger.Error( ctx, "could not create proxy request", logger.CapturedE(errors.WithStack(err)), ) edgehttp.JSONError(w, http.StatusInternalServerError, edgehttp.ErrCodeInternalError) return } for header, values := range r.Header { for _, value := range values { proxyReq.Header.Add(header, value) } } proxyReq.Header.Add("X-Forwarded-From", r.RemoteAddr) httpClient, ok := edgehttp.ContextHTTPClient(ctx) if !ok { logger.Error(ctx, "could find http client on context") edgehttp.JSONError(w, http.StatusInternalServerError, edgehttp.ErrCodeInternalError) return } res, err := httpClient.Do(proxyReq) if err != nil { logger.Error( ctx, "could not execute proxy request", logger.CapturedE(errors.WithStack(err)), ) edgehttp.JSONError(w, http.StatusInternalServerError, edgehttp.ErrCodeInternalError) return } defer func() { if err := res.Body.Close(); err != nil { logger.Error( ctx, "could not close response body", logger.CapturedE(errors.WithStack(err)), ) } }() for header, values := range res.Header { for _, value := range values { w.Header().Add(header, value) } } w.WriteHeader(res.StatusCode) if _, err := io.Copy(w, res.Body); err != nil { panic(errors.WithStack(err)) } }