Changeset 12181
- Timestamp:
- 01/27/08 22:21:23 (12 months ago)
- Location:
- dojox/trunk/io/proxy
- Files:
-
- 3 modified
-
xip.js (modified) (5 diffs)
-
xip_client.html (modified) (6 diffs)
-
xip_server.html (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
dojox/trunk/io/proxy/xip.js
r12124 r12181 12 12 //Usage of XHR IFrame Proxying does not work from local disk in Safari. 13 13 14 xipClientUrl: dojo.config["xipClientUrl"] || dojo.moduleUrl("dojox.io.proxy", "xip_client.html"), 15 14 /* 15 This code is really focused on just sending one complete request to the server, and 16 receiving one complete response per iframe. The code does not expect to reuse iframes for multiple XHR request/response 17 sequences. This might be reworked later if performance indicates a need for it. 18 19 xip fragment identifier/hash values have the form: 20 #id:cmd:realEncodedMessage 21 22 id: some ID that should be unique among message fragments. No inherent meaning, 23 just something to make sure the hash value is unique so the message 24 receiver knows a new message is available. 25 26 cmd: command to the receiver. Valid values are: 27 - init: message used to init the frame. Sent as the first URL when loading 28 the page. Contains some config parameters. 29 - loaded: the remote frame is loaded. Only sent from xip_client.html to this module. 30 - ok: the message that this page sent was received OK. The next message may 31 now be sent. 32 - start: the start message of a block of messages (a complete message may 33 need to be segmented into many messages to get around the limitiations 34 of the size of an URL that a browser accepts. 35 - part: indicates this is a part of a message. 36 - end: the end message of a block of messages. The message can now be acted upon. 37 If the message is small enough that it doesn't need to be segmented, then 38 just one hash value message can be sent with "end" as the command. 39 40 To reassemble a segmented message, the realEncodedMessage parts just have to be concatenated 41 together. 42 */ 43 44 xipClientUrl: ((dojo.config || djConfig)["xipClientUrl"]) || dojo.moduleUrl("dojox.io.proxy", "xip_client.html"), 45 46 47 //MSIE has the lowest limit for URLs with fragment identifiers, 48 //at around 4K. Choosing a slightly smaller number for good measure. 49 urlLimit: 4000, 50 51 _callbackName: (dojox._scopeName || "dojox") + ".io.proxy.xip.fragmentReceived", 16 52 _state: {}, 17 53 _stateIdCounter: 0, 18 19 needFrameRecursion: function(){ 20 return (dojo.isIE >= 7); 21 }, 22 23 send: function(facade){ 54 _isWebKit: navigator.userAgent.indexOf("WebKit") != -1, 55 56 send: function(facade){ 24 57 var stateId = "XhrIframeProxy" + (this._stateIdCounter++); 25 58 facade._stateId = stateId; 26 27 28 var frameUrl = this.xipClientUrl + "#0:init:id=" + stateId + "&server=" 29 + encodeURIComponent(facade._ifpServerUrl) + "&fr=false"; 30 if(this.needFrameRecursion()){ 31 //IE7 hack. Need to load server URL, and have that load the xip_client.html. 32 //Also, this server URL needs to different from the one eventually loaded by xip_client.html 33 //Otherwise, IE7 will not load it. Funky. 34 var fullClientUrl = window.location.href.split("#")[0].split("?")[0]; 35 if((this.xipClientUrl + "").charAt(0) == "/"){ 36 var endIndex = fullClientUrl.indexOf("://"); 37 endIndex = fullClientUrl.indexOf("/", endIndex + 3); 38 fullClientUrl = fullClientUrl.substring(0, endIndex); 39 }else{ 40 fullClientUrl = fullClientUrl.substring(0, fullClientUrl.lastIndexOf("/") + 1); 41 } 42 fullClientUrl += this.xipClientUrl; 43 44 var serverUrl = facade._ifpServerUrl 45 + (facade._ifpServerUrl.indexOf("?") == -1 ? "?" : "&") + "dojo.fr=1"; 46 47 frameUrl = serverUrl + "#0:init:id=" + stateId + "&client=" 48 + encodeURIComponent(fullClientUrl) + "&fr=" + this.needFrameRecursion(); //fr is for Frame Recursion 49 } 50 51 this._state[stateId] = { 52 facade: facade, 53 stateId: stateId, 54 clientFrame: dojo.io.iframe.create(stateId, "", frameUrl) 55 }; 56 57 return stateId; 58 }, 59 59 var url = this.xipClientUrl; 60 61 //Make sure we are not dealing with javascript urls, just to be safe. 62 if(!url.split(":")[0].match(/javascript/i) && !facade._ifpServerUrl.split(":")[0].match(/javascript/i)){ 63 //Make xip_client a full URL. 64 var colonIndex = url.indexOf(":"); 65 var slashIndex = url.indexOf("/"); 66 if(colonIndex == -1 || slashIndex < colonIndex){ 67 //No colon or we are starting with a / before a colon, so we need to make a full URL. 68 var loc = window.location.href; 69 if(slashIndex == 0){ 70 //Have a full path, just need the domain. 71 url = loc.substring(0, loc.indexOf("/", 9)) + url; //Using 9 to get past http(s):// 72 }else{ 73 url = loc.substring(0, (loc.lastIndexOf("/") + 1)) + url; 74 } 75 } 76 77 var frameUrl = facade._ifpServerUrl + "#0:init:id=" + stateId + "&client=" 78 + encodeURIComponent(url) + "&callback=" + encodeURIComponent(this._callbackName); 79 80 this._state[stateId] = { 81 facade: facade, 82 stateId: stateId, 83 clientFrame: dojo.io.iframe.create(stateId, "", frameUrl), 84 isSending: false, 85 serverUrl: facade._ifpServerUrl, 86 requestData: null, 87 responseMessage: "", 88 requestParts: [], 89 idCounter: 1, 90 partIndex: 0, 91 serverWindow: null 92 }; 93 94 return stateId; 95 } 96 }, 97 60 98 receive: function(/*String*/stateId, /*String*/urlEncodedData){ 61 99 /* urlEncodedData should have the following params: … … 93 131 if(contentType){ 94 132 var mimeType = contentType.split(";")[0]; 95 if(mimeType == "application/xml" || mimeType == "text/xml"){133 if(mimeType.indexOf("application/xml") == 0 || mimeType.indexOf("text/xml") == 0){ 96 134 facade.responseXML = dojox.data.dom.createDocument(response.responseText, contentType); 97 135 } … … 103 141 }, 104 142 105 clientFrameLoaded: function(/*String*/stateId){143 frameLoaded: function(/*String*/stateId){ 106 144 var state = this._state[stateId]; 107 145 var facade = state.facade; 108 146 109 var clientWindow = this.needFrameRecursion() ? window.open("", state.stateId + "_clientEndPoint") :state.clientFrame.contentWindow;147 var clientWindow = state.clientFrame.contentWindow; 110 148 111 149 var reqHeaders = []; … … 127 165 } 128 166 129 clientWindow.send(dojo.objectToQuery(requestData));167 this.sendRequest(stateId, dojo.objectToQuery(requestData)); 130 168 }, 131 169 … … 147 185 return dojox.io.proxy.xip._xhrObjOld.apply(dojo, arguments); 148 186 } 187 }, 188 189 //**** State-bound methods **** 190 sendRequest: function(stateId, encodedData){ 191 var state = this._state[stateId]; 192 if(!state.isSending){ 193 state.isSending = true; 194 195 state.requestData = encodedData || ""; 196 197 //Get a handle to the server iframe. 198 state.serverWindow = frames[state.stateId]; 199 if (!state.serverWindow){ 200 state.serverWindow = document.getElementById(state.stateId).contentWindow; 201 } 202 203 if(state.serverWindow.contentWindow){ 204 state.serverWindow = state.serverWindow.contentWindow; 205 } 206 207 this.sendRequestStart(stateId); 208 } 209 }, 210 211 sendRequestStart: function(stateId){ 212 //Break the message into parts, if necessary. 213 var state = this._state[stateId]; 214 state.requestParts = []; 215 var reqData = state.requestData; 216 var urlLength = state.serverUrl.length; 217 var partLength = this.urlLimit - urlLength; 218 var reqIndex = 0; 219 220 while((reqData.length - reqIndex) + urlLength > this.urlLimit){ 221 var part = reqData.substring(reqIndex, reqIndex + partLength); 222 //Safari will do some extra hex escaping unless we keep the original hex 223 //escaping complete. 224 var percentIndex = part.lastIndexOf("%"); 225 if(percentIndex == part.length - 1 || percentIndex == part.length - 2){ 226 part = part.substring(0, percentIndex); 227 } 228 state.requestParts.push(part); 229 reqIndex += part.length; 230 } 231 state.requestParts.push(reqData.substring(reqIndex, reqData.length)); 232 233 state.partIndex = 0; 234 this.sendRequestPart(stateId); 235 236 }, 237 238 sendRequestPart: function(stateId){ 239 var state = this._state[stateId]; 240 241 if(state.partIndex < state.requestParts.length){ 242 //Get the message part. 243 var partData = state.requestParts[state.partIndex]; 244 245 //Get the command. 246 var cmd = "part"; 247 if(state.partIndex + 1 == state.requestParts.length){ 248 cmd = "end"; 249 }else if (state.partIndex == 0){ 250 cmd = "start"; 251 } 252 253 this.setServerUrl(stateId, cmd, partData); 254 state.partIndex++; 255 } 256 }, 257 258 setServerUrl: function(stateId, cmd, message){ 259 var serverUrl = this.makeServerUrl(stateId, cmd, message); 260 var state = this._state[stateId]; 261 262 //Safari won't let us replace across domains. 263 if(this._isWebKit){ 264 state.serverWindow.location = serverUrl; 265 }else{ 266 state.serverWindow.location.replace(serverUrl); 267 } 268 }, 269 270 makeServerUrl: function(stateId, cmd, message){ 271 var state = this._state[stateId]; 272 var serverUrl = state.serverUrl + "#" + (state.idCounter++) + ":" + cmd; 273 if(message){ 274 serverUrl += ":" + message; 275 } 276 return serverUrl; 277 }, 278 279 fragmentReceived: function(frag){ 280 var index = frag.indexOf("#"); 281 var stateId = frag.substring(0, index); 282 var encodedData = frag.substring(index + 1, frag.length); 283 284 var msg = this.unpackMessage(encodedData); 285 var state = this._state[stateId]; 286 287 switch(msg.command){ 288 case "loaded": 289 this.frameLoaded(stateId); 290 break; 291 case "ok": 292 this.sendRequestPart(stateId); 293 break; 294 case "start": 295 state.responseMessage = "" + msg.message; 296 this.setServerUrl(stateId, "ok"); 297 break; 298 case "part": 299 state.responseMessage += msg.message; 300 this.setServerUrl(stateId, "ok"); 301 break; 302 case "end": 303 this.setServerUrl(stateId, "ok"); 304 state.responseMessage += msg.message; 305 this.receive(stateId, state.responseMessage); 306 break; 307 } 308 }, 309 310 unpackMessage: function(encodedMessage){ 311 var parts = encodedMessage.split(":"); 312 var command = parts[1]; 313 encodedMessage = parts[2] || ""; 314 315 var config = null; 316 if(command == "init"){ 317 var configParts = encodedMessage.split("&"); 318 config = {}; 319 for(var i = 0; i < configParts.length; i++){ 320 var nameValue = configParts[i].split("="); 321 config[decodeURIComponent(nameValue[0])] = decodeURIComponent(nameValue[1]); 322 } 323 } 324 return {command: command, message: encodedMessage, config: config}; 149 325 } 150 326 } -
dojox/trunk/io/proxy/xip_client.html
r10855 r12181 1 <!-- 2 /* 3 Copyright (c) 2004-2008, The Dojo Foundation 4 All Rights Reserved. 5 6 Licensed under the Academic Free License version 2.1 or above OR the 7 modified BSD license. For more information on Dojo licensing, see: 8 9 http://dojotoolkit.org/community/licensing.shtml 10 */ 11 --> 1 12 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 2 13 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> … … 9 20 <!-- script type="text/javascript" --> 10 21 // <!-- 11 /*12 This file is really focused on just sending one message to the server, and13 receiving one response. The code does not expect to be re-used for multiple messages.14 This might be reworked later if performance indicates a need for it.15 16 xip fragment identifier/hash values have the form:17 #id:cmd:realEncodedMessage18 19 id: some ID that should be unique among messages. No inherent meaning,20 just something to make sure the hash value is unique so the message21 receiver knows a new message is available.22 23 cmd: command to the receiver. Valid values are:24 - init: message used to init the frame. Sent as the first URL when loading25 the page. Contains some config parameters.26 - loaded: the remote frame is loaded. Only sent from server to client.27 - ok: the message that this page sent was received OK. The next message may28 now be sent.29 - start: the start message of a block of messages (a complete message may30 need to be segmented into many messages to get around the limitiations31 of the size of an URL that a browser accepts.32 - part: indicates this is a part of a message.33 - end: the end message of a block of messages. The message can now be acted upon.34 If the message is small enough that it doesn't need to be segmented, then35 just one hash value message can be sent with "end" as the command.36 37 To reassemble a segmented message, the realEncodedMessage parts just have to be concatenated38 together.39 */40 41 //MSIE has the lowest limit for URLs with fragment identifiers,42 //at around 4K. Choosing a slightly smaller number for good measure.43 xipUrlLimit = 4000;44 xipIdCounter = 1;45 46 function xipInit(){47 xipStateId = "";48 xipIsSending = false;49 xipServerUrl = null;50 xipStateId = null;51 xipRequestData = null;52 xipCurrentHash = "";53 xipResponseMessage = "";54 xipRequestParts = [];55 xipPartIndex = 0;56 xipServerWindow = null;57 xipUseFrameRecursion = false;58 }59 xipInit();60 61 function send(encodedData){62 if(xipUseFrameRecursion == "true"){63 var clientEndPoint = window.open(xipStateId + "_clientEndPoint");64 clientEndPoint.send(encodedData);65 }else{66 if(!xipIsSending){67 xipIsSending = true;68 69 xipRequestData = encodedData || "";70 71 //Get a handle to the server iframe.72 xipServerWindow = frames[xipStateId + "_frame"];73 if (!xipServerWindow){74 xipServerWindow = document.getElementById(xipStateId + "_frame").contentWindow;75 }76 77 sendRequestStart();78 }79 }80 }81 82 //Modify the server URL if it is a local path and83 //This is done for local/same domain testing.84 function fixServerUrl(ifpServerUrl){85 if(ifpServerUrl.indexOf("..") == 0){86 var parts = ifpServerUrl.split("/");87 ifpServerUrl = parts[parts.length - 1];88 }89 return ifpServerUrl;90 }91 92 22 93 23 function pollHash(){ … … 98 28 if(newHash != xipCurrentHash){ 99 29 try{ 100 messageReceived(newHash);30 callMaster(xipStateId, newHash); 101 31 }catch(e){ 102 32 //Make sure to not keep processing the error hash value. … … 107 37 } 108 38 } 109 }110 111 function messageReceived(encodedData){112 var msg = unpackMessage(encodedData);113 114 switch(msg.command){115 case "loaded":116 xipMasterFrame.dojox.io.proxy.xip.clientFrameLoaded(xipStateId);117 break;118 case "ok":119 sendRequestPart();120 break;121 case "start":122 xipResponseMessage = "";123 xipResponseMessage += msg.message;124 setServerUrl("ok");125 break;126 case "part":127 xipResponseMessage += msg.message;128 setServerUrl("ok");129 break;130 case "end":131 setServerUrl("ok");132 xipResponseMessage += msg.message;133 xipMasterFrame.dojox.io.proxy.xip.receive(xipStateId, xipResponseMessage);134 break;135 }136 }137 138 function sendRequestStart(){139 //Break the message into parts, if necessary.140 xipRequestParts = [];141 var reqData = xipRequestData;142 var urlLength = xipServerUrl.length;143 var partLength = xipUrlLimit - urlLength;144 var reqIndex = 0;145 146 while((reqData.length - reqIndex) + urlLength > xipUrlLimit){147 var part = reqData.substring(reqIndex, reqIndex + partLength);148 //Safari will do some extra hex escaping unless we keep the original hex149 //escaping complete.150 var percentIndex = part.lastIndexOf("%");151 if(percentIndex == part.length - 1 || percentIndex == part.length - 2){152 part = part.substring(0, percentIndex);153 }154 xipRequestParts.push(part);155 reqIndex += part.length;156 }157 xipRequestParts.push(reqData.substring(reqIndex, reqData.length));158 159 xipPartIndex = 0;160 sendRequestPart();161 162 }163 164 function sendRequestPart(){165 if(xipPartIndex < xipRequestParts.length){166 //Get the message part.167 var partData = xipRequestParts[xipPartIndex];168 169 //Get the command.170 var cmd = "part";171 if(xipPartIndex + 1 == xipRequestParts.length){172 cmd = "end";173 }else if (xipPartIndex == 0){174 cmd = "start";175 }176 177 setServerUrl(cmd, partData);178 xipPartIndex++;179 }180 }181 182 function setServerUrl(cmd, message){183 var serverUrl = makeServerUrl(cmd, message);184 185 //Safari won't let us replace across domains.186 if(navigator.userAgent.indexOf("Safari") == -1){187 xipServerWindow.location.replace(serverUrl);188 }else{189 xipServerWindow.location = serverUrl;190 }191 }192 193 function makeServerUrl(cmd, message){194 var serverUrl = xipServerUrl + "#" + (xipIdCounter++) + ":" + cmd;195 if(message){196 serverUrl += ":" + message;197 }198 return serverUrl;199 39 } 200 40 … … 216 56 } 217 57 218 function onClientLoad(){ 219 //Decode the init params 220 var config = unpackMessage(window.location.href.split("#")[1]).config; 58 //************** Init ************************** 59 xipCurrentHash = ""; 60 61 //Decode the init params 62 var fragId = window.location.href.split("#")[1]; 63 var config = unpackMessage(fragId).config; 221 64 222 xipStateId = config.id; 65 xipStateId = config.id; 66 xipMasterFrame = parent.parent; 223 67 224 //Remove the query param for the IE7 recursive case. 225 xipServerUrl = fixServerUrl(config.server).replace(/(\?|\&)dojo\.fr\=1/, ""); 226 227 //Make sure we don't have a javascript: url, just for good measure. 228 if(xipServerUrl.split(":")[0].match(/javascript/i)){ 229 throw "Invalid server URL"; 230 } 231 232 xipUseFrameRecursion = config["fr"]; 233 234 if(xipUseFrameRecursion == "endpoint"){ 235 xipMasterFrame = parent.parent; 236 }else{ 237 xipMasterFrame = parent; 238 } 239 240 //Start counter to inspect hash value. 241 setInterval(pollHash, 10); 242 243 var clientUrl = window.location.href.split("#")[0]; 244 var iframeNode = document.getElementsByTagName("iframe")[0]; 245 iframeNode.id = xipStateId + "_frame"; 246 iframeNode.src = makeServerUrl("init", 'id=' + xipStateId + '&client=' 247 + encodeURIComponent(clientUrl) + '&fr=' + xipUseFrameRecursion); 68 var parts = config.callback.split("."); 69 xipCallbackObject = xipMasterFrame; 70 for(var i = 0; i < parts.length - 1; i++){ 71 xipCallbackObject = xipCallbackObject[parts[i]]; 72 } 73 xipCallback = parts[parts.length - 1]; 74 75 callMaster = function(stateId, message){ 76 xipCallbackObject[xipCallback](stateId + "#" + message); 248 77 } 249 78 250 if(typeof(window.addEventListener) == "undefined"){ 251 window.attachEvent("onload", onClientLoad); 252 }else{ 253 window.addEventListener('load', onClientLoad, false); 254 } 79 //Call the master frame to let it know it is OK to start sending. 80 callMaster(xipStateId, "0:loaded"); 255 81 82 //Start counter to inspect hash value. 83 setInterval(pollHash, 10); 84 256 85 // --> 257 86 <!-- </script> --> … … 262 91 <p>This file is used for Dojo's XMLHttpRequest Iframe Proxy. This is the "client" file used 263 92 internally by dojox.io.proxy.xip.</p> 264 265 <iframe src="javascript:false"></iframe>266 93 </body> 267 94 </html> -
dojox/trunk/io/proxy/xip_server.html
r10855 r12181 1 1 <!-- 2 2 /* 3 Copyright (c) 2004-200 6, The Dojo Foundation3 Copyright (c) 2004-2008, The Dojo Foundation 4 4 All Rights Reserved. 5 5 … … 201 201 //Safari won't let us replace across domains. 202 202 if(navigator.userAgent.indexOf("Safari") == -1){ 203 &nb