Skip to content

Commit

Permalink
feat: Full support for proxy deploy
Browse files Browse the repository at this point in the history
The index page will be handled correctly if the `XFF` and `X-API-URL-PREFIX` are set properly.
  • Loading branch information
orangedeng committed Jul 25, 2024
1 parent 568eda3 commit 8f069c3
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 2 deletions.
1 change: 0 additions & 1 deletion internal/config/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
var InsecureSkipTLSVerify bool
var SystemDefaultRegistry string
var APIUIVersion = "1.1.11"

var ShellPodImage string

func Flags() []cli.Flag {
Expand Down
116 changes: 116 additions & 0 deletions internal/ui/proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package ui

import (
"bufio"
"bytes"
"fmt"
"io"
"net"
"net/http"
"strconv"

"github.com/rancher/apiserver/pkg/urlbuilder"
"k8s.io/apimachinery/pkg/util/proxy"
)

type RoundTripFunc func(*http.Request) (*http.Response, error)

func (r RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return r(req)
}

func proxyMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
scheme := urlbuilder.GetScheme(r)
host := urlbuilder.GetHost(r, scheme)
pathPrepend := r.Header.Get(urlbuilder.PrefixHeader)

if scheme == r.URL.Scheme && host == r.URL.Host && pathPrepend == "" {
next.ServeHTTP(w, r)
return
}

proxyRoundtrip := proxy.Transport{
Scheme: scheme,
Host: host,
PathPrepend: pathPrepend,
RoundTripper: RoundTripFunc(func(r *http.Request) (*http.Response, error) {
rw := &dummyResponseWriter{
next: w,
header: make(http.Header),
}
next.ServeHTTP(rw, r)
return rw.getResponse(r), nil
}),
}
//proxyRoundtripper will write the response in RoundTrip func
resp, _ := proxyRoundtrip.RoundTrip(r)
responseToWriter(resp, w)
})

}

var _ http.ResponseWriter = &dummyResponseWriter{}
var _ http.Hijacker = &dummyResponseWriter{}

type dummyResponseWriter struct {
next http.ResponseWriter

header http.Header
body bytes.Buffer
statusCode int
}

// Hijack implements http.Hijacker.
func (drw *dummyResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if h, ok := drw.next.(http.Hijacker); ok {
return h.Hijack()
}
return nil, nil, fmt.Errorf("")
}

// Header implements the http.ResponseWriter interface.
func (drw *dummyResponseWriter) Header() http.Header {
return drw.header
}

// Write implements the http.ResponseWriter interface.
func (drw *dummyResponseWriter) Write(b []byte) (int, error) {
return drw.body.Write(b)
}

// WriteHeader implements the http.ResponseWriter interface.
func (drw *dummyResponseWriter) WriteHeader(statusCode int) {
drw.statusCode = statusCode
}

// GetStatusCode returns the status code written to the response.
func (drw *dummyResponseWriter) GetStatusCode() int {
if drw.statusCode == 0 {
return 200
}
return drw.statusCode
}

func (drw *dummyResponseWriter) getResponse(req *http.Request) *http.Response {
return &http.Response{
Status: strconv.Itoa(drw.GetStatusCode()),
StatusCode: drw.GetStatusCode(),
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Request: req,
Header: drw.header,
Body: io.NopCloser(&drw.body),
}
}

func responseToWriter(resp *http.Response, writer http.ResponseWriter) {
for k, v := range resp.Header {
writer.Header()[k] = v
}
if resp.StatusCode != http.StatusOK {
writer.WriteHeader(resp.StatusCode)
}
_, _ = io.Copy(writer, resp.Body)
}
2 changes: 1 addition & 1 deletion internal/ui/routers.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ func New(opt *Options) (http.Handler, APIUI) {
http.Redirect(rw, req, url, http.StatusFound)
})

return router, apiUI(opt)
return proxyMiddleware(router), apiUI(opt)
}

0 comments on commit 8f069c3

Please sign in to comment.