# 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 }
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