Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | decouple connection code so that WebSockets work without first requiring initial AJAX connection |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
02630ca04a29f520123fe08ef251e728 |
User & Date: | stever 2013-01-18 16:01:13.323 |
Context
2013-01-18
| ||
19:25 | Added stubs for most commands. check-in: e2ff912839 user: gerald tags: trunk | |
16:01 | decouple connection code so that WebSockets work without first requiring initial AJAX connection check-in: 02630ca04a user: stever tags: trunk | |
15:30 | reformat demo to reflect the concept of rendering the page view for MVC style apps check-in: ea0ef51845 user: stever tags: trunk | |
Changes
Changes to server.tcl.
︙ | ︙ | |||
83 84 85 86 87 88 89 | append upgrade "WebSocket-Location: ws://localhost:9001/wsctrl\r\n" append upgrade "Sec-WebSocket-Accept: $acceptKey" append upgrade "\r\n\r\n" fconfigure $sock -translation binary puts -nonewline $sock $upgrade flush $sock fileevent $sock readable [list ws_receive junk $sock] | < > > > > > > > > > | > > | > | | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | append upgrade "WebSocket-Location: ws://localhost:9001/wsctrl\r\n" append upgrade "Sec-WebSocket-Accept: $acceptKey" append upgrade "\r\n\r\n" fconfigure $sock -translation binary puts -nonewline $sock $upgrade flush $sock fileevent $sock readable [list ws_receive junk $sock] set sessionid [lindex [split [dict get $data query] =] end] puts "Socket $sock upgraded to WebSocket for sessionid $sessionid" dict set ::session($sessionid) wsock $sock dict set ::sock($sock) sessionid $sessionid #use this after full message frames are implemented #toclient $sessionid [dict get $::session($sessionid) msgq] #for now I dish out small chucks of js to fit the 126 byte message length foreach message [split [string range [dict get $::session($sessionid) msgq] 0 end-1] \;] { #toclient $sessionid ${message}; puts "WSSERVERA: ${message};" ws_send $sock "${message};" update idletasks } dict set ::session($sessionid) msgq "" return 1 } else { #puts "\nVersion != 13 no good" close $sock return 0 } } |
︙ | ︙ | |||
116 117 118 119 120 121 122 | httpd loadrequest $sock data query if {![info exists data(url)]} {return} regsub {(^http://[^/]+)?} $data(url) {} url puts stderr "URL: $url" set url [string trimleft $url /] switch -glob -- $url { "" {httpd return $sock [filecontents index.html]} | | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | httpd loadrequest $sock data query if {![info exists data(url)]} {return} regsub {(^http://[^/]+)?} $data(url) {} url puts stderr "URL: $url" set url [string trimleft $url /] switch -glob -- $url { "" {httpd return $sock [filecontents index.html]} "*.tcl" {httpd return $sock [newSession $sock [string trimleft $url /] lib/wtkcoreapp.html [array get data]]} "*.js" {httpd return $sock [filecontents $url] -mimetype "text/javascript"} "*.gif" {httpd returnfile $sock $url $url "image/gif" [clock seconds] 1 -static } "*.png" {httpd returnfile $sock $url $url "image/png" [clock seconds] 1 -static } "*.jpg" {httpd returnfile $sock $url $url "image/jpg" [clock seconds] 1 -static } "*.ico" {httpd returnfile $sock $url $url "image/x-icon" [clock seconds] 1 -static } "wtkpoll.html" {if !{[sendany $sock $query(sessionid)]} {error "pending"}} "wtkcb.html" {fromclient $query(sessionid) $query(cmd)} |
︙ | ︙ | |||
144 145 146 147 148 149 150 | # This is called when a client first loads one of our 'application' pages. We create a new # application instance (interpreter), load and initialize "wtk" in that interpreter, and then # load in the Tcl script for the application we're running. We return a HTML page that will # load up the client side of wtk and cause the browser to initiate a connection back to the # server. Notably, this page includes the 'sessionid' we've generated for the application # instance, which is unique to each client. | | > > > | 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | # This is called when a client first loads one of our 'application' pages. We create a new # application instance (interpreter), load and initialize "wtk" in that interpreter, and then # load in the Tcl script for the application we're running. We return a HTML page that will # load up the client side of wtk and cause the browser to initiate a connection back to the # server. Notably, this page includes the 'sessionid' we've generated for the application # instance, which is unique to each client. proc newSession {sock script webpage data} { set sessionid [incr ::sessioncounter] set interp [interp create] dict set ::session($sessionid) interp $interp dict set ::session($sessionid) msgq "" dict set ::session($sessionid) sock $sock dict set ::session($sessionid) wsock 0 if {[catch {$interp eval source lib/wtk-base.tcl}]!=0} {puts $::errorInfo} $interp alias sendto toclient $sessionid $interp eval wtk::init sendto #pass in the server header vars first $interp eval [list set ::reqdata $data] #now source the app script if {[catch {$interp eval source $script}]!=0} {puts $::errorInfo} if {[file exists favicon.ico]} { set link "<link href='data:image/x-icon;base64,%%%BASE64ICO%%%' rel='icon' type='image/x-icon' />" set favicon [string map "%%%BASE64ICO%%% [binary encode base64 [filecontents favicon.ico]]" $link] } else { set favicon "" } |
︙ | ︙ |
Changes to widgets/wtk.js.
1 2 3 4 | var wtk = { widgets : new Array(), objs : new Array(), | | > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var wtk = { widgets : new Array(), objs : new Array(), pause : function(ms) { ms += new Date().getTime(); while (new Date() < ms){} }, /* * Initialize, and manage two AJAX connections to the server; one is used to send * messages, and the other polling connection is used to receive messages. These * correspond to the routines in server.tcl, and could be equally well replaced by * a different and/or more reliable communications channel. */ |
︙ | ︙ | |||
30 31 32 33 34 35 36 | ws_uri += "/wsctrl"; ws_uri += "?sessionid=" + sessionid; wtk.connection = new WebSocket(ws_uri); wtk.connection.onopen = function(){ /*Send a small message to the console once the connection is established */ | | > > | | > | > > > | | > < < < < < < < < < > | | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | ws_uri += "/wsctrl"; ws_uri += "?sessionid=" + sessionid; wtk.connection = new WebSocket(ws_uri); wtk.connection.onopen = function(){ /*Send a small message to the console once the connection is established */ console.log('WebSocket connection open'); wtk.conntype = "websocket"; wtk.wsfailcnt = 0; }; wtk.connection.onclose = function(){ console.log('WebSocket connection closed'); if (wtk.conntype = "websocket" && wtk.wsfailcnt <= 4) { console.log('WebSocket connection retry '+wtk.wsfailcnt+' of 4'); wtk.pause(1000); wtk.wsfailcnt = wtk.wsfailcnt + 1; wtk.init(); } else { console.log('Initiating AJAX fallback connection'); setTimeout(wtk.poller,100); }; }; wtk.connection.onerror = function(error){ console.log('WebSocket Error detected: ' + error); }; wtk.connection.onmessage = function(e){ var server_message = e.data; //console.log(server_message); eval(server_message); }; } else { /*WebSockets are not supported. Fallback to long-polling */ console.log('Browser doesnt support WebSockets, using AJAX instead'); setTimeout(wtk.poller,100); }; }, poller : function() {$.ajax({type:'GET', url:'wtkpoll.html?sessionid='+wtk.sessionid, dataType:'script', complete: function() {setTimeout(wtk.poller,100);}, error: function(jqXHR, textStatus, errorThrown) { //alert("AJAX server connection interrupted\nPress OK to reconnect."); console.log('AJAX server connecton interrupted '+textStatus+' '+errorThrown); setTimeout(location.reload(1),200);} }); }, sendto : function(msg) { if (wtk.conntype != "websocket") { $.get('wtkcb.html?sessionid='+wtk.sessionid, {cmd : msg}); |
︙ | ︙ |