chore: Initial commit.
This commit is contained in:
parent
90b91d06d3
commit
f737c34a3e
4 changed files with 233 additions and 0 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -25,3 +25,5 @@ go.work.sum
|
||||||
# env file
|
# env file
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
hostess
|
||||||
|
|
8
go.mod
Normal file
8
go.mod
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module git.greysoh.dev/imterah/hostess
|
||||||
|
|
||||||
|
go 1.23.3
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
golang.org/x/net v0.31.0 // indirect
|
||||||
|
)
|
4
go.sum
Normal file
4
go.sum
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||||
|
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
219
main.go
Normal file
219
main.go
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
var isLoggingEnabled bool
|
||||||
|
|
||||||
|
func logRequest(method, path string, respStatus int) {
|
||||||
|
if isLoggingEnabled {
|
||||||
|
log.Printf("%s %s - %d", method, path, respStatus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
listenAddr := ":8080"
|
||||||
|
sourceIP := "http://192.168.2.10"
|
||||||
|
hostHeader := "immich.hofers.cloud"
|
||||||
|
|
||||||
|
isLoggingEnabled = true
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
wsUpgrader := &websocket.Upgrader{}
|
||||||
|
wsDialer := &websocket.Dialer{
|
||||||
|
NetDial: func(network, addr string) (net.Conn, error) {
|
||||||
|
spoofedAddr := sourceIP[strings.LastIndex(sourceIP, "/")+1:]
|
||||||
|
|
||||||
|
if strings.Index(spoofedAddr, ":") == -1 {
|
||||||
|
if strings.HasPrefix(sourceIP, "http://") {
|
||||||
|
spoofedAddr += ":80"
|
||||||
|
} else if strings.HasPrefix(sourceIP, "https://") {
|
||||||
|
spoofedAddr += ":443"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("spoofed address (in dialer): %s\n", spoofedAddr)
|
||||||
|
|
||||||
|
conn, err := net.Dial(network, spoofedAddr)
|
||||||
|
return conn, err
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// TODO: Currently incomplete
|
||||||
|
if websocket.IsWebSocketUpgrade(r) {
|
||||||
|
fmt.Println("detected WebSocket upgrade request. doing initialization tasks...")
|
||||||
|
remoteAddr := ""
|
||||||
|
|
||||||
|
if strings.HasPrefix(sourceIP, "http://") {
|
||||||
|
remoteAddr += "ws://"
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(sourceIP, "https://") {
|
||||||
|
remoteAddr += "wss://"
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteAddr += hostHeader
|
||||||
|
|
||||||
|
// I'd like to preserve more headers, but Cookies is the only easy thing to implement...
|
||||||
|
newClientHeaders := make(http.Header)
|
||||||
|
|
||||||
|
newClientHeaders.Add("Cookie", r.Header.Get("Cookie"))
|
||||||
|
|
||||||
|
fmt.Printf("spoofed address (in handler): %s\n", remoteAddr)
|
||||||
|
wsClient, resp, err := wsDialer.Dial(remoteAddr, newClientHeaders)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to initialize client's (remote server) WebSocket for '%s': %s", r.URL.Path, err.Error())
|
||||||
|
http.Error(w, "Internal Server Error", 500)
|
||||||
|
|
||||||
|
logRequest(r.Method, r.URL.Path, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, values := range resp.Header {
|
||||||
|
for _, value := range values {
|
||||||
|
w.Header().Add(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wsServer, err := wsUpgrader.Upgrade(w, r, w.Header())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to initialize server's (us) WebSocket for '%s': %s", r.URL.Path, err.Error())
|
||||||
|
http.Error(w, "Internal Server Error", 500)
|
||||||
|
|
||||||
|
logRequest(r.Method, r.URL.Path, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
messageType, message, err := wsServer.ReadMessage()
|
||||||
|
|
||||||
|
if err != nil || messageType == websocket.CloseMessage {
|
||||||
|
if err != nil && err.Error() != "EOF" {
|
||||||
|
log.Printf("failed to read server's (us) WebSocket for '%s': %s", r.URL.Path, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
err := wsClient.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to close client's (remote server) WebSocket for '%s': %s", r.URL.Path, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
wsClient.WriteMessage(messageType, message)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
messageType, message, err := wsClient.ReadMessage()
|
||||||
|
|
||||||
|
if err != nil || messageType == websocket.CloseMessage {
|
||||||
|
if err != nil && err.Error() != "EOF" {
|
||||||
|
log.Printf("failed to read client's (remote server) WebSocket for '%s': %s", r.URL.Path, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
err := wsServer.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to close server's (remote server) WebSocket for '%s': %s", r.URL.Path, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
wsServer.WriteMessage(messageType, message)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
logRequest(r.Method, r.URL.Path, 101)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(r.Method, sourceIP+r.URL.Path, r.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to construct request for '%s': %s", r.URL.Path, err.Error())
|
||||||
|
http.Error(w, "Internal Server Error", 500)
|
||||||
|
|
||||||
|
logRequest(r.Method, r.URL.Path, r.Response.StatusCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Host = hostHeader
|
||||||
|
|
||||||
|
for key, values := range r.Header {
|
||||||
|
if key == "Host" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range values {
|
||||||
|
req.Header.Add(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to handle response for '%s': %s", r.URL.Path, err.Error())
|
||||||
|
http.Error(w, "Internal Server Error", 500)
|
||||||
|
|
||||||
|
logRequest(r.Method, r.URL.Path, r.Response.StatusCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, values := range resp.Header {
|
||||||
|
for _, value := range values {
|
||||||
|
w.Header().Add(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if resp.StatusCode >= 300 && resp.StatusCode <= 399 {
|
||||||
|
existingLocationHeaderUnparsed := resp.Header.Get("Location")
|
||||||
|
|
||||||
|
if existingLocationHeaderUnparsed != "" {
|
||||||
|
w.Header().Set("Location", existingLocationHeaderUnparsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
w.WriteHeader(resp.StatusCode)
|
||||||
|
|
||||||
|
totalBytesRead := int64(0)
|
||||||
|
byteBuffer := make([]byte, 65535)
|
||||||
|
|
||||||
|
for totalBytesRead != resp.ContentLength {
|
||||||
|
readContents, err := resp.Body.Read(byteBuffer)
|
||||||
|
totalBytesRead += int64(readContents)
|
||||||
|
|
||||||
|
if err != nil && totalBytesRead != resp.ContentLength {
|
||||||
|
log.Printf("failed to either read or finish reading response for '%s': %s", r.URL.Path, err.Error())
|
||||||
|
logRequest(r.Method, r.URL.Path, resp.StatusCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(byteBuffer[:readContents])
|
||||||
|
}
|
||||||
|
|
||||||
|
logRequest(r.Method, r.URL.Path, resp.StatusCode)
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Printf("Hostess is listening on %s", listenAddr)
|
||||||
|
log.Fatal(http.ListenAndServe(listenAddr, nil))
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue