1 | <!-- |
---|
2 | /* |
---|
3 | Copyright (c) 2004-2011, 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 | Pieces taken from Dojo source to make this file stand-alone |
---|
12 | --> |
---|
13 | <html> |
---|
14 | <head> |
---|
15 | <title></title> |
---|
16 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta> |
---|
17 | <script type="text/javascript" src="isAllowed.js"></script> |
---|
18 | <!-- |
---|
19 | BY DEFAULT THIS FILE DOES NOT WORK SO THAT YOU DON'T ACCIDENTALLY EXPOSE |
---|
20 | ALL OF YOUR XHR-ENABLED SERVICES ON YOUR SITE. |
---|
21 | |
---|
22 | In order for this file to work, you need to uncomment the start and end script tags, |
---|
23 | and you should define a function with the following signature: |
---|
24 | |
---|
25 | function isAllowedRequest(request){ |
---|
26 | return false; |
---|
27 | } |
---|
28 | |
---|
29 | Return true out of the function if you want to allow the cross-domain request. |
---|
30 | |
---|
31 | DON'T DEFINE THIS FUNCTION IN THIS FILE! Define it in a separate file called isAllowed.js |
---|
32 | and include it in this page with a script tag that has a src attribute pointing to the file. |
---|
33 | See the very first script tag in this file for an example. You do not have to place the |
---|
34 | script file in the same directory as this file, just update the path above if you move it |
---|
35 | somewhere else. |
---|
36 | |
---|
37 | Customize the isAllowedRequest function to restrict what types of requests are allowed |
---|
38 | for this server. The request object has the following properties: |
---|
39 | - requestHeaders: an object with the request headers that are to be added to |
---|
40 | the XHR request. |
---|
41 | - method: the HTTP method (GET, POST, etc...) |
---|
42 | - uri: The URI for the request. |
---|
43 | - data: The URL-encoded data for the request. For a GET request, this would |
---|
44 | be the querystring parameters. For a POST request, it wll be the |
---|
45 | body data. |
---|
46 | |
---|
47 | See xip_client.html for more info on the xip fragment identifier protocol. |
---|
48 | --> |
---|
49 | |
---|
50 | <!-- Security protection: uncomment the script tag to enable. --> |
---|
51 | <!-- script type="text/javascript" --> |
---|
52 | // <!-- |
---|
53 | //Core XHR handling taken from Dojo IO code. |
---|
54 | dojo = {}; |
---|
55 | dojo.hostenv = {}; |
---|
56 | // These are in order of decreasing likelihood; this will change in time. |
---|
57 | dojo.hostenv._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; |
---|
58 | |
---|
59 | dojo.hostenv.getXmlhttpObject = function(){ |
---|
60 | var http = null; |
---|
61 | var last_e = null; |
---|
62 | try{ http = new XMLHttpRequest(); }catch(e){} |
---|
63 | if(!http){ |
---|
64 | for(var i=0; i<3; ++i){ |
---|
65 | var progid = dojo.hostenv._XMLHTTP_PROGIDS[i]; |
---|
66 | try{ |
---|
67 | http = new ActiveXObject(progid); |
---|
68 | }catch(e){ |
---|
69 | last_e = e; |
---|
70 | } |
---|
71 | |
---|
72 | if(http){ |
---|
73 | dojo.hostenv._XMLHTTP_PROGIDS = [progid]; // so faster next time |
---|
74 | break; |
---|
75 | } |
---|
76 | } |
---|
77 | |
---|
78 | /*if(http && !http.toString) { |
---|
79 | http.toString = function() { "[object XMLHttpRequest]"; } |
---|
80 | }*/ |
---|
81 | } |
---|
82 | |
---|
83 | if(!http){ |
---|
84 | throw "xip_server.html: XMLHTTP not available: " + last_e; |
---|
85 | } |
---|
86 | |
---|
87 | return http; |
---|
88 | } |
---|
89 | |
---|
90 | dojo.setHeaders = function(http, headers){ |
---|
91 | if(headers) { |
---|
92 | for(var header in headers) { |
---|
93 | var headerValue = headers[header]; |
---|
94 | http.setRequestHeader(header, headerValue); |
---|
95 | } |
---|
96 | } |
---|
97 | } |
---|
98 | |
---|
99 | //MSIE has the lowest limit for URLs with fragment identifiers, |
---|
100 | //at around 4K. Choosing a slightly smaller number for good measure. |
---|
101 | xipUrlLimit = 4000; |
---|
102 | xipIdCounter = 1; |
---|
103 | |
---|
104 | function xipServerInit(){ |
---|
105 | xipStateId = ""; |
---|
106 | xipCurrentHash = ""; |
---|
107 | xipRequestMessage = ""; |
---|
108 | xipResponseParts = []; |
---|
109 | xipPartIndex = 0; |
---|
110 | } |
---|
111 | |
---|
112 | function pollHash(){ |
---|
113 | //Can't use location.hash because at least Firefox does a decodeURIComponent on it. |
---|
114 | var urlParts = window.location.href.split("#"); |
---|
115 | if(urlParts.length == 2){ |
---|
116 | var newHash = urlParts[1]; |
---|
117 | if(newHash != xipCurrentHash){ |
---|
118 | try{ |
---|
119 | messageReceived(newHash); |
---|
120 | }catch(e){ |
---|
121 | //Make sure to not keep processing the error hash value. |
---|
122 | xipCurrentHash = newHash; |
---|
123 | throw e; |
---|
124 | } |
---|
125 | xipCurrentHash = newHash; |
---|
126 | } |
---|
127 | } |
---|
128 | } |
---|
129 | |
---|
130 | function messageReceived(encodedData){ |
---|
131 | var msg = unpackMessage(encodedData); |
---|
132 | |
---|
133 | switch(msg.command){ |
---|
134 | case "ok": |
---|
135 | sendResponsePart(); |
---|
136 | break; |
---|
137 | case "start": |
---|
138 | xipRequestMessage = ""; |
---|
139 | xipRequestMessage += msg.message; |
---|
140 | setClientUrl("ok"); |
---|
141 | break; |
---|
142 | case "part": |
---|
143 | xipRequestMessage += msg.message; |
---|
144 | setClientUrl("ok"); |
---|
145 | break; |
---|
146 | case "end": |
---|
147 | setClientUrl("ok"); |
---|
148 | xipRequestMessage += msg.message; |
---|
149 | sendXhr(); |
---|
150 | break; |
---|
151 | } |
---|
152 | } |
---|
153 | |
---|
154 | function sendResponse(encodedData){ |
---|
155 | //Break the message into parts, if necessary. |
---|
156 | xipResponseParts = []; |
---|
157 | var resData = encodedData; |
---|
158 | var urlLength = xipClientUrl.length; |
---|
159 | var partLength = xipUrlLimit - urlLength; |
---|
160 | var resIndex = 0; |
---|
161 | |
---|
162 | while((resData.length - resIndex) + urlLength > xipUrlLimit){ |
---|
163 | var part = resData.substring(resIndex, resIndex + partLength); |
---|
164 | //Safari will do some extra hex escaping unless we keep the original hex |
---|
165 | //escaping complete. |
---|
166 | var percentIndex = part.lastIndexOf("%"); |
---|
167 | if(percentIndex == part.length - 1 || percentIndex == part.length - 2){ |
---|
168 | part = part.substring(0, percentIndex); |
---|
169 | } |
---|
170 | xipResponseParts.push(part); |
---|
171 | resIndex += part.length; |
---|
172 | } |
---|
173 | xipResponseParts.push(resData.substring(resIndex, resData.length)); |
---|
174 | |
---|
175 | xipPartIndex = 0; |
---|
176 | sendResponsePart(); |
---|
177 | } |
---|
178 | |
---|
179 | function sendResponsePart(){ |
---|
180 | if(xipPartIndex < xipResponseParts.length){ |
---|
181 | //Get the message part. |
---|
182 | var partData = xipResponseParts[xipPartIndex]; |
---|
183 | |
---|
184 | //Get the command. |
---|
185 | var cmd = "part"; |
---|
186 | if(xipPartIndex + 1 == xipResponseParts.length){ |
---|
187 | cmd = "end"; |
---|
188 | }else if (xipPartIndex == 0){ |
---|
189 | cmd = "start"; |
---|
190 | } |
---|
191 | |
---|
192 | setClientUrl(cmd, partData); |
---|
193 | xipPartIndex++; |
---|
194 | }else{ |
---|
195 | xipServerInit(); |
---|
196 | } |
---|
197 | } |
---|
198 | |
---|
199 | function setClientUrl(cmd, message){ |
---|
200 | var clientUrl = makeClientUrl(cmd, message); |
---|
201 | //Safari won't let us replace across domains. |
---|
202 | if(navigator.userAgent.indexOf("Safari") == -1){ |
---|
203 | xipClientWindow.location.replace(clientUrl); |
---|
204 | }else{ |
---|
205 | xipClientWindow.location = clientUrl; |
---|
206 | } |
---|
207 | } |
---|
208 | |
---|
209 | function makeClientUrl(cmd, message){ |
---|
210 | var clientUrl = xipClientUrl + "#" + (xipIdCounter++) + ":" + cmd; |
---|
211 | if(message){ |
---|
212 | clientUrl += ":" + message; |
---|
213 | } |
---|
214 | return clientUrl |
---|
215 | } |
---|
216 | |
---|
217 | function xhrDone(xhr){ |
---|
218 | /* Need to pull off and return the following data: |
---|
219 | - responseHeaders |
---|
220 | - status |
---|
221 | - statusText |
---|
222 | - responseText |
---|
223 | */ |
---|
224 | var response = {}; |
---|
225 | |
---|
226 | if(typeof(xhr.getAllResponseHeaders) != "undefined"){ |
---|
227 | var allHeaders = xhr.getAllResponseHeaders(); |
---|
228 | if(allHeaders){ |
---|
229 | response.responseHeaders = allHeaders; |
---|
230 | } |
---|
231 | } |
---|
232 | |
---|
233 | if(xhr.status == 0 || xhr.status){ |
---|
234 | response.status = xhr.status; |
---|
235 | } |
---|
236 | |
---|
237 | if(xhr.statusText){ |
---|
238 | response.statusText = xhr.statusText; |
---|
239 | } |
---|
240 | |
---|
241 | if(xhr.responseText){ |
---|
242 | response.responseText = xhr.responseText; |
---|
243 | } |
---|
244 | |
---|
245 | //Build a string of the response object. |
---|
246 | var result = ""; |
---|
247 | var isFirst = true; |
---|
248 | for (var param in response){ |
---|
249 | if(isFirst){ |
---|
250 | isFirst = false; |
---|
251 | }else{ |
---|
252 | result += "&"; |
---|
253 | } |
---|
254 | result += param + "=" + encodeURIComponent(response[param]); |
---|
255 | } |
---|
256 | sendResponse(result); |
---|
257 | } |
---|
258 | |
---|
259 | function sendXhr(){ |
---|
260 | var request = {}; |
---|
261 | var nvPairs = xipRequestMessage.split("&"); |
---|
262 | var i = 0; |
---|
263 | var nameValue = null; |
---|
264 | for(i = 0; i < nvPairs.length; i++){ |
---|
265 | if(nvPairs[i]){ |
---|
266 | var nameValue = nvPairs[i].split("="); |
---|
267 | request[decodeURIComponent(nameValue[0])] = decodeURIComponent(nameValue[1]); |
---|
268 | } |
---|
269 | } |
---|
270 | |
---|
271 | //Split up the request headers, if any. |
---|
272 | var headers = {}; |
---|
273 | if(request.requestHeaders){ |
---|
274 | nvPairs = request.requestHeaders.split("\r\n"); |
---|
275 | for(i = 0; i < nvPairs.length; i++){ |
---|
276 | if(nvPairs[i]){ |
---|
277 | nameValue = nvPairs[i].split(": "); |
---|
278 | headers[decodeURIComponent(nameValue[0])] = decodeURIComponent(nameValue[1]); |
---|
279 | } |
---|
280 | } |
---|
281 | |
---|
282 | request.requestHeaders = headers; |
---|
283 | } |
---|
284 | |
---|
285 | if(isAllowedRequest(request)){ |
---|
286 | |
---|
287 | //The request is allowed, so set up the XHR object. |
---|
288 | var xhr = dojo.hostenv.getXmlhttpObject(); |
---|
289 | |
---|
290 | //Start timer to look for readyState. |
---|
291 | var xhrIntervalId = setInterval(function(){ |
---|
292 | |
---|
293 | if(xhr.readyState == 4){ |
---|
294 | clearInterval(xhrIntervalId); |
---|
295 | xhrDone(xhr); |
---|
296 | } |
---|
297 | }, 10); |
---|
298 | |
---|
299 | //Actually start up the XHR request. |
---|
300 | xhr.open(request.method, request.uri, true); |
---|
301 | dojo.setHeaders(xhr, request.requestHeaders); |
---|
302 | |
---|
303 | var content = ""; |
---|
304 | if(request.data){ |
---|
305 | content = request.data; |
---|
306 | } |
---|
307 | |
---|
308 | try{ |
---|
309 | xhr.send(content); |
---|
310 | }catch(e){ |
---|
311 | if(typeof xhr.abort == "function"){ |
---|
312 | xhr.abort(); |
---|
313 | xhrDone({status: 404, statusText: "xip_server.html error: " + e}); |
---|
314 | } |
---|
315 | } |
---|
316 | } |
---|
317 | } |
---|
318 | |
---|
319 | function unpackMessage(encodedMessage){ |
---|
320 | var parts = encodedMessage.split(":"); |
---|
321 | var command = parts[1]; |
---|
322 | encodedMessage = parts[2] || ""; |
---|
323 | |
---|
324 | var config = null; |
---|
325 | if(command == "init"){ |
---|
326 | var configParts = encodedMessage.split("&"); |
---|
327 | config = {}; |
---|
328 | for(var i = 0; i < configParts.length; i++){ |
---|
329 | var nameValue = configParts[i].split("="); |
---|
330 | config[decodeURIComponent(nameValue[0])] = decodeURIComponent(nameValue[1]); |
---|
331 | } |
---|
332 | } |
---|
333 | return {command: command, message: encodedMessage, config: config}; |
---|
334 | } |
---|
335 | |
---|
336 | function onServerLoad(){ |
---|
337 | xipServerInit(); |
---|
338 | |
---|
339 | //Decode the init params |
---|
340 | var config = unpackMessage(window.location.href.split("#")[1]).config; |
---|
341 | |
---|
342 | xipStateId = config.id; |
---|
343 | xipClientUrl = config.client; |
---|
344 | |
---|
345 | //Make sure we don't have a javascript: url, just for good measure. |
---|
346 | if(xipClientUrl.split(":")[0].match(/javascript/i)){ |
---|
347 | throw "Invalid client URL"; |
---|
348 | } |
---|
349 | if(!xipStateId.match(/^XhrIframeProxy[0-9]+$/)){ |
---|
350 | throw "Invalid state ID"; |
---|
351 | } |
---|
352 | |
---|
353 | setInterval(pollHash, 10); |
---|
354 | |
---|
355 | var serverUrl = window.location.href.split("#")[0]; |
---|
356 | document.getElementById("iframeHolder").innerHTML = '<iframe name="' |
---|
357 | + xipStateId + '_clientEndPoint' |
---|
358 | + '" src="javascript:false">' |
---|
359 | + '</iframe>'; |
---|
360 | xipClientWindow = document.getElementsByTagName("iframe")[0]; |
---|
361 | xipClientWindow.src = makeClientUrl("init", 'id=' + xipStateId + "&callback=" + encodeURIComponent(config.callback)); |
---|
362 | if(xipClientWindow.contentWindow){ |
---|
363 | xipClientWindow = xipClientWindow.contentWindow; |
---|
364 | } |
---|
365 | } |
---|
366 | |
---|
367 | if(typeof(window.addEventListener) == "undefined"){ |
---|
368 | window.attachEvent("onload", onServerLoad); |
---|
369 | }else{ |
---|
370 | window.addEventListener('load', onServerLoad, false); |
---|
371 | } |
---|
372 | // --> |
---|
373 | <!-- </script> --> |
---|
374 | </head> |
---|
375 | <body> |
---|
376 | <h4>The Dojo Toolkit -- xip_server.html</h4> |
---|
377 | |
---|
378 | <p>This file is used for Dojo's XMLHttpRequest Iframe Proxy. This is the the file |
---|
379 | that should go on the server that will actually be doing the XHR request.</p> |
---|
380 | <div id="iframeHolder"></div> |
---|
381 | </body> |
---|
382 | </html> |
---|