-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
143 lines (134 loc) · 4.65 KB
/
main.go
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package main
import (
"fmt"
"io"
"io/fs"
"log"
"net/http"
"net/url"
"os"
"strconv"
"strings"
)
func main() {
config := read_config("config.yasgp")
for pattern, g := range config.groups {
if pattern == "" { pattern = "/" }
http.HandleFunc(pattern, func(w http.ResponseWriter, req *http.Request) {
log.Printf("%v", req.URL.String())
hostname := req.URL.Hostname()
for _, rule := range g {
if hostname == "" { hostname = strings.Split(req.Host, ":")[0] }
if hostname == rule.trigger.Host {
target_url := req.URL
target_url.Scheme = "http"
target_url.Host = rule.target.Host
joined_path, err := url.JoinPath(rule.target.Path, req.URL.Path)
if err != nil { log.Fatalln(err) }
target_url.Path = joined_path
newReq, err := http.NewRequest(req.Method, target_url.String(), req.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Copy the headers from the original request
for key, val := range req.Header {
newReq.Header[key] = val
}
newReq.Header.Set("x-forwarded-host", req.Host)
// Send the request via a http.Client
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
res, err := client.Do(newReq)
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
// Copy the response headers and body back to the original writer
for key, val := range res.Header {
w.Header()[key] = val
}
w.WriteHeader(res.StatusCode)
io.Copy(w, res.Body)
return
}
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
})
}
if err := http.ListenAndServe(fmt.Sprintf(":%v", config.port), nil); err != nil {
log.Fatalf("Could not start server: %s\n", err.Error())
}
}
type Rule struct {
trigger url.URL
target url.URL
}
func parse_rule(rule_string string) (*Rule, error) {
rule_components := strings.Split(rule_string, " to ")
if len(rule_components) < 2 { return nil, fmt.Errorf("rule parse error") }
trigger, err := url.Parse(rule_components[0])
if err != nil { return nil, err }
target, err := url.Parse(rule_components[1])
if err != nil { return nil, err }
return &Rule{
trigger: *trigger,
target: *target,
}, nil
}
type Config struct {
port uint16
rules []Rule
groups map[string][]*Rule
}
func parse_config(config_file_data string) Config {
config_file_lines := strings.Split(config_file_data, "\n")
for i, l := range config_file_lines { // windows support
config_file_lines[i] = strings.TrimSuffix(l, "\r")
}
var port uint16
port = 80
rules := make([]Rule, 0)
groups := make(map[string][]*Rule, 0)
for i, r := range config_file_lines {
if strings.Contains(r, "port") {
p, err := strconv.ParseInt(strings.Split(r, " ")[1], 10, 32)
if err != nil {
panic(err)
}
port = uint16(p)
} else if r != "" {
rule, err := parse_rule(r)
if err != nil {
log.Fatalf("%s at line %v\n", err.Error(), i)
}
rules = append(rules, *rule)
if g, ok := groups[rule.trigger.Path]; ok {
groups[rule.trigger.Path] = append(g, rule)
} else if groups[rule.trigger.Path] == nil {
groups[rule.trigger.Path] = []*Rule{ rule }
}
}
}
return Config{
port: port,
rules: rules,
groups: groups,
}
}
func read_config(filename string) Config {
if !fs.ValidPath(filename) {
}
file_data, err := os.ReadFile(filename)
if err != nil {
os.Stderr.WriteString(err.Error())
}
file_data_str := string(file_data)
return parse_config(file_data_str)
}