sarveshpatel commited on
Commit
4e5b86f
·
verified ·
1 Parent(s): e312e71

Create main.go

Browse files
Files changed (1) hide show
  1. main.go +208 -0
main.go ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "io"
6
+ "log"
7
+ "net/http"
8
+ "sync"
9
+ "sync/atomic"
10
+ "time"
11
+
12
+ "github.com/gorilla/websocket"
13
+ )
14
+
15
+ type Request struct {
16
+ ID uint64 `json:"id"`
17
+ Method string `json:"method"`
18
+ Path string `json:"path"`
19
+ Headers map[string][]string `json:"headers"`
20
+ Body []byte `json:"body"`
21
+ }
22
+
23
+ type Response struct {
24
+ ID uint64 `json:"id"`
25
+ Status int `json:"status"`
26
+ Headers map[string][]string `json:"headers"`
27
+ Body []byte `json:"body"`
28
+ }
29
+
30
+ type Tunnel struct {
31
+ conn *websocket.Conn
32
+ mu sync.Mutex
33
+ pending map[uint64]chan Response
34
+ pendMu sync.RWMutex
35
+ counter uint64
36
+ lastPing time.Time
37
+ }
38
+
39
+ var (
40
+ tunnel *Tunnel
41
+ tunnelMu sync.RWMutex
42
+ upgrader = websocket.Upgrader{
43
+ CheckOrigin: func(r *http.Request) bool { return true },
44
+ ReadBufferSize: 65536,
45
+ WriteBufferSize: 65536,
46
+ }
47
+ authToken = "your-secret-token-change-me"
48
+ )
49
+
50
+ func main() {
51
+ http.HandleFunc("/", handler)
52
+ log.Println("Server starting on :7860")
53
+ log.Fatal(http.ListenAndServe(":7860", nil))
54
+ }
55
+
56
+ func handler(w http.ResponseWriter, r *http.Request) {
57
+ switch r.URL.Path {
58
+ case "/_tunnel":
59
+ handleTunnel(w, r)
60
+ case "/_health":
61
+ handleHealth(w)
62
+ default:
63
+ handleProxy(w, r)
64
+ }
65
+ }
66
+
67
+ func handleHealth(w http.ResponseWriter) {
68
+ tunnelMu.RLock()
69
+ connected := tunnel != nil
70
+ tunnelMu.RUnlock()
71
+
72
+ w.Header().Set("Content-Type", "application/json")
73
+ json.NewEncoder(w).Encode(map[string]any{
74
+ "status": "ok",
75
+ "connected": connected,
76
+ "time": time.Now().Unix(),
77
+ })
78
+ }
79
+
80
+ func handleTunnel(w http.ResponseWriter, r *http.Request) {
81
+ if r.Header.Get("X-Tunnel-Token") != authToken {
82
+ http.Error(w, "unauthorized", http.StatusUnauthorized)
83
+ return
84
+ }
85
+
86
+ conn, err := upgrader.Upgrade(w, r, nil)
87
+ if err != nil {
88
+ log.Printf("upgrade failed: %v", err)
89
+ return
90
+ }
91
+
92
+ t := &Tunnel{
93
+ conn: conn,
94
+ pending: make(map[uint64]chan Response),
95
+ lastPing: time.Now(),
96
+ }
97
+
98
+ tunnelMu.Lock()
99
+ if tunnel != nil {
100
+ tunnel.conn.Close()
101
+ }
102
+ tunnel = t
103
+ tunnelMu.Unlock()
104
+
105
+ log.Println("client connected")
106
+ t.readLoop()
107
+
108
+ tunnelMu.Lock()
109
+ if tunnel == t {
110
+ tunnel = nil
111
+ }
112
+ tunnelMu.Unlock()
113
+ log.Println("client disconnected")
114
+ }
115
+
116
+ func (t *Tunnel) readLoop() {
117
+ defer t.conn.Close()
118
+
119
+ for {
120
+ _, data, err := t.conn.ReadMessage()
121
+ if err != nil {
122
+ return
123
+ }
124
+
125
+ var resp Response
126
+ if json.Unmarshal(data, &resp) != nil {
127
+ continue
128
+ }
129
+
130
+ t.pendMu.RLock()
131
+ ch, ok := t.pending[resp.ID]
132
+ t.pendMu.RUnlock()
133
+
134
+ if ok {
135
+ select {
136
+ case ch <- resp:
137
+ default:
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ func (t *Tunnel) send(req Request) (Response, error) {
144
+ ch := make(chan Response, 1)
145
+
146
+ t.pendMu.Lock()
147
+ t.pending[req.ID] = ch
148
+ t.pendMu.Unlock()
149
+
150
+ defer func() {
151
+ t.pendMu.Lock()
152
+ delete(t.pending, req.ID)
153
+ t.pendMu.Unlock()
154
+ }()
155
+
156
+ data, _ := json.Marshal(req)
157
+
158
+ t.mu.Lock()
159
+ err := t.conn.WriteMessage(websocket.TextMessage, data)
160
+ t.mu.Unlock()
161
+
162
+ if err != nil {
163
+ return Response{}, err
164
+ }
165
+
166
+ select {
167
+ case resp := <-ch:
168
+ return resp, nil
169
+ case <-time.After(60 * time.Second):
170
+ return Response{Status: 504}, nil
171
+ }
172
+ }
173
+
174
+ func handleProxy(w http.ResponseWriter, r *http.Request) {
175
+ tunnelMu.RLock()
176
+ t := tunnel
177
+ tunnelMu.RUnlock()
178
+
179
+ if t == nil {
180
+ http.Error(w, "tunnel not connected", http.StatusBadGateway)
181
+ return
182
+ }
183
+
184
+ body, _ := io.ReadAll(r.Body)
185
+
186
+ req := Request{
187
+ ID: atomic.AddUint64(&t.counter, 1),
188
+ Method: r.Method,
189
+ Path: r.URL.RequestURI(),
190
+ Headers: r.Header,
191
+ Body: body,
192
+ }
193
+
194
+ resp, err := t.send(req)
195
+ if err != nil {
196
+ http.Error(w, "tunnel error", http.StatusBadGateway)
197
+ return
198
+ }
199
+
200
+ for k, vals := range resp.Headers {
201
+ for _, v := range vals {
202
+ w.Header().Add(k, v)
203
+ }
204
+ }
205
+
206
+ w.WriteHeader(resp.Status)
207
+ w.Write(resp.Body)
208
+ }