193 lines
5.7 KiB
Go
193 lines
5.7 KiB
Go
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(":8080", recoveryRouter)
|
|
}
|