-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
server.go
192 lines (163 loc) · 5.68 KB
/
server.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package main
import (
"encoding/json"
"log"
"net"
"net/http"
"os"
"strconv"
"strings"
"time"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
)
const PING_DEFAULT_TIMEOUT = 10
const PING_DEFAULT_NETWORK = "tcp"
type response struct {
Host string `json:"host"`
Proto string `json:"proto"`
Method string `json:"method"`
Scheme string `json:"scheme"`
RequestURI string `json:"requestUri"`
URL string `json:"url"`
RemoteAddr string `json:"remoteAddr"`
Counter int `json:"counter"`
Headers map[string][]string `json:"headers"`
Hostname string `json:"hostname"`
EnvVariables []string `json:"envVariables,omitempty"`
Files map[string]string `json:"files,omitempty"`
}
type errorResponse struct {
Error string `json:"error"`
}
type successResponse struct {
Message string `json:"message"`
}
var counter = 0
var hostname = os.Getenv("HOSTNAME")
func preflight(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "*")
w.Header().Set("Access-Control-Allow-Headers", "*")
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Expose-Headers", "*")
w.Header().Set("Access-Control-Max-Age", "600")
w.WriteHeader(http.StatusOK)
}
func handler(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
showEnvs := os.Getenv("YOSOY_SHOW_ENVS")
showFiles := os.Getenv("YOSOY_SHOW_FILES")
response := &response{}
counter++
response.Counter = counter
remoteAddr := remoteAddrWithoutPort(req)
response.RemoteAddr = remoteAddr
response.RequestURI = req.RequestURI
response.Host = req.Host
response.Proto = req.Proto
response.Method = req.Method
response.Scheme = req.URL.Scheme
response.Headers = req.Header
response.URL = req.URL.String()
response.Hostname = hostname
if strings.ToLower(showEnvs) == "true" || strings.ToLower(showEnvs) == "yes" || strings.ToLower(showEnvs) == "on" || showEnvs == "1" {
response.EnvVariables = os.Environ()
}
if len(showFiles) > 0 {
response.Files = make(map[string]string)
files := strings.Split(showFiles, ",")
for _, file := range files {
bytes, err := os.ReadFile(file)
if err != nil {
log.Printf("Could not read file %v: %v\n", file, err)
continue
}
contents := string(bytes)
response.Files[file] = contents
}
}
w.Header().Add("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}
func ping(w http.ResponseWriter, req *http.Request) {
// get h, p, n, t parameters from query string
hostname := req.URL.Query().Get("h")
port := req.URL.Query().Get("p")
network := req.URL.Query().Get("n")
timeoutString := req.URL.Query().Get("t")
var timeout int64 = PING_DEFAULT_TIMEOUT
// return HTTP BadRequest when hostname is empty
if hostname == "" {
w.WriteHeader(http.StatusBadRequest)
w.Header().Add("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(&errorResponse{"hostname is empty"})
return
}
// return HTTP BadRequest when port is empty
if port == "" {
w.WriteHeader(http.StatusBadRequest)
w.Header().Add("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(&errorResponse{"port is empty"})
return
}
// check if timeoutString is a valid int64, return HTTP BadRequest when invalid, otherwise set timeout value
if timeoutString != "" {
timeoutInt, err := strconv.ParseInt(timeoutString, 10, 64)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Header().Add("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(&errorResponse{"timeout is invalid"})
return
}
timeout = timeoutInt
}
// if network is empty set default to tcp
if network == "" {
network = PING_DEFAULT_NETWORK
}
// ping the hostname and port
err := pingHost(hostname, port, network, timeout)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Header().Add("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(&errorResponse{err.Error()})
return
}
// return HTTP OK
w.WriteHeader(http.StatusOK)
w.Header().Add("Content-Type", "application/json; charset=utf-8")
json.NewEncoder(w).Encode(&successResponse{"ping succeeded"})
}
func pingHost(hostname, port, network string, timeout int64) error {
// create timeoutDuration variable of Duration type using timeout as the value in seconds
timeoutDuration := time.Duration(timeout) * time.Second
// open a socket to hostname and port
conn, err := net.DialTimeout(network, hostname+":"+port, timeoutDuration)
if err != nil {
return err
}
// close the socket
conn.Close()
return nil
}
func remoteAddrWithoutPort(req *http.Request) string {
remoteAddr := req.RemoteAddr
if index := strings.LastIndex(remoteAddr, ":"); index > 0 {
remoteAddr = remoteAddr[0:index]
}
return remoteAddr
}
func main() {
log.Printf("yosoy is up %v\n", hostname)
r := mux.NewRouter()
r.Handle("/favicon.ico", r.NotFoundHandler)
r.HandleFunc("/_/yosoy/ping", ping).Methods(http.MethodGet)
r.PathPrefix("/").HandlerFunc(preflight).Methods(http.MethodOptions)
r.PathPrefix("/").HandlerFunc(handler).Methods(http.MethodGet, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete, http.MethodConnect, http.MethodHead, http.MethodTrace)
loggingRouter := handlers.CombinedLoggingHandler(os.Stdout, r)
proxyRouter := handlers.ProxyHeaders(loggingRouter)
recoveryRouter := handlers.RecoveryHandler()(proxyRouter)
http.ListenAndServe(":80", recoveryRouter)
}