Skip to content

Commit d383268

Browse files
committed
Enhance tunnel ID extraction and routing logic in server
- Implement fallback mechanisms for tunnel ID retrieval from cookies and Referer headers. - Update request handling to set cookies for path-based routing. - Introduce a new function to extract tunnel ID from Referer URLs. - Ensure proper error handling for missing or invalid tunnel IDs.
1 parent 020401f commit d383268

2 files changed

Lines changed: 82 additions & 11 deletions

File tree

internal/server/server.go

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -210,26 +210,66 @@ func (s *Server) handleConnect(w http.ResponseWriter, r *http.Request) {
210210
// handleRequest handles incoming HTTP requests and routes them to the appropriate tunnel.
211211
func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
212212
var tunnelID string
213+
var fromPath bool
213214

214215
if s.config.RoutingMode == protocol.RoutingModePath {
215216
// Extract tunnel ID from the first path segment
216217
tunnelID = protocol.ExtractTunnelIDFromPath(r.URL.Path)
217-
if tunnelID == "" {
218-
http.Error(w, "Missing tunnel ID in path", http.StatusNotFound)
219-
return
218+
if tunnelID != "" && s.registry.Exists(tunnelID) {
219+
fromPath = true
220+
}
221+
222+
// If no valid tunnel ID in path, try cookie
223+
if !fromPath {
224+
if cookie, err := r.Cookie("exio_tunnel"); err == nil && cookie.Value != "" {
225+
if s.registry.Exists(cookie.Value) {
226+
tunnelID = cookie.Value
227+
s.logger.Printf("Cookie routing: %s (tunnel: %s)", r.URL.Path, tunnelID)
228+
}
229+
}
220230
}
221231

222-
// Rewrite the path to strip the tunnel ID prefix
223-
originalPath := r.URL.Path
224-
r.URL.Path = protocol.StripTunnelIDPrefix(r.URL.Path, tunnelID)
225-
r.RequestURI = r.URL.RequestURI()
232+
// If still no tunnel, try Referer header as fallback
233+
if tunnelID == "" || !s.registry.Exists(tunnelID) {
234+
referer := r.Header.Get("Referer")
235+
if referer != "" {
236+
refererTunnelID := protocol.ExtractTunnelIDFromReferer(referer)
237+
if refererTunnelID != "" && s.registry.Exists(refererTunnelID) {
238+
tunnelID = refererTunnelID
239+
s.logger.Printf("Referer routing: %s (tunnel: %s)", r.URL.Path, tunnelID)
240+
}
241+
}
242+
}
226243

227-
// Also update RawPath if set
228-
if r.URL.RawPath != "" {
229-
r.URL.RawPath = protocol.StripTunnelIDPrefix(r.URL.RawPath, tunnelID)
244+
if tunnelID == "" || !s.registry.Exists(tunnelID) {
245+
http.Error(w, "Tunnel not found", http.StatusNotFound)
246+
return
230247
}
231248

232-
s.logger.Printf("Path routing: %s -> %s (tunnel: %s)", originalPath, r.URL.Path, tunnelID)
249+
// Set cookie for future requests (when accessing via path with tunnel ID)
250+
if fromPath {
251+
http.SetCookie(w, &http.Cookie{
252+
Name: "exio_tunnel",
253+
Value: tunnelID,
254+
Path: "/",
255+
MaxAge: 3600, // 1 hour
256+
HttpOnly: true,
257+
SameSite: http.SameSiteLaxMode,
258+
})
259+
}
260+
261+
// Only strip tunnel ID prefix if it was in the path
262+
if fromPath {
263+
originalPath := r.URL.Path
264+
r.URL.Path = protocol.StripTunnelIDPrefix(r.URL.Path, tunnelID)
265+
r.RequestURI = r.URL.RequestURI()
266+
267+
if r.URL.RawPath != "" {
268+
r.URL.RawPath = protocol.StripTunnelIDPrefix(r.URL.RawPath, tunnelID)
269+
}
270+
271+
s.logger.Printf("Path routing: %s -> %s (tunnel: %s)", originalPath, r.URL.Path, tunnelID)
272+
}
233273
} else {
234274
// Extract subdomain from Host header (existing behavior)
235275
tunnelID = protocol.ExtractSubdomain(r, s.config.BaseDomain)

pkg/protocol/protocol.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,34 @@ func StripTunnelIDPrefix(path, tunnelID string) string {
178178

179179
return path
180180
}
181+
182+
// ExtractTunnelIDFromReferer extracts the tunnel ID from a Referer URL.
183+
// For example, "https://tunnel.example.com/bold-owl-716/page" returns "bold-owl-716".
184+
func ExtractTunnelIDFromReferer(referer string) string {
185+
if referer == "" {
186+
return ""
187+
}
188+
189+
// Find the path portion after the host
190+
// Look for :// then find the next /
191+
schemeEnd := 0
192+
for i := 0; i < len(referer)-2; i++ {
193+
if referer[i] == ':' && referer[i+1] == '/' && referer[i+2] == '/' {
194+
schemeEnd = i + 3
195+
break
196+
}
197+
}
198+
199+
// Find the start of the path (first / after the host)
200+
pathStart := schemeEnd
201+
for pathStart < len(referer) && referer[pathStart] != '/' {
202+
pathStart++
203+
}
204+
205+
if pathStart >= len(referer) {
206+
return ""
207+
}
208+
209+
// Extract tunnel ID from the path
210+
return ExtractTunnelIDFromPath(referer[pathStart:])
211+
}

0 commit comments

Comments
 (0)