Merge branch 'master' into ws
[brisk.git] / web / Obj / transports.phh
index 08ad7a0..3b6e79c 100644 (file)
  *
  */
 
+/*
+ *  test: SO x Browser
+ *  Values: Y: works, N: not works, @: continuous download,
+ *          D: continuous download after first reload
+ *
+ *  Stream IFRAME:
+ *
+ * Iframe| IW | FF | Ch | Op | Ko | IE
+ * ------+----+----+----+----+----+----
+ *   Lnx | D  |    | @  |    | @  | x
+ *   Win | x  | D  | @  | @  |    | D
+ *   Mac | x  |    |    |    |    |
+ *
+ *
+ *   XHR | IW | FF | Ch | Op | Ko | IE
+ * ------+----+----+----+----+----+----
+ *   Lnx | Y  |    | ^D |    | Y  | x
+ *   Win | x  | Y  | Y  |    |    | N
+ *   Mac | x  |    |    |    |    |
+ *
+ *
+ * HtmlFl| IW | FF | Ch | Op | Ko | IE
+ * ------+----+----+----+----+----+----
+ *   Lnx | N  |    |    |    | N  |
+ *   Win | x  | N  | N  |    |    | Y* (* seems delay between click and load of a new page)
+ *   Mac | x  |    |    |    |    |
+ *
+ *
+ */
+
+
 class Transport_template {
 
     function Transport_template() {
     }
 
-    function init($enc, &$header_out, $init_string, $base, $step)
+    // return string value is appended to the content of the returned page
+    function init($enc, $header, &$header_out, $init_string, $base, $step)
     {
     }
 
-    static function fini($init_string, $blockerr)
+    static function fini($init_string, $base, $blockerr)
     {
     }
 
     function chunk($step, $cont)
     {
     }
+
+    function is_chunked()
+    {
+    }
+}
+
+class Transport_websocket {
+    $magicGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
+    function Transport_websocket() {
+    }
+
+    protected function doHandshake($user, $buffer) {
+        $magicGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+        $headers = array();
+        $lines = explode("\n",$buffer);
+        foreach ($lines as $line) {
+            if (strpos($line,":") !== false) {
+                $header = explode(":",$line,2);
+                $headers[strtolower(trim($header[0]))] = trim($header[1]);
+            } else if (stripos($line,"get ") !== false) {
+                preg_match("/GET (.*) HTTP/i", $buffer, $reqResource);
+                $headers['get'] = trim($reqResource[1]);
+            }
+        }
+        if (isset($headers['get'])) {
+            $user->requestedResource = $headers['get'];
+        } else {
+            // todo: fail the connection
+            $handshakeResponse = "HTTP/1.1 405 Method Not Allowed\r\n\r\n";                    
+        }
+        if (!isset($headers['host']) || !$this->checkHost($headers['host'])) {
+            $handshakeResponse = "HTTP/1.1 400 Bad Request";
+        }
+        if (!isset($headers['upgrade']) || strtolower($headers['upgrade']) != 'websocket') {
+            $handshakeResponse = "HTTP/1.1 400 Bad Request";
+        } 
+        if (!isset($headers['connection']) || strpos(strtolower($headers['connection']), 'upgrade') === FALSE) {
+            $handshakeResponse = "HTTP/1.1 400 Bad Request";
+        }
+        if (!isset($headers['sec-websocket-key'])) {
+            $handshakeResponse = "HTTP/1.1 400 Bad Request";
+        } else {
+            
+        }
+        if (!isset($headers['sec-websocket-version']) || strtolower($headers['sec-websocket-version']) != 13) {
+            $handshakeResponse = "HTTP/1.1 426 Upgrade Required\r\nSec-WebSocketVersion: 13";
+        }
+        if (($this->headerOriginRequired && !isset($headers['origin']) ) || ($this->headerOriginRequired && !$this->checkOrigin($headers['origin']))) {
+            $handshakeResponse = "HTTP/1.1 403 Forbidden";
+        }
+        if (($this->headerSecWebSocketProtocolRequired && !isset($headers['sec-websocket-protocol'])) || ($this->headerSecWebSocketProtocolRequired && !$this->checkWebsocProtocol($header['sec-websocket-protocol']))) {
+            $handshakeResponse = "HTTP/1.1 400 Bad Request";
+        }
+        if (($this->headerSecWebSocketExtensionsRequired && !isset($headers['sec-websocket-extensions'])) || ($this->headerSecWebSocketExtensionsRequired && !$this->checkWebsocExtensions($header['sec-websocket-extensions']))) {
+            $handshakeResponse = "HTTP/1.1 400 Bad Request";
+        }
+        
+        // Done verifying the _required_ headers and optionally required headers.
+        
+        if (isset($handshakeResponse)) {
+            socket_write($user->socket,$handshakeResponse,strlen($handshakeResponse));
+            $this->disconnect($user->socket);
+            return false;
+        }
+        
+        $user->headers = $headers;
+        $user->handshake = $buffer;
+        
+        $webSocketKeyHash = sha1($headers['sec-websocket-key'] . $magicGUID);
+        
+        $rawToken = "";
+        for ($i = 0; $i < 20; $i++) {
+            $rawToken .= chr(hexdec(substr($webSocketKeyHash,$i*2, 2)));
+        }
+        $handshakeToken = base64_encode($rawToken) . "\r\n";
+        
+        $subProtocol = (isset($headers['sec-websocket-protocol'])) ? $this->processProtocol($headers['sec-websocket-protocol']) : "";
+        $extensions = (isset($headers['sec-websocket-extensions'])) ? $this->processExtensions($headers['sec-websocket-extensions']) : "";
+        
+        $handshakeResponse = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $handshakeToken$subProtocol$extensions\r\n";
+        socket_write($user->socket,$handshakeResponse,strlen($handshakeResponse));
+        $this->connected($user);
+    }
+    
+
+    function init($enc, $header, &$header_out, $init_string, $base, $step)
+    {
+        
+
+
+
+        $ret = sprintf("@BEGIN@ /* %s */ @END@", $init_string);
+        if ($enc != 'plain')
+            $header_out['Content-Encoding'] = $enc;
+        $header_out['Cache-Control'] = 'no-cache, must-revalidate';     // HTTP/1.1
+        $header_out['Expires']       = 'Mon, 26 Jul 1997 05:00:00 GMT'; // Date in the past
+        $header_out['Content-type']  = 'application/xml; charset="utf-8"';
+
+        return ($ret);
+    }
+
+    static function fini($init_string, $base, $blockerr)
+    {
+        return (sprintf('@BEGIN@ %s window.onbeforeunload = null; window.onunload = null; document.location.assign("%sindex.php"); @END@',  ($blockerr ? 'xstm.stop(); ' : ''), $base));
+        return ("");
+    }
+
+    function chunk($step, $cont)
+    {
+        return ("@BEGIN@".$cont."@END@");
+    }
 }
 
 class Transport_xhr {
@@ -45,9 +189,9 @@ class Transport_xhr {
     function Transport_xhr() {
     }
 
-    function init($enc, &$header_out, $init_string, $base, $step)
+    function init($enc, $header, &$header_out, $init_string, $base, $step)
     {
-        $ret = $init_string;
+        $ret = sprintf("@BEGIN@ /* %s */ @END@", $init_string);
         if ($enc != 'plain')
             $header_out['Content-Encoding'] = $enc;
         $header_out['Cache-Control'] = 'no-cache, must-revalidate';     // HTTP/1.1
@@ -57,8 +201,9 @@ class Transport_xhr {
         return ($ret);
     }
 
-    static function fini($init_string, $blockerr)
+    static function fini($init_string, $base, $blockerr)
     {
+        return (sprintf('@BEGIN@ %s window.onbeforeunload = null; window.onunload = null; document.location.assign("%sindex.php"); @END@',  ($blockerr ? 'xstm.stop(); ' : ''), $base));
         return ("");
     }
 
@@ -66,6 +211,11 @@ class Transport_xhr {
     {
         return ("@BEGIN@".$cont."@END@");
     }
+
+    function is_chunked()
+    {
+        return TRUE;
+    }
 }
 
 class Transport_iframe {
@@ -73,7 +223,7 @@ class Transport_iframe {
     function Transport_iframe() {
     }
 
-    function init($enc, &$header_out, $init_string, $base, $step)
+    function init($enc, $header, &$header_out, $init_string, $base, $step)
     {
         $ret = "";
 
@@ -92,7 +242,7 @@ var xynt_streaming = \"ready\";", $base, $base);
         if ($step > 0)
             $ret .= sprintf("last_clean = %d;\n", ($step-1));
         $ret .= sprintf("
-window.onload = function () { if (xynt_streaming != \"ready\") { xynt_streaming.reload(); } };
+window.onload = function () { try { if (xynt_streaming != \"ready\") { xynt_streaming.transp.stopped = true; } } catch(e) { /* console.log(\"catcha\"); */ } };
 </script> 
 </head>
 <body>");
@@ -101,7 +251,7 @@ window.onload = function () { if (xynt_streaming != \"ready\") { xynt_streaming.
         return ($ret);
     }
 
-    static function fini($init_string, $blockerr)
+    static function fini($init_string, $base, $blockerr)
     {
         $ret = "";
         $ret .= sprintf("<html>
@@ -109,9 +259,9 @@ window.onload = function () { if (xynt_streaming != \"ready\") { xynt_streaming.
 <script type=\"text/javascript\" src=\"%scommons.js\"></script>
 <script type=\"text/javascript\" src=\"%sxynt-streaming-ifra.js\"></script>
 <script type=\"text/javascript\">
-var xynt_streaming = \"ready\";", self::base_get(), self::base_get());
+var xynt_streaming = \"ready\";", $base, $base);
         $ret .= sprintf("
-window.onload = function () { if (xynt_streaming != \"ready\") { xynt_streaming.reload(); } };
+window.onload = function () { try { if (xynt_streaming != \"ready\") { xynt_streaming.reload(); } } catch(e) { /* console.log(\"catcha\"); */ } };
 </script>
 </head>
 <body>");
@@ -134,9 +284,41 @@ push(null);\n// -->\n</script>", $step);
 push(\"%s\");\n// -->\n</script>", $step, escpush($cont) );
         }
     }
+
+    function is_chunked()
+    {
+        return TRUE;
+    }
 }
 
 class Transport_htmlfile extends Transport_iframe {
 }
 
+class Transport {
+    function Transport()
+    {
+    }
+
+    static function create($transp)
+    {
+        if ($transp == 'xhr') {
+            return new Transport_xhr();
+        }
+        else if ($transp == 'htmlfile') {
+            return new Transport_htmlfile();
+        }
+        else  {
+            return new Transport_iframe();
+        }
+    }
+    static function gettype($transp)
+    {
+        if ($transp == 'xhr' || $transp == 'htmlfile') {
+            return "Transport_".$transp;
+        }
+        else {
+            return 'Transport_iframe';
+        }
+    }
+}
 ?>
\ No newline at end of file