summaryrefslogtreecommitdiff
path: root/proxy.go
blob: 4442fb97b827259adc227eeb8304980a77ab4e35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package main

import (
	"io"
	"strings"
	"net"
	"net/http"
	"net/url"
	"sanine.net/git/phlox/auth"
	"sanine.net/git/phlox/config"
	"sanine.net/git/phlox/page"
	log "github.com/sirupsen/logrus"
)


type Endpoint struct {
	Path string
	Origin *url.URL
}


func addEndpoint(s *auth.Sessions, pages page.Pages, e config.Endpoint) {
	log.Infof("proxying endpoint %v to %v", e.Path, e.Address)
	origin, err := url.Parse(e.Address)
	if err != nil {
		log.Fatal(err)
	}

	end := Endpoint{
		Path: e.Path,
		Origin: origin,
	}

	http.HandleFunc(e.Path, func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Location", e.Path + "/")
		w.WriteHeader(http.StatusPermanentRedirect)
	})

	http.HandleFunc(e.Path + "/", func(w http.ResponseWriter, r *http.Request) {
		log.Infof("REQ: %v", r.URL.Path)
		proxy(w, r, s, pages, end)
	})
}


func proxy(w http.ResponseWriter, r *http.Request, s *auth.Sessions, pages page.Pages, end Endpoint) {
	ok := authenticateRequest(r, s)
	if !ok {
		w.Header().Set("Location", "/phlox/login")
		w.WriteHeader(http.StatusTemporaryRedirect)
		return
	}

	response := proxyRequest(w, r, pages, end)
	if response != nil {
		proxyResponse(w, response)
	}
}


func proxyRequest(w http.ResponseWriter, r *http.Request, pages page.Pages, end Endpoint)  *http.Response {
	// configure host address
	r.Host = end.Origin.Host
	r.URL.Host = end.Origin.Host

	// strip proxy endpoint path from request path
	r.URL.Path = strings.TrimPrefix(r.URL.Path, end.Path)

	// set X-Forwarded-For
	forwardedFor, _, _ := net.SplitHostPort(r.RemoteAddr)
	r.Header.Set("X-Forwarded-For", forwardedFor)

	// misc request cleanups
	r.URL.Scheme = end.Origin.Scheme
	r.RequestURI = ""

	// make request
	response, err := http.DefaultClient.Do(r)
	if err != nil {
		pages.ServeError500(w)
		log.Error(err)
		return nil
	}

	return response
}


func proxyResponse(w http.ResponseWriter, response *http.Response) {
	// copy header
	for key, values := range response.Header {
		for _, value := range values {
			w.Header().Add(key, value)
		}
	}

	// get trailer keys
	trailerKeys := []string{}
	for key := range response.Trailer {
		trailerKeys = append(trailerKeys, key)
	}
	if (len(trailerKeys) > 0) {
		w.Header().Set("Trailer", strings.Join(trailerKeys, ","))
	}

	w.WriteHeader(response.StatusCode)

	// copy body to client
	io.Copy(w, response.Body)

	// write trailers
	for key, values := range response.Trailer {
		for _, value := range values {
			w.Header().Set(key, value)
		}
	}
}