Skip to content
This repository
Newer
Older
100644 489 lines (378 sloc) 17.115 kb
36b284dd »
2014-02-08 README: style
1
0612d5a0 »
2012-02-27 Shortened heading.
2 # Engine.IO: the realtime engine
3bbf02b1 »
2011-11-18 Initial import
3
67992530 »
2012-02-27 Added engine.io.
4 [![Build Status](https://secure.travis-ci.org/LearnBoost/engine.io.png)](http://travis-ci.org/LearnBoost/engine.io)
b6c477a2 »
2013-03-18 Add Version Badge to README
5 [![NPM version](https://badge.fury.io/js/engine.io.png)](http://badge.fury.io/js/engine.io)
67992530 »
2012-02-27 Added engine.io.
6
3bbf02b1 »
2011-11-18 Initial import
7 `Engine` is the implementation of transport-based cross-browser/cross-device
8 bi-directional communication layer for
9 [Socket.IO](http://github.com/learnboost/socket.io).
10
11 ## Hello World
12
13 ### Server
14
5b4b3689 »
2011-12-15 Indicate that there's 3 interfaces to implementing a Server.
15 #### (A) Listening on a port
3bbf02b1 »
2011-11-18 Initial import
16
17 ```js
36b284dd »
2014-02-08 README: style
18 var engine = require('engine.io');
19 var server = engine.listen(80);
3bbf02b1 »
2011-11-18 Initial import
20
32db7c46 »
2014-04-11 tweaks
21 server.on('connection', function(socket){
3bbf02b1 »
2011-11-18 Initial import
22 socket.send('utf 8 string');
dbe140c1 »
2014-01-20 Binary support
23 socket.send(new Buffer([0, 1, 2, 3, 4, 5])); // binary data
3bbf02b1 »
2011-11-18 Initial import
24 });
25 ```
26
5b4b3689 »
2011-12-15 Indicate that there's 3 interfaces to implementing a Server.
27 #### (B) Intercepting requests for a http.Server
3bbf02b1 »
2011-11-18 Initial import
28
29 ```js
36b284dd »
2014-02-08 README: style
30 var engine = require('engine.io');
31 var http = require('http').createServer().listen(3000);
32 var server = engine.attach(http);
3bbf02b1 »
2011-11-18 Initial import
33
18a735c5 »
2011-12-15 README improvements.
34 server.on('connection', function (socket) {
32db7c46 »
2014-04-11 tweaks
35 socket.on('message', function(data){ });
36 socket.on('close', function(){ });
18a735c5 »
2011-12-15 README improvements.
37 });
38 ```
39
5b4b3689 »
2011-12-15 Indicate that there's 3 interfaces to implementing a Server.
40 #### (C) Passing in requests
18a735c5 »
2011-12-15 README improvements.
41
42 ```js
36b284dd »
2014-02-08 README: style
43 var engine = require('engine.io');
44 var server = new engine.Server();
18a735c5 »
2011-12-15 README improvements.
45
32db7c46 »
2014-04-11 tweaks
46 server.on('connection', function(socket){
18a735c5 »
2011-12-15 README improvements.
47 socket.send('hi');
48 });
49
50 // …
32db7c46 »
2014-04-11 tweaks
51 httpServer.on('upgrade', function(req, socket, head){
18a735c5 »
2011-12-15 README improvements.
52 server.handleUpgrade(req, socket, head);
53 });
32db7c46 »
2014-04-11 tweaks
54 httpServer.on('request', function(req, res){
18a735c5 »
2011-12-15 README improvements.
55 server.handleRequest(req, res);
3bbf02b1 »
2011-11-18 Initial import
56 });
57 ```
58
59 ### Client
60
61 ```html
0a5304f5 »
2012-03-28 update docs
62 <script src="/path/to/engine.io.js"></script>
3bbf02b1 »
2011-11-18 Initial import
63 <script>
18da6167 »
2012-09-04 README: simplified client constructor example
64 var socket = new eio.Socket('ws://localhost/');
32db7c46 »
2014-04-11 tweaks
65 socket.on('open', function(){
66 socket.on('message', function(data){});
67 socket.on('close', function(){});
efc4c06b »
2014-02-01 Adapting code to conventions
68 });
69 </script>
70 ```
71
3bbf02b1 »
2011-11-18 Initial import
72 For more information on the client refer to the
73 [engine-client](http://github.com/learnboost/engine.io-client) repository.
74
75 ## What features does it have?
76
77 - **Maximum reliability**. Connections are established even in the presence of:
78 - proxies and load balancers.
79 - personal firewall and antivirus software.
80 - for more information refer to **Goals** and **Architecture** sections
81 - **Minimal client size** aided by:
82 - lazy loading of flash transports.
83 - lack of redundant transports.
84 - **Scalable**
85 - load balancer friendly
86 - **Future proof**
87 - **100% Node.JS core style**
88 - No API sugar (left for higher level projects)
89 - Written in readable vanilla JavaScript
90
91 ## API
92
93 ### Server
94
95 <hr><br>
96
97 #### Top-level
98
6f115fa0 »
2011-12-23 Fixed require in README
99 These are exposed by `require('engine.io')`:
3bbf02b1 »
2011-11-18 Initial import
100
834cbcf7 »
2012-08-10 README: added flush/drain docs
101 ##### Events
102
103 - `flush`
104 - Called when a socket buffer is being flushed.
105 - **Arguments**
106 - `Socket`: socket being flushed
107 - `Array`: write buffer
108 - `drain`
109 - Called when a socket buffer is drained
110 - **Arguments**
111 - `Socket`: socket being flushed
112
3bbf02b1 »
2011-11-18 Initial import
113 ##### Properties
114
18a735c5 »
2011-12-15 README improvements.
115 - `protocol` _(Number)_: protocol revision number
3bbf02b1 »
2011-11-18 Initial import
116 - `Server`: Server class constructor
117 - `Socket`: Socket class constructor
8a61a1c6 »
2012-01-03 Added engine#Transport and engine#transports as public API
118 - `Transport` _(Function)_: transport constructor
119 - `transports` _(Object)_: map of available transports
3bbf02b1 »
2011-11-18 Initial import
120
121 ##### Methods
122
123 - `listen`
124 - Creates an `http.Server` which listens on the given port and attaches WS
125 to it. It returns `501 Not Implemented` for regular http requests.
126 - **Parameters**
127 - `Number`: port to listen on.
8a677a9d »
2011-12-23 Simply simplifying
128 - `Function`: callback for `listen`.
3bbf02b1 »
2011-11-18 Initial import
129 - **Returns** `Server`
130 - `attach`
131 - Captures `upgrade` requests for a `http.Server`. In other words, makes
6bd328e7 »
2013-03-23 Fix typos and use consistent capitalization
132 a regular http.Server WebSocket-compatible.
3bbf02b1 »
2011-11-18 Initial import
133 - **Parameters**
134 - `http.Server`: server to attach to.
5ed8ac95 »
2012-02-26 Documented `attach` options.
135 - `Object`: optional, options object
136 - **Options**
c59bb42e »
2012-12-16 engine.io: deprecated `resource`
137 - `path` (`String`): name of the path to capture (`/engine.io`).
5ed8ac95 »
2012-02-26 Documented `attach` options.
138 - `policyFile` (`Boolean`): whether to handle policy file requests (`true`)
139 - `destroyUpgrade` (`Boolean`): destroy unhandled upgrade requests (`true`)
fd36eeaf »
2013-01-31 Only end upgrade socket connections if unhandled
140 - `destroyUpgradeTimeout` (`Number`): milliseconds after which unhandled requests are ended (`1000`)
8387ffb8 »
2012-07-03 Added missing docs for Server constructor
141 - **See Server options below for additional options you can pass**
3bbf02b1 »
2011-11-18 Initial import
142 - **Returns** `Server`
143
144 <hr><br>
145
146 #### Server
147
148 The main server/manager. _Inherits from EventEmitter_.
149
150 ##### Events
151
152 - `connection`
834cbcf7 »
2012-08-10 README: added flush/drain docs
153 - Fired when a new connection is established.
3bbf02b1 »
2011-11-18 Initial import
154 - **Arguments**
155 - `Socket`: a Socket object
156
157 ##### Properties
158
6bd328e7 »
2013-03-23 Fix typos and use consistent capitalization
159 **Important**: if you plan to use Engine.IO in a scalable way, please
1254c2c2 »
2012-04-18 Added note about clients and clientsCount
160 keep in mind the properties below will only reflect the clients connected
161 to a single process.
162
ff50d1d1 »
2012-01-03 Added Server#clientsCount property
163 - `clients` _(Object)_: hash of connected clients by id.
164 - `clientsCount` _(Number)_: number of connected clients.
3bbf02b1 »
2011-11-18 Initial import
165
166 ##### Methods
167
8387ffb8 »
2012-07-03 Added missing docs for Server constructor
168 - **constructor**
169 - Initializes the server
170 - **Parameters**
171 - `Object`: optional, options object
172 - **Options**
173 - `pingTimeout` (`Number`): how many ms without a pong packet to
174 consider the connection closed (`60000`)
175 - `pingInterval` (`Number`): how many ms before sending a new ping
176 packet (`25000`)
f8100f92 »
2014-03-26 Added a maximum buffer size to received data from polling. Settable w…
177 - `maxHttpBufferSize` (`Number`): how many bytes or characters a message
178 can be when polling, before closing the session (to avoid DoS). Default
179 value is `10E7`.
5b7f0cff »
2014-04-11 Updated README to include allowRequest callback
180 - `allowRequest` (`Function`): A function that receives a given handshake
181 or upgrade request as its first parameter, and can decide whether to
182 continue or not. The second argument is a function that needs to be
183 called with the decided information: `fn(err, success)`, where
184 `success` is a boolean value where false means that the request is
185 rejected, and err is an error code.
8387ffb8 »
2012-07-03 Added missing docs for Server constructor
186 - `transports` (`<Array> String`): transports to allow connections
187 to (`['polling', 'websocket', 'flashsocket']`)
6bd328e7 »
2013-03-23 Fix typos and use consistent capitalization
188 - `allowUpgrades` (`Boolean`): whether to allow transport upgrades
8387ffb8 »
2012-07-03 Added missing docs for Server constructor
189 (`true`)
190 - `cookie` (`String|Boolean`): name of the HTTP cookie that
191 contains the client sid to send as part of handshake response
192 headers. Set to `false` to not send one. (`io`)
2d13524c »
2012-01-03 Documented public Server methods
193 - `close`
194 - Closes all clients
195 - **Returns** `Server` for chaining
3bbf02b1 »
2011-11-18 Initial import
196 - `handleRequest`
197 - Called internally when a `Engine` request is intercepted.
198 - **Parameters**
2d13524c »
2012-01-03 Documented public Server methods
199 - `http.ServerRequest`: a node request object
200 - `http.ServerResponse`: a node response object
201 - **Returns** `Server` for chaining
202 - `handleUpgrade`
203 - Called internally when a `Engine` ws upgrade is intercepted.
204 - **Parameters** (same as `upgrade` event)
205 - `http.ServerRequest`: a node request object
206 - `net.Stream`: TCP socket for the request
207 - `Buffer`: legacy tail bytes
208 - **Returns** `Server` for chaining
209 - `handleSocket`
210 - Called with raw TCP sockets from http requests to intercept flash policy
211 file requests
212 - **Parameters**
213 - `net.Stream`: TCP socket on which requests are listened
214 - **Returns** `Server` for chaining
3bbf02b1 »
2011-11-18 Initial import
215
216 <hr><br>
217
218 #### Socket
219
220 A representation of a client. _Inherits from EventEmitter_.
221
222 ##### Events
223
224 - `close`
225 - Fired when the client is disconnected.
32f2d57f »
2012-01-03 Documented close event arguments
226 - **Arguments**
227 - `String`: reason for closing
228 - `Object`: description object (optional)
3bbf02b1 »
2011-11-18 Initial import
229 - `message`
230 - Fired when the client sends a message.
231 - **Arguments**
dbe140c1 »
2014-01-20 Binary support
232 - `String` or `Buffer`: Unicode string or Buffer with binary contents
3bbf02b1 »
2011-11-18 Initial import
233 - `error`
234 - Fired when an error occurs.
81a57972 »
2012-07-03 Improved docs
235 - **Arguments**
236 - `Error`: error object
834cbcf7 »
2012-08-10 README: added flush/drain docs
237 - `flush`
238 - Called when the write buffer is being flushed.
239 - **Arguments**
240 - `Array`: write buffer
241 - `drain`
242 - Called when the write buffer is drained
8b549b62 »
2012-11-10 add pakcet, packetCreate document to README
243 - `packet`
6bd328e7 »
2013-03-23 Fix typos and use consistent capitalization
244 - Called when a socket received a packet (`message`, `ping`)
8b549b62 »
2012-11-10 add pakcet, packetCreate document to README
245 - **Arguments**
246 - `type`: packet type
247 - `data`: packet data (if type is message)
248 - `packetCreate`
249 - Called before a socket sends a packet (`message`, `pong`)
250 - **Arguments**
251 - `type`: packet type
252 - `data`: packet data (if type is message)
3bbf02b1 »
2011-11-18 Initial import
253
3d97e1c4 »
2012-01-03 Documented Socket properties.
254 ##### Properties
255
4785b786 »
2014-01-29 document Socket.id
256 - `id` _(String)_: unique identifier
3d97e1c4 »
2012-01-03 Documented Socket properties.
257 - `server` _(Server)_: engine parent reference
f30f353a »
2012-07-03 Renamed Socket#req to Socket#request
258 - `request` _(http.ServerRequest)_: request that originated the Socket
3d97e1c4 »
2012-01-03 Documented Socket properties.
259 - `upgraded` _(Boolean)_: whether the transport has been upgraded
260 - `readyState` _(String)_: opening|open|closing|closed
261 - `transport` _(Transport)_: transport reference
262
3bbf02b1 »
2011-11-18 Initial import
263 ##### Methods
264
265 - `send`:
dbe140c1 »
2014-01-20 Binary support
266 - Sends a message, performing `message = toString(arguments[0])` unless
267 sending binary data, which is sent as is.
3bbf02b1 »
2011-11-18 Initial import
268 - **Parameters**
dbe140c1 »
2014-01-20 Binary support
269 - `String` | `Buffer` | `ArrayBuffer` | `ArrayBufferView`: a string or any object implementing `toString()`, with outgoing data, or a Buffer or ArrayBuffer with binary data. Also any ArrayBufferView can be sent as is.
f4931723 »
2012-09-04 README: documented `send` callback
270 - `Function`: optional, a callback executed when the message gets flushed out by the transport
18a735c5 »
2011-12-15 README improvements.
271 - **Returns** `Socket` for chaining
3bbf02b1 »
2011-11-18 Initial import
272 - `close`
273 - Disconnects the client
18a735c5 »
2011-12-15 README improvements.
274 - **Returns** `Socket` for chaining
3bbf02b1 »
2011-11-18 Initial import
275
276 ### Client
277
278 <hr><br>
279
8085e399 »
2012-08-07 Update README.md
280 Exposed in the `eio` global namespace (in the browser), or by
c41baae1 »
2011-12-03 Updated client side API in README
281 `require('engine.io-client')` (in Node.JS).
3bbf02b1 »
2011-11-18 Initial import
282
8085e399 »
2012-08-07 Update README.md
283 For the client API refer to the
1c57fdc2 »
2012-10-18 broken link
284 [engine-client](http://github.com/learnboost/engine.io-client) repository.
3bbf02b1 »
2011-11-18 Initial import
285
88e24feb »
2012-09-09 README: documented debug (fixes #77)
286 ## Debug / logging
287
288 Engine.IO is powered by [debug](http://github.com/visionmedia/debug).
6bd328e7 »
2013-03-23 Fix typos and use consistent capitalization
289 In order to see all the debug output, run your app with the environment variable
88e24feb »
2012-09-09 README: documented debug (fixes #77)
290 `DEBUG` including the desired scope.
291
292 To see the output from all of Engine.IO's debugging scopes you can use:
293
294 ```
295 DEBUG=engine* node myapp
296 ```
297
3bbf02b1 »
2011-11-18 Initial import
298 ## Transports
299
300 - `polling`: XHR / JSONP polling transport.
301 - `websocket`: WebSocket transport.
d7c1ff51 »
2012-01-03 Documented client close event arguemnts.
302 - `flashsocket`: WebSocket transport backed by flash.
3bbf02b1 »
2011-11-18 Initial import
303
d4cb3135 »
2012-08-12 README: plugins list (cc @EugenDueck)
304 ## Plugins
305
658b5a6a »
2012-08-13 Added description for engine.io-conflation plugin
306 - [engine.io-conflation](https://github.com/EugenDueck/engine.io-conflation): Makes **conflation and aggregation** of messages straightforward.
d4cb3135 »
2012-08-12 README: plugins list (cc @EugenDueck)
307
3bbf02b1 »
2011-11-18 Initial import
308 ## Support
309
310 The support channels for `engine.io` are the same as `socket.io`:
311 - irc.freenode.net **#socket.io**
312 - [Google Groups](http://groups.google.com/group/socket_io)
313 - [Website](http://socket.io)
314
315 ## Development
316
317 To contribute patches, run tests or benchmarks, make sure to clone the
318 repository:
319
320 ```
321 git clone git://github.com/LearnBoost/engine.io.git
322 ```
323
324 Then:
325
326 ```
327 cd engine.io
328 npm install
329 ```
330
331 ## Tests
332
fba04956 »
2014-02-09 README: fix test section
333 Tests run with `make test`. It runs the server tests that are aided by
334 the usage of `engine.io-client`.
3bbf02b1 »
2011-11-18 Initial import
335
fba04956 »
2014-02-09 README: fix test section
336 Make sure `npm install` is run first.
3bbf02b1 »
2011-11-18 Initial import
337
338 ## Goals
339
340 The main goal of `Engine` is ensuring the most reliable realtime communication.
6bd328e7 »
2013-03-23 Fix typos and use consistent capitalization
341 Unlike the previous Socket.IO core, it always establishes a long-polling
3bbf02b1 »
2011-11-18 Initial import
342 connection first, then tries to upgrade to better transports that are "tested" on
343 the side.
344
6bd328e7 »
2013-03-23 Fix typos and use consistent capitalization
345 During the lifetime of the Socket.IO projects, we've found countless drawbacks
3bbf02b1 »
2011-11-18 Initial import
346 to relying on `HTML5 WebSocket` or `Flash Socket` as the first connection
347 mechanisms.
348
349 Both are clearly the _right way_ of establishing a bidirectional communication,
350 with HTML5 WebSocket being the way of the future. However, to answer most business
351 needs, alternative traditional HTTP 1.1 mechanisms are just as good as delivering
352 the same solution.
353
354 WebSocket/FlashSocket based connections have two fundamental benefits:
355
356 1. **Better server performance**
357
358 - _A: Load balancers_<br>
359 Load balancing a long polling connection poses a serious architectural nightmare
360 since requests can come from any number of open sockets by the user agent, but
361 they all need to be routed to the process and computer that owns the `Engine`
362 connection. This negatively impacts RAM and CPU usage.
363 - _B: Network traffic_<br>
364 WebSocket is designed around the premise that each message frame has to be
365 surrounded by the least amount of data. In HTTP 1.1 transports, each message
366 frame is surrounded by HTTP headers and chunked encoding frames. If you try to
367 send the message _"Hello world"_ with xhr-polling, the message ultimately
368 becomes larger than if you were to send it with WebSocket.
369 - _C: Lightweight parser_<br>
370 As an effect of **B**, the server has to do a lot more work to parse the network
371 data and figure out the message when traditional HTTP requests are used
372 (as in long polling). This means that another advantage of WebSocket is
373 less server CPU usage.
374
375 2. **Better user experience**
376
377 Due to the reasons stated in point **1**, the most important effect of being able
378 to establish a WebSocket connection is raw data transfer speed, which translates
379 in _some_ cases in better user experience.
380
381 Applications with heavy realtime interaction (such as games) will benefit greatly,
382 whereas applications like realtime chat (Gmail/Facebook), newsfeeds (Facebook) or
383 timelines (Twitter) will have negligible user experience improvements.
384
385 Having said this, attempting to establish a WebSocket connection directly so far has
386 proven problematic:
387
388 1. **Proxies**<br>
389 Many corporate proxies block WebSocket traffic.
390
391 2. **Personal firewall and antivirus software**<br>
392 As a result of our research, we've found that at least 3 personal security
6bd328e7 »
2013-03-23 Fix typos and use consistent capitalization
393 applications block WebSocket traffic.
3bbf02b1 »
2011-11-18 Initial import
394
395 3. **Cloud application platforms**<br>
396 Platforms like Heroku or No.de have had trouble keeping up with the fast-paced
397 nature of the evolution of the WebSocket protocol. Applications therefore end up
398 inevitably using long polling, but the seamless installation experience of
6bd328e7 »
2013-03-23 Fix typos and use consistent capitalization
399 Socket.IO we strive for (_"require() it and it just works"_) disappears.
3bbf02b1 »
2011-11-18 Initial import
400
401 Some of these problems have solutions. In the case of proxies and personal programs,
402 however, the solutions many times involve upgrading software. Experience has shown
403 that relying on client software upgrades to deliver a business solution is
404 fruitless: the very existence of this project has to do with a fragmented panorama
405 of user agent distribution, with clients connecting with latest versions of the most
406 modern user agents (Chrome, Firefox and Safari), but others with versions as low as
407 IE 5.5.
408
409 From the user perspective, an unsuccessful WebSocket connection can translate in
410 up to at least 10 seconds of waiting for the realtime application to begin
411 exchanging data. This **perceptively** hurts user experience.
412
413 To summarize, **Engine** focuses on reliability and user experience first, marginal
414 potential UX improvements and increased server performance second. `Engine` is the
415 result of all the lessons learned with WebSocket in the wild.
416
417 ## Architecture
418
419 The main premise of `Engine`, and the core of its existence, is the ability to
420 swap transports on the fly. A connection starts as xhr-polling, but it can
421 switch to WebSocket.
422
423 The central problem this poses is: how do we switch transports without losing
424 messages?
425
426 `Engine` only switches from polling to another transport in between polling
427 cycles. Since the server closes the connection after a certain timeout when
428 there's no activity, and the polling transport implementation buffers messages
429 in between connections, this ensures no message loss and optimal performance.
430
431 Another benefit of this design is that we workaround almost all the limitations
432 of **Flash Socket**, such as slow connection times, increased file size (we can
433 safely lazy load it without hurting user experience), etc.
434
435 ## FAQ
436
437 ### Can I use engine without Socket.IO ?
438
439 Absolutely. Although the recommended framework for building realtime applications
440 is Socket.IO, since it provides fundamental features for real-world applications
441 such as multiplexing, reconnection support, etc.
442
443 `Engine` is to Socket.IO what Connect is to Express. An essential piece for building
444 realtime frameworks, but something you _probably_ won't be using for building
445 actual applications.
446
447 ### Does the server serve the client?
448
449 No. The main reason is that `Engine` is meant to be bundled with frameworks.
450 Socket.IO includes `Engine`, therefore serving two clients is not necessary. If
451 you use Socket.IO, including
452
453 ```html
454 <script src="/socket.io/socket.io.js">
455 ```
456
457 has you covered.
458
459 ### Can I implement `Engine` in other languages?
460
461918ba »
2014-01-18 README: point to new spec
461 Absolutely. The [engine.io-protocol](https://github.com/LearnBoost/engine.io-protocol)
462 repository contains the most up to date description of the specification
463 at all times, and the parser implementation in JavaScript.
3bbf02b1 »
2011-11-18 Initial import
464
465 ## License
466
467 (The MIT License)
468
193f401e »
2014-02-03 update copyright year
469 Copyright (c) 2014 Guillermo Rauch &lt;guillermo@learnboost.com&gt;
3bbf02b1 »
2011-11-18 Initial import
470
471 Permission is hereby granted, free of charge, to any person obtaining
472 a copy of this software and associated documentation files (the
473 'Software'), to deal in the Software without restriction, including
474 without limitation the rights to use, copy, modify, merge, publish,
475 distribute, sublicense, and/or sell copies of the Software, and to
476 permit persons to whom the Software is furnished to do so, subject to
477 the following conditions:
478
479 The above copyright notice and this permission notice shall be
480 included in all copies or substantial portions of the Software.
481
482 THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
483 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
484 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
485 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
486 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
487 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
488 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Something went wrong with that request. Please try again.