Wrote proxy

This commit is contained in:
Yann Stepienik 2023-02-26 22:26:09 +00:00
parent 987fd909a9
commit 944b787432
11 changed files with 364 additions and 98 deletions

1
.gitignore vendored
View file

@ -10,3 +10,4 @@ localcert.key
dev.json
.bin
client/dist
config_dev.json

View file

@ -5,3 +5,4 @@ env("GOPATH", run("go", ["env", "GOROOT"]) + ":" + pwd() + "/go_modules" + ":" +
env("MONGODB", readJsonFile("dev.json").MONGODB)
env("HTTP_PORT", 8080)
env("HTTPS_PORT", 8443)
env("CONFIG_FILE", "./config_dev.json")

View file

@ -20,6 +20,8 @@
"go://gopkg.in/ffmt.v1": "v1.5.6",
"npm://@esbuild/linux-x64": "0.16.17",
"npm://@vitejs/plugin-react": "3.1.0",
"npm://express": "4.18.2",
"npm://express-ws": "5.0.2",
"npm://react": "18.2.0",
"npm://react-dom": "18.2.0",
"npm://typescript": "4.9.5",

82
src/config.go Normal file
View file

@ -0,0 +1,82 @@
package main
import (
"log"
"os"
"regexp"
"./proxy"
"encoding/json"
)
type Config struct {
HTTPConfig HTTPConfig
}
var defaultConfig = Config{
HTTPConfig: HTTPConfig{
TLSCert: "localcert.crt",
TLSKey: "localcert.key",
GenerateMissingTLSCert: true,
HTTPPort: "80",
HTTPSPort: "443",
ProxyConfig: proxy.Config{
Routes: []proxy.RouteConfig{},
},
},
}
func GetConfig() Config {
configFile := os.Getenv("CONFIG_FILE")
if configFile == "" {
configFile = "/cosmos.config.json"
}
log.Println("Using config file: " + configFile)
// if file does not exist, create it
if _, err := os.Stat(configFile); os.IsNotExist(err) {
log.Println("Config file does not exist. Creating default config file.")
file, err := os.Create(configFile)
if err != nil {
log.Fatal("[ERROR] Creating Default Config File: " + err.Error())
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
err = encoder.Encode(defaultConfig)
if err != nil {
log.Fatal("[ERROR] Writing Default Config File: " + err.Error())
}
return defaultConfig
}
file, err := os.Open(configFile)
if err != nil {
log.Fatal("[ERROR] Opening Config File: " + err.Error())
}
defer file.Close()
decoder := json.NewDecoder(file)
config := Config{}
err = decoder.Decode(&config)
// check file is not empty
if err != nil {
// check error is not empty
if err.Error() == "EOF" {
log.Fatal("[ERROR] Reading Config File: File is empty.")
}
// get error string
errString := err.Error()
// replace string in error
m1 := regexp.MustCompile(`json: cannot unmarshal ([A-Za-z\.]+) into Go struct field ([A-Za-z\.]+) of type ([A-Za-z\.]+)`)
errString = m1.ReplaceAllString(errString, "Invalid JSON in config file.\n > Field $2 is wrong.\n > Type is $1 Should be $3")
log.Fatal("[ERROR] Reading Config File: " + errString)
}
return config
}

93
src/httpServer.go Normal file
View file

@ -0,0 +1,93 @@
package main
import (
"net/http"
"./utils"
// "./file"
// "./user"
"./proxy"
"github.com/gorilla/mux"
"log"
"os"
"strings"
)
type HTTPConfig struct {
TLSCert string
TLSKey string
GenerateMissingTLSCert bool
HTTPPort string
HTTPSPort string
ProxyConfig proxy.Config
}
var serverPortHTTP = os.Getenv("HTTP_PORT")
var serverPortHTTPS = os.Getenv("HTTPS_PORT")
func startHTTPServer(router *mux.Router) {
log.Println("Listening to HTTP on :" + serverPortHTTP)
err := http.ListenAndServe("0.0.0.0:" + serverPortHTTP, router)
if err != nil {
log.Fatal(err)
}
}
func startHTTPSServer(router *mux.Router, tlsCert string, tlsKey string) {
// redirect http to https
go (func () {
err := http.ListenAndServe("0.0.0.0:" + serverPortHTTP, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// change port in host
if strings.HasSuffix(r.Host, ":" + serverPortHTTP) {
if serverPortHTTPS != "443" {
r.Host = r.Host[:len(r.Host)-len(":" + serverPortHTTP)] + ":" + serverPortHTTPS
} else {
r.Host = r.Host[:len(r.Host)-len(":" + serverPortHTTP)]
}
}
http.Redirect(w, r, "https://"+r.Host+r.URL.String(), http.StatusMovedPermanently)
}))
if err != nil {
log.Fatal(err)
}
})()
log.Println("Listening to HTTP on :" + serverPortHTTP)
log.Println("Listening to HTTPS on :" + serverPortHTTPS)
// start https server
err := http.ListenAndServeTLS("0.0.0.0:" + serverPortHTTPS, tlsCert, tlsKey, router)
if err != nil {
log.Fatal(err)
}
}
func StartServer(config HTTPConfig) {
var tlsCert = config.TLSCert
var tlsKey= config.TLSKey
if serverPortHTTP == "" {
serverPortHTTP = config.HTTPPort
}
if serverPortHTTPS == "" {
serverPortHTTPS = config.HTTPSPort
}
router := proxy.BuildFromConfig(config.ProxyConfig)
if utils.FileExists(tlsCert) && utils.FileExists(tlsKey) {
log.Println("TLS certificate found, starting HTTPS servers and redirecting HTTP to HTTPS")
startHTTPSServer(router, tlsCert, tlsKey)
} else {
log.Println("No TLS certificate found, starting HTTP server only")
startHTTPServer(router)
}
}
func StopServer() {
}

View file

@ -1,106 +1,14 @@
package main
import (
"net/http"
"./utils"
"./file"
"./user"
"github.com/gorilla/mux"
"log"
"os"
"strings"
"log"
)
var tlsCert = "localcert.crt"
var tlsKey= "localcert.key"
var serverPortHTTP = os.Getenv("HTTP_PORT")
var serverPortHTTPS = os.Getenv("HTTPS_PORT")
func startHTTPServer(router *mux.Router) {
log.Println("Listening to HTTP on :" + serverPortHTTP)
err := http.ListenAndServe("0.0.0.0:" + serverPortHTTP, router)
if err != nil {
log.Fatal(err)
}
}
func startHTTPSServer(router *mux.Router) {
// redirect http to https
go (func () {
err := http.ListenAndServe("0.0.0.0:" + serverPortHTTP, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// change port in host
if strings.HasSuffix(r.Host, ":" + serverPortHTTP) {
if serverPortHTTPS != "443" {
r.Host = r.Host[:len(r.Host)-len(":" + serverPortHTTP)] + ":" + serverPortHTTPS
} else {
r.Host = r.Host[:len(r.Host)-len(":" + serverPortHTTP)]
}
}
http.Redirect(w, r, "https://"+r.Host+r.URL.String(), http.StatusMovedPermanently)
}))
if err != nil {
log.Fatal(err)
}
})()
log.Println("Listening to HTTP on :" + serverPortHTTP)
log.Println("Listening to HTTPS on :" + serverPortHTTPS)
// start https server
err := http.ListenAndServeTLS("0.0.0.0:" + serverPortHTTPS, tlsCert, tlsKey, router)
if err != nil {
log.Fatal(err)
}
}
func main() {
log.Println("Starting...")
if serverPortHTTP == "" {
serverPortHTTP = "80"
}
config := GetConfig()
if serverPortHTTPS == "" {
serverPortHTTPS = "443"
}
router := mux.NewRouter().StrictSlash(true)
utils.DB()
defer utils.Disconnect()
router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
router.HandleFunc("/file/list", file.FileList)
router.HandleFunc("/file/get", file.FileGet)
router.HandleFunc("/file/delete", file.FileDelete)
router.HandleFunc("/file/copy", file.FileCopy)
router.HandleFunc("/file/move", file.FileMove)
router.HandleFunc("/user/login", user.UserLogin)
// router.HandleFunc("/user/register", user.UserRegister)
// router.HandleFunc("/user/edit", )
// router.HandleFunc("/user/delete", )
// router.HandleFunc("/config/get", )
// router.HandleFunc("/config/set", )
// router.HandleFunc("/db", )
if utils.FileExists(tlsCert) && utils.FileExists(tlsKey) {
log.Println("TLS certificate found, starting HTTPS servers and redirecting HTTP to HTTPS")
startHTTPSServer(router)
} else {
log.Println("No TLS certificate found, starting HTTP server only")
startHTTPServer(router)
}
defer StopServer()
StartServer(config.HTTPConfig)
}

View file

@ -0,0 +1,33 @@
package proxy
import (
"github.com/gorilla/mux"
"net/http"
)
type RouteConfig struct {
Routing Route
Target string
}
type Config struct {
Routes []RouteConfig
}
func BuildFromConfig(config Config) *mux.Router {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/_health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
router.PathPrefix("/_static").Handler(http.StripPrefix("/_static", http.FileServer(http.Dir("static"))))
for i := len(config.Routes)-1; i >= 0; i-- {
routeConfig := config.Routes[i]
RouterGen(routeConfig.Routing, router, RouteTo(routeConfig.Target))
}
return router
}

51
src/proxy/routeTo.go Normal file
View file

@ -0,0 +1,51 @@
package proxy
import (
"net/http"
"net/http/httputil"
"net/url"
"log"
// "io/ioutil"
// "io"
// "os"
// "golang.org/x/crypto/bcrypt"
// "../utils"
)
// NewProxy takes target host and creates a reverse proxy
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
url, err := url.Parse(targetHost)
if err != nil {
return nil, err
}
proxy := httputil.NewSingleHostReverseProxy(url)
// upgrade the request to websocket
proxy.ModifyResponse = func(resp *http.Response) error {
log.Println("[INFO] Response from backend: ", resp.Status)
log.Println("[INFO] URL was ", resp.Request.URL)
return nil
}
return proxy, nil
}
// ProxyRequestHandler handles the http request using proxy
func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
proxy.ServeHTTP(w, r)
}
}
func RouteTo(destination string) *httputil.ReverseProxy /*func(http.ResponseWriter, *http.Request)*/ {
// initialize a reverse proxy and pass the actual backend server url here
proxy, err := NewProxy(destination)
if err != nil {
panic(err)
}
// create a handler function which uses the reverse proxy
return proxy //ProxyRequestHandler(proxy)
}

41
src/proxy/routerGen.go Normal file
View file

@ -0,0 +1,41 @@
package proxy
import (
"net/http"
"net/http/httputil"
"github.com/gorilla/mux"
// "log"
// "io/ioutil"
// "io"
// "os"
// "golang.org/x/crypto/bcrypt"
// "../utils"
)
type Route struct {
UseHost bool
Host string
UsePathPrefix bool
PathPrefix string
}
func RouterGen(route Route, router *mux.Router, destination *httputil.ReverseProxy) *mux.Route {
var realDestination http.Handler
realDestination = destination
origin := router.Methods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD")
if(route.UseHost) {
origin = origin.Host(route.Host)
}
if(route.UsePathPrefix) {
origin = origin.PathPrefix(route.PathPrefix)
realDestination = http.StripPrefix(route.PathPrefix, destination)
}
origin.Handler(realDestination)
return origin
}

38
test-server.js Normal file
View file

@ -0,0 +1,38 @@
const express = require('express')
const app = express()
const port = 3000
var expressWs = require('express-ws')(app);
// console log every request sent
app.use((req, res, next) => {
console.log(`[REQ] - ${req.method} ${req.url}`)
next()
});
app.get('/return/:status/:time', async (req, res) => {
const statusCode = parseInt(req.params.status);
const returnString =`Hello status ${statusCode} after ${req.params.time}ms !`
console.log(`[RES] - ${statusCode} ${returnString}`)
await new Promise(resolve => setTimeout(resolve, req.params.time));
return res.status(statusCode).send(returnString)
});
app.get('/', (req, res) => {
console.log("[RES] - Hello World!")
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
app.ws('/ws', function(ws, req) {
ws.on('message', function(msg) {
console.log(msg);
ws.send(msg);
});
});

16
test-websocket.js Normal file
View file

@ -0,0 +1,16 @@
const WebSocket = require('ws');
// send
const ws = new WebSocket('ws://localhost:8080/proxy/ws', {
rejectUnauthorized: false,
followRedirects : true,
});
ws.on('open', function open() {
ws.send('something');
});
ws.on('message', function incoming(data) {
console.log(data);
});