File: socket.js

package info (click to toggle)
dojo 1.15.0+dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 101,984 kB
  • sloc: php: 10,436; xml: 3,415; java: 3,102; sql: 928; sh: 489; pascal: 205; perl: 182; makefile: 57; sed: 37; ruby: 10
file content (229 lines) | stat: -rw-r--r-- 7,656 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
define([
	"dojo/_base/array",
	"dojo/_base/lang",
	"dojo/_base/xhr",
	"dojo/aspect",
	"dojo/on",
	"dojo/Evented",
	"dojo/_base/url"
], function(array, lang, xhr, aspect, on, Evented, dBaseUrl) {

var WebSocket = window.WebSocket;

var Socket = function(/*dojo.__XhrArgs*/ argsOrUrl){
	// summary:
	//		Provides a simple socket connection using WebSocket, or alternate
	//		communication mechanisms in legacy browsers for comet-style communication. This is based
	//		on the WebSocket API and returns an object that implements the WebSocket interface:
	//		http://dev.w3.org/html5/websockets/#websocket
	// description:
	//		Provides socket connections. This can be used with virtually any Comet protocol.
	// argsOrUrl:
	//		This uses the same arguments as the other I/O functions in Dojo, or a
	//		URL to connect to. The URL should be a relative URL in order to properly
	//		work with WebSockets (it can still be host relative, like //other-site.org/endpoint)
	// returns:
	//		An object that implements the WebSocket API
	// example:
	//		| require(["dojox/socket", "dojo/aspect"], function(socket, aspect) {
	//		|    var sock = socket({"url://comet-server/comet");
	//		|    // we could also add auto-reconnect support
	//		|    // now we can connect to standard HTML5 WebSocket-style events
	//		|    aspect.after(socket, "onmessage", function(event){
	//		|       var message = event.data;
	//		|       // do something with the message
	//		|    });
	//		|    // send something
	//		|    sock.send("hi there");
	//		|    ...
	//		| });
	//		You can also use the Reconnect module:
	//		| require["dojox/socket", "dojox/socket/Reconnect"], function(dxSocket, reconnect){
	//		|    var socket = dxSocket({url:"/comet"});
	//		|    // add auto-reconnect support
	//		|    socket = reconnect(socket);
	if(typeof argsOrUrl == "string"){
		argsOrUrl = {url: argsOrUrl};
	}
	return WebSocket ? Socket.WebSocket(argsOrUrl, true) : Socket.LongPoll(argsOrUrl);
};

Socket.WebSocket = function(args, fallback){
	// summary:
	//		A wrapper for WebSocket, than handles standard args and relative URLs
	var baseURI = document.baseURI || window.location.href;
	var ws = new WebSocket(new dBaseUrl(baseURI.replace(/^http/i,'ws'), args.url));
	ws.on = function(type, listener){
		ws.addEventListener(type, listener, true);
	};
	var opened;
	aspect.after(ws, "onopen", function(event){
		opened = true;
	}, true);
	aspect.after(ws, "onclose", function(event){
		if(opened){
			return;
		}
		if(fallback){
			Socket.replace(ws, Socket.LongPoll(args), true);
		}
	}, true);
	return ws;
};

Socket.replace = function(socket, newSocket, listenForOpen){
	// make the original socket a proxy for the new socket
	socket.send = lang.hitch(newSocket, "send");
	socket.close = lang.hitch(newSocket, "close");
	var proxyEvent = function(type){
		(newSocket.addEventListener || newSocket.on).call(newSocket, type, function(event){
			on.emit(socket, event.type, event);
		}, true);
	};

	if(listenForOpen){
		proxyEvent("open");
	}
	// redirect the events as well
	array.forEach(["message", "close", "error"], proxyEvent);
};

Socket.LongPoll = function(/*dojo.__XhrArgs*/ args){
	// summary:
	//		Provides a simple long-poll based comet-style socket/connection to a server and returns an
	//		object implementing the WebSocket interface:
	//		http://dev.w3.org/html5/websockets/#websocket
	// args:
	//		This uses the same arguments as the other I/O functions in Dojo, with this addition:
	//	args.interval:
	//		Indicates the amount of time (in milliseconds) after a response was received
	//		before another request is made. By default, a request is made immediately
	//		after getting a response. The interval can be increased to reduce load on the
	//		server or to do simple time-based polling where the server always responds
	//		immediately.
	//	args.transport:
	//		Provide an alternate transport like dojo.io.script.get
	// returns:
	//		An object that implements the WebSocket API
	// example:
	//		| dojo.require("dojox.socket.LongPoll");
	//		| var socket = dojox.socket.LongPoll({url:"/comet"});
	//		or:
	//		| dojo.require("dojox.socket.LongPoll");
	//		| dojox.socket.LongPoll.add();
	//		| var socket = dojox.socket({url:"/comet"});

	var cancelled = false,
		first = true,
		timeoutId,
		connections = [];

	// create the socket object
	var fire, connect;
	var socket = {
		send: function(data){
			// summary:
			//		Send some data using XHR or provided transport
			var sendArgs = lang.delegate(args);
			sendArgs.rawBody = data;
			clearTimeout(timeoutId);
			var deferred = first ? (first = false) || socket.firstRequest(sendArgs) :
				socket.transport(sendArgs);
			connections.push(deferred);
			deferred.then(function(response){
				// got a response
				socket.readyState = 1;
				// remove the current connection
				connections.splice(array.indexOf(connections, deferred), 1);
				// reconnect to listen for the next message if there are no active connections,
				// we queue it up in case one of the onmessage handlers has a message to send
				if(!connections.length){
					timeoutId = setTimeout(connect, args.interval);
				}
				if(response){
					// now send the message along to listeners
					fire("message", {data: response}, deferred);
				}
			}, function(error){
				connections.splice(array.indexOf(connections, deferred), 1);
				// an error occurred, fire the appropriate event listeners
				if(!cancelled){
					fire("error", {error:error}, deferred);
					if(!connections.length){
						clearTimeout(timeoutId);
						socket.readyState = 3;
						fire("close", {wasClean:false}, deferred);
					}
				}
			});
			return deferred;
		},
		close: function(){
			// summary:
			//		Close the connection
			socket.readyState = 2;
			cancelled = true;
			var i;
			for(i = 0; i < connections.length; i++){
				connections[i].cancel();
			}
			socket.readyState = 3;
			fire("close", {wasClean:true});
		},
		transport: args.transport || xhr.post,
		args: args,
		url: args.url,
		readyState: 0,
		CONNECTING: 0,
		OPEN: 1,
		CLOSING: 2,
		CLOSED: 3,
		on: Evented.prototype.on,
		firstRequest: function(args){
			// summary:
			//		This allows for special handling for the first request. This is useful for
			//		providing information to disambiguate between the first request and
			//		subsequent long-poll requests so the server can properly setup a
			//		connection on the first connection or reject a request for an expired
			//		connection if the request is not expecting to be the first for a connection.
			//		This method can be overriden. The default behavior is to include a Pragma
			//		header with a value of "start-long-poll"
			var headers = (args.headers || (args.headers = {}));
			headers.Pragma = "start-long-poll";
			try{
				return this.transport(args);
			}finally{
				// cleanup the header so it is not used on subsequent requests
				delete headers.Pragma;
			}
		}
	};
	fire = function(type, object, deferred){
		if(socket["on" + type]){
			object.ioArgs = deferred && deferred.ioArgs;
			object.type = type;
			on.emit(socket, type, object);
		}
	};
	connect = function(){
		if(socket.readyState === 0){
			// we fire the open event now because we really don't know when the "socket"
			// is truly open, and this gives us a to do a send() and get it included in the
			// HTTP request
			fire("open",{});
		}
		// make the long-poll connection, to wait for response from the server
		if(!connections.length){
			socket.send();
		}
	};
	// provide an alias for Dojo's connect method
	socket.connect = socket.on;
	// do the initial connection
	setTimeout(connect);
	return socket;
};

return Socket;

});