Provide support for the CONNECT method for the http proxy.
authorPascal Scheffers <pascalscheffers@users.sourceforge.net>
Fri, 27 Nov 2009 12:32:57 +0000 (12:32 +0000)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Fri, 27 Nov 2009 20:30:19 +0000 (20:30 +0000)
Applied sourceforge patch #1076720 with modifications to make it
work right.

Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
bin/sockspy.tcl

index f094b78b21001aa9496aa2ba6845fa7d3e9ce101..f3f2605ff41c69e5898fae60e09fc3b4d1dd9448 100644 (file)
@@ -604,16 +604,35 @@ proc sockReadable {fromSock toSock who} {
 # to forward to.
 # 
 proc ProxyConnect {fromSock data} {
+    set skip 0
     set line1 [string range $data 0 [string first "\n" $data]]
     set line1 [string trimright $line1 "\r\n"]
     INFO "proxy request \"$line1\"" meta2
-    set bad [regexp {^([^ ]+) +([^ ]+)} $line1 -> method uri]
-    if {$bad == 0} {
+    if {![regexp {^([^ ]+) +([^ ]+)} $line1 -> method uri]} {
        return -code error "failed to parse a uri from '$line1'"
     }
+    
+    set method [string trim [string toupper $method]]
+    if {$method eq "CONNECT"} {
+        foreach {host port} [split $uri :] break
+        if {$port eq ""} {set port 443}
+        set uri "https://$host:$port"
+        # Find the end of headers - if we do not have that yet then
+        # we will have to do some reading until we have that.
+        set pos [string first \r\n\r\n $data]
+        if { $pos == -1 } {
+            INFO "waiting for additional headers for CONNECT proxying"
+            set skip 1
+        } else {
+            incr pos 4
+            set data [string range $data $pos end]
+            INFO "[string length $data] bytes to send for CONNECT"
+        }
+    }
+
     set state(uri) $uri                                ;# For debugging
     array set URI [::uri::split $uri]
-    if {$URI(scheme) != "http"} {
+    if {[lsearch -nocase {http https} $URI(scheme)] == -1} {
         return -code error "cannot proxy the '$URI(scheme)' protocol"
     }
     if {$URI(port) == ""} { set URI(port) 80 }
@@ -621,16 +640,64 @@ proc ProxyConnect {fromSock data} {
     if {$bad} {
        return -code error "cannot connect to $URI(host):$URI(port) => $reason"
     }
+    if {$method eq "CONNECT"} {
+       # We must send an HTTP response header to the client
+       set s "HTTP/1.1 200 OK\nServer: SockSpy/$::state(version)\n"
+       insertData meta $s
+       puts $fromSock $s
+    }
     INFO "fowarding $method request to $URI(host):$URI(port)" meta2
-    fileevent $fromSock readable \
-       [list sockReadable $fromSock $sockServ client]
+    if {$skip} {
+       fileevent $fromSock readable \
+           [list ProxySkip $fromSock $sockServ]
+    } else {
+       fileevent $fromSock readable \
+           [list sockReadable $fromSock $sockServ client]
+    }
     fconfigure $sockServ -blocking 0 -buffering none -translation binary
     fileevent $sockServ readable \
        [list sockReadable $sockServ $fromSock server]
+    
     puts -nonewline $sockServ $data
     return
 }
 
+##+##########################################################################
+# 
+# ProxySkip
+# 
+# Called from a new CONNECT proxy connection when we still need to skip the 
+# http header.
+# 
+proc ProxySkip {fromSock toSock} {
+    global state
+    set data [read $fromSock]
+    if {[string length $data] == 0} {
+       close $fromSock
+       catch { close $toSock }
+       INFO "----- closed connection -----"
+       INFO "waiting for new connection..." 
+       return
+    }
+    set pos [string first \r\n\r\n $data]
+    if { $pos == -1 } {
+       # Just drop the data.
+       return
+    }
+    incr pos 4
+    set data [string range $data $pos end]
+    if { [string length $data] > 0 } {
+       # Forward the real payload
+       catch { puts -nonewline $toSock $data } ;
+        insertData client $data
+    }
+    # And let the normal data handler do the rest:
+    fileevent $fromSock readable \
+        [list sockReadable $fromSock $toSock client]
+
+    update    
+}
+
 ##+##########################################################################
 # 
 # clntConnect -- Called when we get a new client connection