Full Code of amvtek/EventSource for AI

master 2ced4fad8755 cached
23 files
100.7 KB
23.6k tokens
66 symbols
1 requests
Download .txt
Repository: amvtek/EventSource
Branch: master
Commit: 2ced4fad8755
Files: 23
Total size: 100.7 KB

Directory structure:
gitextract_h5s0yxmq/

├── .gitignore
├── .gitmodules
├── Gruntfile.js
├── LICENSE
├── README.md
├── bower.json
├── dist/
│   ├── browserify-eventsource.js
│   └── eventsource.js
├── javascript/
│   ├── SpecConcurrentRunner.html
│   ├── SpecRunner.html
│   ├── spec/
│   │   ├── concurrentSpec.js
│   │   └── eventsourceSpec.js
│   └── src/
│       ├── browserify-eventsource.js
│       └── eventsource.js
├── package.json
└── test_server/
    ├── etc/
    │   ├── nginx/
    │   │   └── evs_tests.conf
    │   └── supervisor/
    │       └── evs_test_server.conf
    ├── evsutils/
    │   ├── __init__.py
    │   ├── log.py
    │   ├── protocol.py
    │   └── utils.py
    ├── requirements.txt
    └── twisted/
        └── plugins/
            └── test_eventsource.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# vim swp files
*.swp

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

# C extensions
*.so

# Distribution / packaging
.Python
env/
bin/
build/
develop-eggs/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# Twisted plugins cache
test_server/twisted/plugins/dropin.cache

# local node_modules
node_modules/

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml

# Translations
*.mo

# Mr Developer
.mr.developer.cfg
.project
.pydevproject

# Rope
.ropeproject

# Django stuff:
*.log
*.pot

# Sphinx documentation
docs/_build/



================================================
FILE: .gitmodules
================================================
[submodule "docs"]
	path = docs
	url = git@github.com:amvtek/EventSource.wiki.git


================================================
FILE: Gruntfile.js
================================================
module.exports = function(grunt) {

    "use strict";
    
    grunt.initConfig({

	pkg: grunt.file.readJSON('package.json'),

	'string-replace': {
	    dist: {
		options: {
		    replacements: [
			{pattern: /{{VERSION}}/g, replacement: '<%= pkg.version %>'}
		    ]
		},
		files: {
		    'dist/eventsource.js': ['javascript/src/eventsource.js'],
		    'dist/browserify-eventsource.js': ['javascript/src/browserify-eventsource.js']
		}
	    }
	},

	uglify: {
	    dist: {
		files: {
		    'dist/eventsource.min.js': ['dist/eventsource.js']
		}
	    }
	},

	
    });
    grunt.loadNpmTasks('grunt-string-replace');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.registerTask('default', ['string-replace', 'uglify']);
};


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014 AmvTek

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: README.md
================================================
EventSource Polyfill
====================

Provide polyfill to support EventSource in browser where it is not available.

> -   Used in production
> -   Tested in Internet Explorer 8 +
> -   Tested in Android browser 2.1 +
> -   [Documented][]
> -   Run the [Browser test suite][]

Installing
----------

### from source

Download suitable project archive (zip or tar.gz) from [release page][]

Include in your html documents one of the following javascript file:

> -   *dist/eventsource.js*
> -   *dist/eventsource.min.js* (minified version)

### Using bower package manager

To install package from **bower registry**, type :

    bower install eventsource-polyfill

Include in your html documents one of the following javascript file:

> -   *bower\_components/eventsource-polyfill/dist/eventsource.js*
> -   *bower\_components/eventsource-polyfill/dist/eventsource.min.js* (minified version)

### Using npm package manager

To install package from **npm registry**, type :

    npm install eventsource-polyfill

Note that this package may only be used with in **browser application**.

If you are using [browserify][] , you just have to require this package in your main module…

``` sourceCode
// load (Polyfill) EventSource, in case browser does not support it...
require('eventsource-polyfill');
```

Run the tests now
-----------------

With your web browser visit this [test site][Browser test suite]

Allow **sufficient time** ( ~ 5 minutes) for the full Test Suite to run…

Project content
---------------

dist/  
    built version of javascript modules

javascript/  
    Contains polyfill module and related unit tests

test_server/  
    python server which generates *easy to test* **event stream**

docs/  
    documentation wiki

  [Documented]: https://github.com/amvtek/EventSource/wiki
  [Browser test suite]: http://testevs.amvtek.com/
  [release page]: https://github.com/amvtek/EventSource/releases/latest
  [browserify]: http://browserify.org


================================================
FILE: bower.json
================================================
{
  "name": "eventsource-polyfill",
  "homepage": "https://github.com/amvtek/EventSource",
  "authors": [
    "amvtek <devel@amvtek.com>"
  ],
  "description": "A polyfill for http://www.w3.org/TR/eventsource/",
  "main": ["javascript/src/eventsource.js", "eventsource.min.js", "README.rst"],
  "keywords": [
    "sse",
    "server sent events",
    "eventsource",
    "event-source",
    "polyfill"
  ],
  "license": "MIT",
  "ignore": [
    "**/.*",
    "javascript",
    "test_server",
    "Gruntfile.js",
    "package.json",
    "node_modules",
    "bower_components",
    "test",
    "tests",
    "docs"
  ]
}


================================================
FILE: dist/browserify-eventsource.js
================================================
/*
   * CommonJS module that exports EventSource polyfill version 0.9.7
   * This module is intended for browser side use
   * =====================================================================
   * THIS IS A POLYFILL MODULE, SO IT HAS SIDE EFFECTS
   * IT AUTOMATICALLY CHECKS IF window OBJECT DEFINES EventSource
   * AND ADD THE EXPORTED ONE IN CASE IT IS UNDEFINED
   * =====================================================================
   * Supported by sc AmvTek srl
   * :email: devel@amvtek.com
 */


var PolyfillEventSource = require('./eventsource.js').EventSource;
module.exports = PolyfillEventSource;

// Add EventSource to window if it is missing...
if (window && !window.EventSource){
    window.EventSource = PolyfillEventSource;
    if (console){
	console.log("polyfill-eventsource added missing EventSource to window");
    }
}


================================================
FILE: dist/eventsource.js
================================================
/*
   * EventSource polyfill version 0.9.7
   * Supported by sc AmvTek srl
   * :email: devel@amvtek.com
 */
;(function (global) {

    if (global.EventSource && !global._eventSourceImportPrefix){
        return;
    }

    var evsImportName = (global._eventSourceImportPrefix||'')+"EventSource";

    var EventSource = function (url, options) {

        if (!url || typeof url != 'string') {
            throw new SyntaxError('Not enough arguments');
        }

        this.URL = url;
        this.setOptions(options);
        var evs = this;
        setTimeout(function(){evs.poll()}, 0);
    };

    EventSource.prototype = {

        CONNECTING: 0,

        OPEN: 1,

        CLOSED: 2,

        defaultOptions: {

            loggingEnabled: false,

            loggingPrefix: "eventsource",

            interval: 500, // milliseconds

            bufferSizeLimit: 256*1024, // bytes

            silentTimeout: 300000, // milliseconds

            getArgs:{
                'evs_buffer_size_limit': 256*1024
            },

            xhrHeaders:{
                'Accept': 'text/event-stream',
                'Cache-Control': 'no-cache',
                'X-Requested-With': 'XMLHttpRequest'
            }
        },

        setOptions: function(options){

            var defaults = this.defaultOptions;
            var option;

            // set all default options...
            for (option in defaults){

                if ( defaults.hasOwnProperty(option) ){
                    this[option] = defaults[option];
                }
            }

            // override with what is in options
            for (option in options){

                if (option in defaults && options.hasOwnProperty(option)){
                    this[option] = options[option];
                }
            }

            // if getArgs option is enabled
            // ensure evs_buffer_size_limit corresponds to bufferSizeLimit
            if (this.getArgs && this.bufferSizeLimit) {

                this.getArgs['evs_buffer_size_limit'] = this.bufferSizeLimit;
            }

            // if console is not available, force loggingEnabled to false
            if (typeof console === "undefined" || typeof console.log === "undefined") {

                this.loggingEnabled = false;
            }
        },

        log: function(message) {

            if (this.loggingEnabled) {

                console.log("[" + this.loggingPrefix +"]:" + message)
            }
        },

        poll: function() {

            try {

                if (this.readyState == this.CLOSED) {
                    return;
                }

                this.cleanup();
                this.readyState = this.CONNECTING;
                this.cursor = 0;
                this.cache = '';
                this._xhr = new this.XHR(this);
                this.resetNoActivityTimer();

            }
            catch (e) {

                // in an attempt to silence the errors
                this.log('There were errors inside the pool try-catch');
                this.dispatchEvent('error', { type: 'error', data: e.message });
            }
        },

        pollAgain: function (interval) {

            // schedule poll to be called after interval milliseconds
            var evs = this;
            evs.readyState = evs.CONNECTING;
            evs.dispatchEvent('error', {
                type: 'error',
                data: "Reconnecting "
            });
            this._pollTimer = setTimeout(function(){evs.poll()}, interval||0);
        },


        cleanup: function() {

            this.log('evs cleaning up')

            if (this._pollTimer){
                clearInterval(this._pollTimer);
                this._pollTimer = null;
            }

            if (this._noActivityTimer){
                clearInterval(this._noActivityTimer);
                this._noActivityTimer = null;
            }

            if (this._xhr){
                this._xhr.abort();
                this._xhr = null;
            }
        },

        resetNoActivityTimer: function(){

            if (this.silentTimeout){

                if (this._noActivityTimer){
                    clearInterval(this._noActivityTimer);
                }
                var evs = this;
                this._noActivityTimer = setTimeout(
                        function(){ evs.log('Timeout! silentTImeout:'+evs.silentTimeout); evs.pollAgain(); },
                        this.silentTimeout
                        );
            }
        },

        close: function () {

            this.readyState = this.CLOSED;
            this.log('Closing connection. readyState: '+this.readyState);
            this.cleanup();
        },

        _onxhrdata: function() {

            var request = this._xhr;

            if (request.isReady() && !request.hasError() ) {
                // reset the timer, as we have activity
                this.resetNoActivityTimer();

                // move this EventSource to OPEN state...
                if (this.readyState == this.CONNECTING) {
                    this.readyState = this.OPEN;
                    this.dispatchEvent('open', { type: 'open' });
                }

                var buffer = request.getBuffer();

                if (buffer.length > this.bufferSizeLimit) {
                    this.log('buffer.length > this.bufferSizeLimit');
                    this.pollAgain();
                }

                if (this.cursor == 0 && buffer.length > 0){

                    // skip byte order mark \uFEFF character if it starts the stream
                    if (buffer.substring(0,1) == '\uFEFF'){
                        this.cursor = 1;
                    }
                }

                var lastMessageIndex = this.lastMessageIndex(buffer);
                if (lastMessageIndex[0] >= this.cursor){

                    var newcursor = lastMessageIndex[1];
                    var toparse = buffer.substring(this.cursor, newcursor);
                    this.parseStream(toparse);
                    this.cursor = newcursor;
                }

                // if request is finished, reopen the connection
                if (request.isDone()) {
                    this.log('request.isDone(). reopening the connection');
                    this.pollAgain(this.interval);
                }
            }
            else if (this.readyState !== this.CLOSED) {

                this.log('this.readyState !== this.CLOSED');
                this.pollAgain(this.interval);

                //MV: Unsure why an error was previously dispatched
            }
        },

        parseStream: function(chunk) {

            // normalize line separators (\r\n,\r,\n) to \n
            // remove white spaces that may precede \n
            chunk = this.cache + this.normalizeToLF(chunk);

            var events = chunk.split('\n\n');

            var i, j, eventType, datas, line, retry;

            for (i=0; i < (events.length - 1); i++) {

                eventType = 'message';
                datas = [];
                parts = events[i].split('\n');

                for (j=0; j < parts.length; j++) {

                    line = this.trimWhiteSpace(parts[j]);

                    if (line.indexOf('event') == 0) {

                        eventType = line.replace(/event:?\s*/, '');
                    }
                    else if (line.indexOf('retry') == 0) {

                        retry = parseInt(line.replace(/retry:?\s*/, ''));
                        if(!isNaN(retry)) {
                            this.interval = retry;
                        }
                    }
                    else if (line.indexOf('data') == 0) {

                        datas.push(line.replace(/data:?\s*/, ''));
                    }
                    else if (line.indexOf('id:') == 0) {

                        this.lastEventId = line.replace(/id:?\s*/, '');
                    }
                    else if (line.indexOf('id') == 0) { // this resets the id

                        this.lastEventId = null;
                    }
                }

                if (datas.length) {
                    // dispatch a new event
                    var event = new MessageEvent(eventType, datas.join('\n'), window.location.origin, this.lastEventId);
                    this.dispatchEvent(eventType, event);
                }
            }

            this.cache = events[events.length - 1];
        },

        dispatchEvent: function (type, event) {
            var handlers = this['_' + type + 'Handlers'];

            if (handlers) {

                for (var i = 0; i < handlers.length; i++) {
                    handlers[i].call(this, event);
                }
            }

            if (this['on' + type]) {
                this['on' + type].call(this, event);
            }

        },

        addEventListener: function (type, handler) {
            if (!this['_' + type + 'Handlers']) {
                this['_' + type + 'Handlers'] = [];
            }

            this['_' + type + 'Handlers'].push(handler);
        },

        removeEventListener: function (type, handler) {
            var handlers = this['_' + type + 'Handlers'];
            if (!handlers) {
                return;
            }
            for (var i = handlers.length - 1; i >= 0; --i) {
                if (handlers[i] === handler) {
                    handlers.splice(i, 1);
                    break;
                }
            }
        },

        _pollTimer: null,

        _noactivityTimer: null,

        _xhr: null,

        lastEventId: null,

        cache: '',

        cursor: 0,

        onerror: null,

        onmessage: null,

        onopen: null,

        readyState: 0,

        // ===================================================================
        // helpers functions
        // those are attached to prototype to ease reuse and testing...

        urlWithParams: function (baseURL, params) {

            var encodedArgs = [];

            if (params){

                var key, urlarg;
                var urlize = encodeURIComponent;

                for (key in params){
                    if (params.hasOwnProperty(key)) {
                        urlarg = urlize(key)+'='+urlize(params[key]);
                        encodedArgs.push(urlarg);
                    }
                }
            }

            if (encodedArgs.length > 0){

                if (baseURL.indexOf('?') == -1)
                    return baseURL + '?' + encodedArgs.join('&');
                return baseURL + '&' + encodedArgs.join('&');
            }
            return baseURL;
        },

        lastMessageIndex: function(text) {

            var ln2 =text.lastIndexOf('\n\n');
            var lr2 = text.lastIndexOf('\r\r');
            var lrln2 = text.lastIndexOf('\r\n\r\n');

            if (lrln2 > Math.max(ln2, lr2)) {
                return [lrln2, lrln2+4];
            }
            return [Math.max(ln2, lr2), Math.max(ln2, lr2) + 2]
        },

        trimWhiteSpace: function(str) {
            // to remove whitespaces left and right of string

            var reTrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
            return str.replace(reTrim, '');
        },

        normalizeToLF: function(str) {

            // replace \r and \r\n with \n
            return str.replace(/\r\n|\r/g, '\n');
        }

    };

    if (!isOldIE()){

        EventSource.isPolyfill = "XHR";

        // EventSource will send request using XMLHttpRequest
        EventSource.prototype.XHR = function(evs) {

            request = new XMLHttpRequest();
            this._request = request;
            evs._xhr = this;

            // set handlers
            request.onreadystatechange = function(){
                if (request.readyState > 1 && evs.readyState != evs.CLOSED) {
                    if (request.status == 200 || (request.status>=300 && request.status<400)){
                        evs._onxhrdata();
                    }
                    else {
                        request._failed = true;
                        evs.readyState = evs.CLOSED;
                        evs.dispatchEvent('error', {
                            type: 'error',
                            data: "The server responded with "+request.status
                        });
                        evs.close();
                    }
                }
            };

            request.onprogress = function () {
            };

            request.open('GET', evs.urlWithParams(evs.URL, evs.getArgs), true);

            var headers = evs.xhrHeaders; // maybe null
            for (var header in headers) {
                if (headers.hasOwnProperty(header)){
                    request.setRequestHeader(header, headers[header]);
                }
            }
            if (evs.lastEventId) {
                request.setRequestHeader('Last-Event-Id', evs.lastEventId);
            }

            request.send();
        };

        EventSource.prototype.XHR.prototype = {

            useXDomainRequest: false,

            _request: null,

            _failed: false, // true if we have had errors...

            isReady: function() {


                return this._request.readyState >= 2;
            },

            isDone: function() {

                return (this._request.readyState == 4);
            },

            hasError: function() {

                return (this._failed || (this._request.status >= 400));
            },

            getBuffer: function() {

                var rv = '';
                try {
                    rv = this._request.responseText || '';
                }
                catch (e){}
                return rv;
            },

            abort: function() {

                if ( this._request ) {
                    this._request.abort();
                }
            }
        };
    }
    else {

	EventSource.isPolyfill = "IE_8-9";

        // patch EventSource defaultOptions
        var defaults = EventSource.prototype.defaultOptions;
        defaults.xhrHeaders = null; // no headers will be sent
        defaults.getArgs['evs_preamble'] = 2048 + 8;

        // EventSource will send request using Internet Explorer XDomainRequest
        EventSource.prototype.XHR = function(evs) {

            request = new XDomainRequest();
            this._request = request;

            // set handlers
            request.onprogress = function(){
                request._ready = true;
                evs._onxhrdata();
            };

            request.onload = function(){
                this._loaded = true;
                evs._onxhrdata();
            };

            request.onerror = function(){
                this._failed = true;
                evs.readyState = evs.CLOSED;
                evs.dispatchEvent('error', {
                    type: 'error',
                    data: "XDomainRequest error"
                });
            };

            request.ontimeout = function(){
                this._failed = true;
                evs.readyState = evs.CLOSED;
                evs.dispatchEvent('error', {
                    type: 'error',
                    data: "XDomainRequest timed out"
                });
            };

            // XDomainRequest does not allow setting custom headers
            // If EventSource has enabled the use of GET arguments
            // we add parameters to URL so that server can adapt the stream...
            var reqGetArgs = {};
            if (evs.getArgs) {

                // copy evs.getArgs in reqGetArgs
                var defaultArgs = evs.getArgs;
                    for (var key in defaultArgs) {
                        if (defaultArgs.hasOwnProperty(key)){
                            reqGetArgs[key] = defaultArgs[key];
                        }
                    }
                if (evs.lastEventId){
                    reqGetArgs['evs_last_event_id'] = evs.lastEventId;
                }
            }
            // send the request

            request.open('GET', evs.urlWithParams(evs.URL,reqGetArgs));
            request.send();
        };

        EventSource.prototype.XHR.prototype = {

            useXDomainRequest: true,

            _request: null,

            _ready: false, // true when progress events are dispatched

            _loaded: false, // true when request has been loaded

            _failed: false, // true if when request is in error

            isReady: function() {

                return this._request._ready;
            },

            isDone: function() {

                return this._request._loaded;
            },

            hasError: function() {

                return this._request._failed;
            },

            getBuffer: function() {

                var rv = '';
                try {
                    rv = this._request.responseText || '';
                }
                catch (e){}
                return rv;
            },

            abort: function() {

                if ( this._request){
                    this._request.abort();
                }
            }
        };
    }

    function MessageEvent(type, data, origin, lastEventId) {

        this.bubbles = false;
        this.cancelBubble = false;
        this.cancelable = false;
        this.data = data || null;
        this.origin = origin || '';
        this.lastEventId = lastEventId || '';
        this.type = type || 'message';
    }

    function isOldIE () {

        //return true if we are in IE8 or IE9
        return (window.XDomainRequest && (window.XMLHttpRequest && new XMLHttpRequest().responseType === undefined)) ? true : false;
    }

    global[evsImportName] = EventSource;
})(this);


================================================
FILE: javascript/SpecConcurrentRunner.html
================================================
<!DOCTYPE HTML>
<html>	
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Polyfill EventSource Test Suite</title>

  <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.0.0/jasmine_favicon.png">
  <link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.0/jasmine.css">

  <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine.js"></script>
  <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine-html.js"></script>
  <script type="text/javascript" src="lib/jasmine-2.0.0/boot.js"></script>

  <!-- include source files here... -->
  <!--<script type="text/javascript" src="src/EventSource.js"></script>-->
    <script>
        // We define a prefix prior to importing evs
        window._eventSourceImportPrefix = 'Test';
    </script>
    <script type="text/javascript" src="src/eventsource.js"></script>

  <!-- include spec files here... -->
    <!--<script type="text/javascript" src="spec/EventSourceSpec.js"></script>-->
    <script type="text/javascript" src="spec/concurrentSpec.js"></script>

</head>

<body>

<h1> Concurrent polyfill EventSource Test Suite </h1>

<div class="test-infos">
  <p>
      Be patient, those tests need a few minutes to complete.
  </p>

  <script>
      if (window["EventSource"]) {
	  document.write('<p>This browser supports EventSource natively</p>')
      }
      else {
	  document.write("<p>This browser doesn't support EventSource</p>")
      }
  </script>
</div>

</body>

</html>


================================================
FILE: javascript/SpecRunner.html
================================================
<!DOCTYPE HTML>
<html>	
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Polyfill EventSource Test Suite</title>

  <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.0.0/jasmine_favicon.png">
  <link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.0/jasmine.css">

  <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine.js"></script>
  <script type="text/javascript" src="lib/jasmine-2.0.0/jasmine-html.js"></script>
  <script type="text/javascript" src="lib/jasmine-2.0.0/boot.js"></script>

  <!-- include source files here... -->
  <!--<script type="text/javascript" src="src/EventSource.js"></script>-->
    <script>
        // We define a prefix prior to importing evs
        window._eventSourceImportPrefix = 'Test';
    </script>
    <script type="text/javascript" src="src/eventsource.js"></script>

  <!-- include spec files here... -->
    <!--<script type="text/javascript" src="spec/EventSourceSpec.js"></script>-->
    <script type="text/javascript" src="spec/eventsourceSpec.js"></script>

</head>

<body>

<h1> Polyfill EventSource Test Suite </h1>

<div class="test-infos">
  <p>
      Be patient, those tests need a few minutes to complete.
  </p>

  <script>
      if (window["EventSource"]) {
	  document.write('<p>This browser supports EventSource natively</p>')
      }
      else {
	  document.write("<p>This browser doesn't support EventSource</p>")
      }
  </script>
</div>

</body>

</html>


================================================
FILE: javascript/spec/concurrentSpec.js
================================================

describe('Test concurrent operation of various EventSource against real stream', function(){

    var polyfillEventSource = window['TestEventSource'];

    var nativeEventSource = window['EventSource'];

    var TEST_SOURCE_URLS = [
	"/test/eventsource/4-messages-with-seed-01",
	"/test/eventsource/6-messages-with-seed-02",
	"/test/eventsource/8-messages-closeat-4-with-seed-03",
	"/test/eventsource/16-messages-closeat-5-with-seed-04",
    ];


    jasmine.DEFAULT_TIMEOUT_INTERVAL = 300000;
    
    var WM = function (EventSourceFactory, EventSourceName, sourceUrls){
	
	var self = {};
	
	var _count = sourceUrls.length;
	
	self.setdonecb = function(donefunc){

	    if (_count <= 0 && donefunc ){
		donefunc();
	    }
	    self.donecallback = donefunc;
	};

	self.complete = function(){

	    --_count;
	    if (_count <= 0 && self.donecallback){
		self.donecallback();
	    }
	};

	function EVS(srcUrl){

	    var evs = new EventSourceFactory(srcUrl);
	    var ctx = this;

	    ctx.eventsource = evs;

	    ctx.msgEvents = [];
	    evs.addEventListener('message', function (e) {
		if (e.data) {ctx.msgEvents.push(e.data);}
	    }, false);

	    ctx.openEvents = [];
	    evs.addEventListener('open', function (e) {
		ctx.openEvents.push(e.type);
	    }, false);

	    ctx.errorEvents = [];
	    evs.addEventListener('error', function (e) {
		ctx.errorEvents.push(e.type);
	    }, false);

	    ctx.testmetaEvents = [];
	    evs.addEventListener('testmeta', function (e) {
		ctx.testmetaEvents = JSON.parse(e.data);
	    }, false);

	    ctx.testendEvents = [];
	    evs.addEventListener('testend', function (e) {
		ctx.testendEvents.push(e.type);
		self.complete();
		evs.close();
	    }, false);
	};
	
	// create one EventSource for each url in sourceUrls
	self.sources = [];
	for (var i=0; i < sourceUrls.length; i++){

	    console.log("creating new "+EventSourceName+" on "+sourceUrls[i]);
	    self.sources.push(new EVS(sourceUrls[i]));
	}

	return self;
    };


    describe("consume 4 sources concurrently", function(){

        describe('using polyfill eventsource', function () {

	    var wm = WM(polyfillEventSource,"polyfill EventSource", TEST_SOURCE_URLS);

            beforeEach(function (done) {

		this.sources = wm.sources;
		wm.setdonecb(done);
            });
	    
	    afterEach(function (done){

		console.log("polyfill EventSource SPEC OVER...");
		done();
	    });


	    it("sources.length shall be equal to TEST_SOURCE_URLS.length", function (){

		expect(this.sources.length).toEqual(TEST_SOURCE_URLS.length);
	    });

	    it("data in testmeta event should match all incoming messages data", function () {

		var result;
		for (var i=0; i < this.sources.length; i++){

		    result = this.sources[i];
		    expect(result.testmetaEvents).toEqual(result.msgEvents);
		};
	    });

	    it("testend event was received", function () {

		var result;
		for (var i=0; i < this.sources.length; i++){

		    result = this.sources[i];
		    expect(result.testendEvents.length).toEqual(1);
		};
	    });
        });

	if (nativeEventSource){

	    describe('using native eventsource', function () {

		var wm = WM(nativeEventSource,"native EventSource", TEST_SOURCE_URLS);

		beforeEach(function (done) {
		    this.sources = wm.sources;
		    wm.setdonecb(done);
		});

		afterEach(function (done){

		    console.log("native EventSource SPEC OVER...");
		    done();
		});

		it("sources.length shall be equal to TEST_SOURCE_URLS.length", function (){

		    expect(this.sources.length).toEqual(TEST_SOURCE_URLS.length);
		});

		it("data in testmeta event should match all incoming messages data", function () {

		    var result;
		    for (var i=0; i < this.sources.length; i++){

			result = this.sources[i];
			expect(result.testmetaEvents).toEqual(result.msgEvents);
		    };
		});

		it("testend event was received", function () {

		    var result;
		    for (var i=0; i < this.sources.length; i++){

			result = this.sources[i];
			expect(result.testendEvents.length).toEqual(1);
		    };
		});
	    });
	};
    });
});


================================================
FILE: javascript/spec/eventsourceSpec.js
================================================
/**
 * Created by vasi on 4/8/14.
 */

var evsName = "EventSource",
    evsImportName = (window._eventSourceImportPrefix || '') + evsName,
    isEventSourceSupported = (window["EventSource"] != undefined);

describe("Testing EventSource polyfill with MockupXHR", function() {

    var previousXHR;

    beforeEach(function() {

        var mockupXHR = function(evs) {

            this.responseText = '';
            this.evs = evs;
        };

        mockupXHR.prototype = {

            useXDomaineRequest: false,

            _request: null,

            _failed: false,

            isReady: function() {

                return true;
            },

            isDone: function() {

                return false;
            },

            hasError: function() {

                return (this._failed);
            },

            getBuffer: function() {

                return this.responseText;
            },

            abort: function() {

                this.responseText = '';
            },

            sendData: function(str) {

                this.responseText += str;
                this.evs._onxhrdata();
            }
        };

        this.eventSource = window[evsImportName];
        previousXHR = this.eventSource.prototype.XHR;
        this.eventSource.prototype.XHR = mockupXHR;
    });

    afterEach(function () {
        this.eventSource.prototype.XHR = previousXHR;
    });

    describe("Provide MockupXHR class to ease testing the EventSource class", function() {
        var evs;

        beforeEach(function (done) {

            evs = new this.eventSource('http://exampleLoadMockupXHR.com');
            setTimeout(function () {
                done();
            }, 0);
        });

        afterEach(function (){

            evs.close();
        });

        it("should load mockupXHR instead of native XHR", function() {
            // the prefix is set in SpecRunner.html

            expect(evs._xhr.sendData).toBeDefined();
        });
    });

    describe("Setting up the polyfill EventSource for it to be available alongside browser native EventSource", function() {

        it("When no options object is passed to the EventSource constructor, all option in EventSource.prototype.defaultOptions are set.", function() {
            // the prefix is set in SpecRunner.html

            var evs = new this.eventSource('http://example.com');
            var expected = evs.defaultOptions;  // in this test we expect all options to be the default ones

            var actual = {
                loggingEnabled: evs.loggingEnabled,
                loggingPrefix: evs.loggingPrefix,
                interval: evs.interval,
                bufferSizeLimit: evs.bufferSizeLimit, // bytes
                silentTimeout: evs.silentTimeout, // milliseconds
                getArgs: evs.getArgs,
                xhrHeaders: evs.xhrHeaders
            };

            expect(actual).toEqual(expected);

            evs.close();
        });

        it("When options is passed to constructor, all option not defined in defaultOptions are ignored.", function() {
            // the prefix is set in SpecRunner.html

            var extraOptions = {
                extraOption: 1000
            };
            var evs = new this.eventSource('http://example.com', extraOptions);

            expect(evs.extraOption).toBeUndefined();

            evs.close();
        });

        it("When options is passed to constructor, option defined in defaultOptions are over written.", function() {
            // the prefix is set in SpecRunner.html

            var options = {
                interval: 1000,
                bufferSizeLimit: 256*1024 // bytes
            };

            var evs = new this.eventSource('http://example.com', options);
            var defaults = evs.defaultOptions;

            var expected = {
                interval: options.interval,
                bufferSizeLimit: options.bufferSizeLimit,
                silentTimeout: defaults.silentTimeout, // milliseconds
                getArgs: defaults.getArgs,
                xhrHeaders: defaults.xhrHeaders
            };

            var actual = {
                interval: evs.interval,
                bufferSizeLimit: evs.bufferSizeLimit, // bytes
                silentTimeout: evs.silentTimeout, // milliseconds
                getArgs: evs.getArgs,
                xhrHeaders: evs.xhrHeaders
            };

            expect(actual).toEqual(expected);

            evs.close();
        });
    });

    describe("tests for urlWithParams", function() {

        it("When the baseURL already contains arguments, the rest of evs arguments are appended.", function() {

            var params =  {
                a: 1,
                b: 2
            };
            var evs = new this.eventSource('http://example.com?ciao=ola');
            var urlWithParams = evs.urlWithParams;
            expect(urlWithParams('http://example.com?ciao=ola', params)).toBe('http://example.com?ciao=ola&a=1&b=2');
        })

        it("When called with a null or undefined params, baseUrl is returned.", function() {
            // the prefix is set in SpecRunner.html

            var evs = new this.eventSource('http://exampleurlWithParams.com');
            var urlWithParams = evs.urlWithParams;
            expect(urlWithParams('http://example.com')).toBe('http://example.com');
        });

        it("When called with a params object that contains no attribute but has a prototype with attributes, baseUrl is returned.", function() {
            // the prefix is set in SpecRunner.html

            var Params = function () {};
            Params.prototype = {
                'a': 1,
                'b': 2
            };
            var evs = new this.eventSource('http://exampleurlWithParams.com');
            var urlWithParams = evs.urlWithParams;
            expect(urlWithParams('http://example.com', new Params())).toBe('http://example.com');
        });

        it("When called with a params that define arguments, those are added to the url", function() {
            // the prefix is set in SpecRunner.html

            var params =  {
                a: 1,
                b: 2
            };
            var evs = new this.eventSource('http://exampleurlWithParams.com');
            var urlWithParams = evs.urlWithParams;
            expect(urlWithParams('http://example.com', params)).toBe('http://example.com?a=1&b=2');
        });
    });

    describe("tests for lastLineIndex", function() {

        it("returns correct lastMessageIndex in case of \\n\\n", function() {
            // the prefix is set in SpecRunner.html

            var evs = new this.eventSource('http://exampleurlWithParams.com');
            var lastLineIndex = evs.lastMessageIndex;
            expect(lastLineIndex("0123\r\r678\n\n9")).toEqual([9, 11]);
        });

        it("returns correct lastLineIndex in case of \\r\\r", function() {
            // the prefix is set in SpecRunner.html

            var evs = new this.eventSource('http://exampleurlWithParams.com');
            var lastLineIndex = evs.lastMessageIndex;
            expect(lastLineIndex("0123\n\n678\r\r")).toEqual([9, 11]);
        });

        it("returns correct lastLineIndex in case of \\r\\n\\r\\n", function() {
            // the prefix is set in SpecRunner.html

            var evs = new this.eventSource('http://exampleurlWithParams.com');
            var lastLineIndex = evs.lastMessageIndex;
            expect(lastLineIndex("0123\n\n67\r\n\r\n")).toEqual([8, 12]);
        });
    });

    describe("tests for trimWhiteSpace", function() {

        it("should remove whitespace left and right of the string", function () {
            // the prefix is set in SpecRunner.html

            var evs = new this.eventSource('http://exampleurlWithParams.com');
            var trimWhiteSpace = evs.trimWhiteSpace;
            expect(trimWhiteSpace("       text between spaces    ")).toBe('text between spaces');
        });
    });

    describe("tests for normalizeToLF", function() {

        it("should replace CR and CRLF with LF(\\n) inside a string", function () {
            // the prefix is set in SpecRunner.html

            var evs = new this.eventSource('http://example.com');
            var normalizeToLF = evs.normalizeToLF;

            var str = "LF:\n. CR:\r. CRLF:\r\n. and again LF:\n. CR:\r. CRLF:\r\n. Double CR:\r\r. Double CRLF:\r\n\r\n";
            var expectedStr = "LF:\n. CR:\n. CRLF:\n. and again LF:\n. CR:\n. CRLF:\n. Double CR:\n\n. Double CRLF:\n\n";
            var actualStr = normalizeToLF(str);

            expect(actualStr).toBe(expectedStr);

            evs.close();
        });
    });

    describe("Simulates EventSource stream using MockupXHR", function() {

        var evs, receivedMessageEvents, receivedOpenEvents, receivedErrorEvents;

        beforeEach(function (done) {

            evs = new this.eventSource('http://exampleSimulatesEventSourceWithMockupXHR.com');
            receivedMessageEvents = [];
            receivedOpenEvents = [];
            receivedErrorEvents = [];
            evs.addEventListener('message', function(e) {
                receivedMessageEvents.push(e.data);
            }, false);
            evs.addEventListener('open', function(e) {
                receivedOpenEvents.push(e.type);
            }, false);
            evs.addEventListener('error', function(e) {
                receivedErrorEvents.push(e.type);
            }, false);

            setTimeout(function () {
                done();
            }, 0)
        });

        afterEach(function (done) {
            evs.close();
            done();
        });

        it("check that Mockup XHR is used", function () {

            expect(evs._xhr.sendData).toBeDefined();
        });

        it("Initial BOM mark is skipped", function () {

            evs._xhr.sendData('\ufeffdata: "First line of data."\r\r');
            var expectedEvents = ['"First line of data."'];
            expect(receivedMessageEvents).toEqual(expectedEvents);
        });

        it("Multiline message are properly reassembled", function () {

            evs._xhr.sendData('data: "First line of data."\ndata: "Second line of data."\r\r');
            var expectedEvents = ['"First line of data."\n"Second line of data."'];
            expect(receivedMessageEvents).toEqual(expectedEvents);
        });

        it("Chunky transmissions are properly buffered", function () {

            evs._xhr.sendData('data: "First line of data."\ndata: "Second line of data."\n\ndata: "First part of broken message.');
            evs._xhr.sendData('Second part of broken message."\n\n');
            var expectedEvents = ['"First line of data."\n"Second line of data."', '"First part of broken message.Second part of broken message."'];
            expect(receivedMessageEvents).toEqual(expectedEvents);
        });

        it("id lines are properly processed", function () {

            var expectedEventId = '1983';
            evs._xhr.sendData('data: "First line of data."\ndata: "Second line of data."\n\ndata: "First part of broken message.');
            evs._xhr.sendData('Second part of broken message."\n\nid: '+expectedEventId+'\ndata: "Message with new id"\n\n');
            expect(evs.lastEventId).toEqual(expectedEventId);
        });

        it("option bufferSizeLimit is taken into account", function () {

            evs.bufferSizeLimit = 100;
            evs._xhr.sendData('data: "First line of data."\ndata: "Second line of data."\n\ndata: "First part of broken message.');
            evs._xhr.sendData('Second part of broken message."\n\nid: 1983\rdata: "Message with new id"\n\n');
            evs._xhr.sendData('data: "Message to force dispatching the open event"\n\n');
            var expectedOpenEvents = ["open", "open"];
            expect(receivedOpenEvents).toEqual(expectedOpenEvents);
        });

        describe("Manually ticking the Jasmine Clock to produce a sleep until events are fired:", function () {

            var previous = 0;

            beforeEach(function() {

                previous = evs.silentTimeout;
                jasmine.clock().install();
            });

            afterEach(function(done) {

                evs.silentTimeout = previous;
                jasmine.clock().uninstall();

                done();
            });

            it("option silentTimeout is taken into account", function () {

                evs.silentTimeout = 1000;
                evs._xhr.sendData('data: "First line of data."\ndata: "Second line of data."\n\r\ndata: "First part of broken message.');
                evs._xhr.sendData('Second part of broken message."\n\r\nid: 1983\rdata: "Message with new id"\n\n');

                setTimeout(function() {
                }, 1100);
                jasmine.clock().tick(1101);

                evs._xhr.sendData('data: "Message to force dispatching the open event"\n\n');
                var expectedOpenEvents = ["open", "open"];
                var expectedErrorEvents = ["error"];

                expect(receivedOpenEvents).toEqual(expectedOpenEvents);
                expect(receivedErrorEvents).toEqual(expectedErrorEvents);
            });
        });
    });
});

describe("Evaluating EventSource 'time to attach listener' doubt", function() {

    var evs, previousXHR,
        receivedMessageEvents = [],
        receivedOpenEvents = [],
        receivedErrorEvents = [];

    beforeEach(function (done) {

        var mockupXHR = function (evs) {

            this.responseText = '';
            this.evs = evs;
            evs._xhr = this;

            // send data right away to stress the events
            this.sendData(this.initialData || '');
        };

        mockupXHR.prototype = {

            initialData: 'data: "I will fire very quick"\n\n',

            useXDomaineRequest: false,

            _request: null,

            _failed: false,

            isReady: function () {

                return true;
            },

            isDone: function () {

                return false;
            },

            hasError: function () {

                return (this._failed);
            },

            getBuffer: function () {

                return this.responseText;
            },

            abort: function () {

            },

            sendData: function (str) {

                evs = this.evs;
                this.responseText += str;
                evs._onxhrdata();
            }
        };

        this.eventSource = window[evsImportName];
        previousXHR = this.eventSource.prototype.XHR;
        this.eventSource.prototype.XHR = mockupXHR;

        evs = new this.eventSource('http://exampleTimeToAttachDoubt.com');

        evs.addEventListener('message', function(e) {
            receivedMessageEvents.push(e.data);
        }, false);
        evs.addEventListener('open', function(e) {
            receivedOpenEvents.push(e.type);
        }, false);
        evs.addEventListener('error', function(e) {
            receivedErrorEvents.push(e.data);
        }, false);
        setTimeout(function () {
            done();
        }, 0);
    });

    afterEach(function (done) {
        evs.close();
        this.eventSource.prototype.XHR = previousXHR;
        done();
    });

    it("should catch initial message sent", function (done) {

        var expectedMessageEvents = ['"I will fire very quick"'];
        expect(receivedMessageEvents).toEqual(expectedMessageEvents);
        done();
    })
});

describe('Failed XHR request(invalid url) shall trigger EventSource to close and "error" event to be dispatched', function() {

    var evs,
        receivedMessageEvents = [],
        receivedOpenEvents = [],
        receivedErrorEvents = [];

    beforeEach(function (done) {

        this.eventSource = window[evsImportName];

        // sending to wrong url
        evs = new this.eventSource('http://exampleFailedXHRequest');

        evs.addEventListener('message', function (e) {
            receivedMessageEvents.push(e.data);
        }, false);
        evs.addEventListener('open', function (e) {
            receivedOpenEvents.push(e.type);
        }, false);
        evs.addEventListener('error', function (e) {
            receivedErrorEvents.push(e.type);
            done();
        }, false);
    });

    afterEach(function (done) {

        evs.close();
        receivedErrorEvents = [];
        done();
    });

    it("should send error event", function (done) {

        expect(receivedErrorEvents.length).toBe(1);
        done();
    });
});

describe('Failed XHR request(missing-code 404) shall trigger EventSource to close and "error" event to be dispatched', function() {

    var evs,
        receivedMessageEvents = [],
        receivedOpenEvents = [],
        receivedErrorEvents = [];

    beforeEach(function (done) {

        this.eventSource = window[evsImportName];

        // sending to wrong url
        evs = new this.eventSource('/missing');

        evs.addEventListener('message', function (e) {
            receivedMessageEvents.push(e.data);
        }, false);
        evs.addEventListener('open', function (e) {
            receivedOpenEvents.push(e.type);
        }, false);
        evs.addEventListener('error', function (e) {
            receivedErrorEvents.push(e.type);
            done();
        }, false);
    });

    afterEach(function () {

        evs.close()
    });

    it("should send error event", function (done) {

        expect(receivedErrorEvents.length).toBe(1);
        done();
    });
});

describe('Tests with twisted server:', function() {


    var evs,
        receivedMessageEvents = [],
        receivedOpenEvents = [],
        receivedErrorEvents = [],
        receivedTestMetaEvents = [],
        receivedTestEndEvents = [];

    function addEventListeners(evs, done) {

        evs.addEventListener('message', function (e) {
//            if (e.lastEventId) {console.log(e.lastEventId);}
//            console.log('message: ' + e.type + ":" + e.data)
//            console.log(e)
            if (e.data) {receivedMessageEvents.push(e.data);}
        }, false);
        evs.addEventListener('open', function (e) {
            receivedOpenEvents.push(e.type);
        }, false);
        evs.addEventListener('error', function (e) {
            receivedErrorEvents.push(e.type);
        }, false);
        evs.addEventListener('testmeta', function (e) {
//            console.log("received testmeta:" + e.type + "["+ evs.id +"]: "+ e.data);
//            console.log(e)
            receivedTestMetaEvents = JSON.parse(e.data);
        }, false);
        evs.addEventListener('testend', function (e) {
            receivedTestEndEvents.push(e.type);
//            console.log('received tesend' + e.type + ":" + e.data + "end");
//            console.log(e)
            done();
        }, false);
    }

    afterEach(function (done) {

        evs.close();
        receivedErrorEvents = [];
        receivedMessageEvents = [];
        receivedTestEndEvents = [];
        receivedTestMetaEvents = [];
        receivedOpenEvents = [];
        done();
    });

    describe('4-messages-with-seed-01', function() {

        var twistedUrl = '/test/eventsource/4-messages-with-seed-01';

        describe('using polyfill eventsource', function () {

            beforeEach(function (done) {

                var eventSource = window[evsImportName];

                evs = new eventSource(twistedUrl);
                addEventListeners(evs, done);
            });

            describe ('after a complete run until testend:', function() {

                it("data in testmeta event should match all incoming messages data", function () {

                    expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);
                });

                it("testend event is received", function () {

                    expect(receivedTestEndEvents.length).toEqual(1);
                })
            });
        });

        if (isEventSourceSupported) {

            describe('using native eventsource', function () {

                beforeEach(function (done) {

                    var eventSource = window[evsName];

                    evs = new eventSource(twistedUrl);
                    addEventListeners(evs, done);
                });

                describe ('after a complete run until testend:', function() {

                    it("data in testmeta event should match all incoming messages data", function () {

                        expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);
                    });

                    it("testend event is received", function () {

                        expect(receivedTestEndEvents.length).toEqual(1);
                    })
                });
            })
        }
    });

    describe('6-messages-with-seed-02', function() {

        var twistedUrl = '/test/eventsource/6-messages-with-seed-02';

        describe('using polyfill eventsource', function () {

            beforeEach(function (done) {

                var eventSource = window[evsImportName];

                evs = new eventSource(twistedUrl);
                addEventListeners(evs, done);
            });

            describe ('after a complete run until testend:', function() {

                it("data in testmeta event should match all incoming messages data", function () {

                    expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);
                });

                it("testend event is received", function () {

                    expect(receivedTestEndEvents.length).toEqual(1);
                })
            });
        });

        if (isEventSourceSupported) {

            describe('using native eventsource', function () {

                beforeEach(function (done) {

                    var eventSource = window[evsName];

                    evs = new eventSource(twistedUrl);
                    addEventListeners(evs, done);
                });

                describe ('after a complete run until testend:', function() {

                    it("data in testmeta event should match all incoming messages data", function () {

                        expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);
                    });

                    it("testend event is received", function () {

                        expect(receivedTestEndEvents.length).toEqual(1);
                    })
                });
            })
        }
    });

    describe('8-messages-closeat-4-with-seed-03', function() {

        var twistedUrl = '/test/eventsource/8-messages-closeat-4-with-seed-03';

        describe('using polyfill eventsource', function () {

            beforeEach(function (done) {

                var eventSource = window[evsImportName];

                evs = new eventSource(twistedUrl);
                addEventListeners(evs, done);
            });

            describe ('after a complete run until testend:', function() {

                it("data in testmeta event should match all incoming messages data", function () {

                    expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);
                });

                it("testend event is received", function () {

                    expect(receivedTestEndEvents.length).toBe(1);
                });

                it("at least 1 error message are received, meaning 1 reconnection", function () {

                    expect(receivedErrorEvents.length).toBeGreaterThan(0); // we expect at least 1
                });
            });
        });

        if (isEventSourceSupported) {

            describe('using native eventsource', function () {

                beforeEach(function (done) {

                    var eventSource = window[evsName];

                    evs = new eventSource(twistedUrl);
                    addEventListeners(evs, done);
                });

                describe ('after a complete run until testend:', function() {

                    it("data in testmeta event should match all incoming messages data", function () {

                        expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);
                    });

                    it("testend event is received", function () {

                        expect(receivedTestEndEvents.length).toBe(1);
                    });

                    it("at least 1 error messages are received, meaning 1 reconnection", function () {

                        expect(receivedErrorEvents.length).toBeGreaterThan(0);
                    });
                });
            })
        }
    });

    describe('16-messages-closeat-5-with-seed-04', function() {

        var twistedUrl = '/test/eventsource/16-messages-closeat-5-with-seed-04';

        describe('using polyfill eventsource', function () {

            beforeEach(function (done) {

                var eventSource = window[evsImportName];

                evs = new eventSource(twistedUrl);
                addEventListeners(evs, done);
            });

            describe ('after a complete run until testend:', function() {

                it("data in testmeta event should match all incoming messages data", function () {

                    expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);
                });

                it("testend event is received", function () {

                    expect(receivedTestEndEvents.length).toBe(1);
                });

                it("3 error messages are received, meaning 3 reconnections", function () {

                    expect(receivedErrorEvents.length).toBeGreaterThan(1); // we expect at least 3
                });
            });
        });

        if (isEventSourceSupported) {

            describe('using native eventsource', function () {

                beforeEach(function (done) {

                    var eventSource = window[evsName];

                    evs = new eventSource(twistedUrl);
                    addEventListeners(evs, done);
                });

                describe ('after a complete run until testend:', function() {

                    it("data in testmeta event should match all incoming messages data", function () {

                        expect(receivedTestMetaEvents).toEqual(receivedMessageEvents);
                    });

                    it("testend event is received", function () {

                        expect(receivedTestEndEvents.length).toBe(1);
                    });

                    it("3 error messages are received, meaning 3 reconnections", function () {

                        expect(receivedErrorEvents.length).toBeGreaterThan(1);
                    });
                });
            })
        }
    });
});



================================================
FILE: javascript/src/browserify-eventsource.js
================================================
/*
   * CommonJS module that exports EventSource polyfill version {{VERSION}}
   * This module is intended for browser side use
   * =====================================================================
   * THIS IS A POLYFILL MODULE, SO IT HAS SIDE EFFECTS
   * IT AUTOMATICALLY CHECKS IF window OBJECT DEFINES EventSource
   * AND ADD THE EXPORTED ONE IN CASE IT IS UNDEFINED
   * =====================================================================
   * Supported by sc AmvTek srl
   * :email: devel@amvtek.com
 */


var PolyfillEventSource = require('./eventsource.js').EventSource;
module.exports = PolyfillEventSource;

// Add EventSource to window if it is missing...
if (window && !window.EventSource){
    window.EventSource = PolyfillEventSource;
    // Don't break IE < 10
    if (typeof console !== "undefined" && typeof console.log !== "undefined"){
        console.log("polyfill-eventsource added missing EventSource to window");
    }
}


================================================
FILE: javascript/src/eventsource.js
================================================
/*
   * EventSource polyfill version {{VERSION}}
   * Supported by sc AmvTek srl
   * :email: devel@amvtek.com
 */
;(function (global) {

    if (global.EventSource && !global._eventSourceImportPrefix){
        return;
    }

    var evsImportName = (global._eventSourceImportPrefix||'')+"EventSource";

    var EventSource = function (url, options) {

        if (!url || typeof url != 'string') {
            throw new SyntaxError('Not enough arguments');
        }

        this.URL = url;
        this.setOptions(options);
        var evs = this;
        setTimeout(function(){evs.poll()}, 0);
    };

    EventSource.prototype = {

        CONNECTING: 0,

        OPEN: 1,

        CLOSED: 2,

        defaultOptions: {

            loggingEnabled: false,

            loggingPrefix: "eventsource",

            interval: 500, // milliseconds

            bufferSizeLimit: 256*1024, // bytes

            silentTimeout: 300000, // milliseconds

            getArgs:{
                'evs_buffer_size_limit': 256*1024
            },

            xhrHeaders:{
                'Accept': 'text/event-stream',
                'Cache-Control': 'no-cache',
                'X-Requested-With': 'XMLHttpRequest'
            }
        },

        setOptions: function(options){

            var defaults = this.defaultOptions;
            var option;

            // set all default options...
            for (option in defaults){

                if ( defaults.hasOwnProperty(option) ){
                    this[option] = defaults[option];
                }
            }

            // override with what is in options
            for (option in options){

                if (option in defaults && options.hasOwnProperty(option)){
                    this[option] = options[option];
                }
            }

            // if getArgs option is enabled
            // ensure evs_buffer_size_limit corresponds to bufferSizeLimit
            if (this.getArgs && this.bufferSizeLimit) {

                this.getArgs['evs_buffer_size_limit'] = this.bufferSizeLimit;
            }

            // if console is not available, force loggingEnabled to false
            if (typeof console === "undefined" || typeof console.log === "undefined") {

                this.loggingEnabled = false;
            }
        },

        log: function(message) {

            if (this.loggingEnabled) {

                console.log("[" + this.loggingPrefix +"]:" + message)
            }
        },

        poll: function() {

            try {

                if (this.readyState == this.CLOSED) {
                    return;
                }

                this.cleanup();
                this.readyState = this.CONNECTING;
                this.cursor = 0;
                this.cache = '';
                this._xhr = new this.XHR(this);
                this.resetNoActivityTimer();

            }
            catch (e) {

                // in an attempt to silence the errors
                this.log('There were errors inside the pool try-catch');
                this.dispatchEvent('error', { type: 'error', data: e.message });
            }
        },

        pollAgain: function (interval) {

            // schedule poll to be called after interval milliseconds
            var evs = this;
            evs.readyState = evs.CONNECTING;
            evs.dispatchEvent('error', {
                type: 'error',
                data: "Reconnecting "
            });
            this._pollTimer = setTimeout(function(){evs.poll()}, interval||0);
        },


        cleanup: function() {

            this.log('evs cleaning up')

            if (this._pollTimer){
                clearInterval(this._pollTimer);
                this._pollTimer = null;
            }

            if (this._noActivityTimer){
                clearInterval(this._noActivityTimer);
                this._noActivityTimer = null;
            }

            if (this._xhr){
                this._xhr.abort();
                this._xhr = null;
            }
        },

        resetNoActivityTimer: function(){

            if (this.silentTimeout){

                if (this._noActivityTimer){
                    clearInterval(this._noActivityTimer);
                }
                var evs = this;
                this._noActivityTimer = setTimeout(
                        function(){ evs.log('Timeout! silentTImeout:'+evs.silentTimeout); evs.pollAgain(); },
                        this.silentTimeout
                        );
            }
        },

        close: function () {

            this.readyState = this.CLOSED;
            this.log('Closing connection. readyState: '+this.readyState);
            this.cleanup();
        },

        _onxhrdata: function() {

            var request = this._xhr;

            if (request.isReady() && !request.hasError() ) {
                // reset the timer, as we have activity
                this.resetNoActivityTimer();

                // move this EventSource to OPEN state...
                if (this.readyState == this.CONNECTING) {
                    this.readyState = this.OPEN;
                    this.dispatchEvent('open', { type: 'open' });
                }

                var buffer = request.getBuffer();

                if (buffer.length > this.bufferSizeLimit) {
                    this.log('buffer.length > this.bufferSizeLimit');
                    this.pollAgain();
                }

                if (this.cursor == 0 && buffer.length > 0){

                    // skip byte order mark \uFEFF character if it starts the stream
                    if (buffer.substring(0,1) == '\uFEFF'){
                        this.cursor = 1;
                    }
                }

                var lastMessageIndex = this.lastMessageIndex(buffer);
                if (lastMessageIndex[0] >= this.cursor){

                    var newcursor = lastMessageIndex[1];
                    var toparse = buffer.substring(this.cursor, newcursor);
                    this.parseStream(toparse);
                    this.cursor = newcursor;
                }

                // if request is finished, reopen the connection
                if (request.isDone()) {
                    this.log('request.isDone(). reopening the connection');
                    this.pollAgain(this.interval);
                }
            }
            else if (this.readyState !== this.CLOSED) {

                this.log('this.readyState !== this.CLOSED');
                this.pollAgain(this.interval);

                //MV: Unsure why an error was previously dispatched
            }
        },

        parseStream: function(chunk) {

            // normalize line separators (\r\n,\r,\n) to \n
            // remove white spaces that may precede \n
            chunk = this.cache + this.normalizeToLF(chunk);

            var events = chunk.split('\n\n');

            var i, j, eventType, datas, line, retry;

            for (i=0; i < (events.length - 1); i++) {

                eventType = 'message';
                datas = [];
                parts = events[i].split('\n');

                for (j=0; j < parts.length; j++) {

                    line = this.trimWhiteSpace(parts[j]);

                    if (line.indexOf('event') == 0) {

                        eventType = line.replace(/event:?\s*/, '');
                    }
                    else if (line.indexOf('retry') == 0) {

                        retry = parseInt(line.replace(/retry:?\s*/, ''));
                        if(!isNaN(retry)) {
                            this.interval = retry;
                        }
                    }
                    else if (line.indexOf('data') == 0) {

                        datas.push(line.replace(/data:?\s*/, ''));
                    }
                    else if (line.indexOf('id:') == 0) {

                        this.lastEventId = line.replace(/id:?\s*/, '');
                    }
                    else if (line.indexOf('id') == 0) { // this resets the id

                        this.lastEventId = null;
                    }
                }

                if (datas.length) {
                    // dispatch a new event
                    var event = new MessageEvent(eventType, datas.join('\n'), window.location.origin, this.lastEventId);
                    this.dispatchEvent(eventType, event);
                }
            }

            this.cache = events[events.length - 1];
        },

        dispatchEvent: function (type, event) {
            var handlers = this['_' + type + 'Handlers'];

            if (handlers) {

                for (var i = 0; i < handlers.length; i++) {
                    handlers[i].call(this, event);
                }
            }

            if (this['on' + type]) {
                this['on' + type].call(this, event);
            }

        },

        addEventListener: function (type, handler) {
            if (!this['_' + type + 'Handlers']) {
                this['_' + type + 'Handlers'] = [];
            }

            this['_' + type + 'Handlers'].push(handler);
        },

        removeEventListener: function (type, handler) {
            var handlers = this['_' + type + 'Handlers'];
            if (!handlers) {
                return;
            }
            for (var i = handlers.length - 1; i >= 0; --i) {
                if (handlers[i] === handler) {
                    handlers.splice(i, 1);
                    break;
                }
            }
        },

        _pollTimer: null,

        _noactivityTimer: null,

        _xhr: null,

        lastEventId: null,

        cache: '',

        cursor: 0,

        onerror: null,

        onmessage: null,

        onopen: null,

        readyState: 0,

        // ===================================================================
        // helpers functions
        // those are attached to prototype to ease reuse and testing...

        urlWithParams: function (baseURL, params) {

            var encodedArgs = [];

            if (params){

                var key, urlarg;
                var urlize = encodeURIComponent;

                for (key in params){
                    if (params.hasOwnProperty(key)) {
                        urlarg = urlize(key)+'='+urlize(params[key]);
                        encodedArgs.push(urlarg);
                    }
                }
            }

            if (encodedArgs.length > 0){

                if (baseURL.indexOf('?') == -1)
                    return baseURL + '?' + encodedArgs.join('&');
                return baseURL + '&' + encodedArgs.join('&');
            }
            return baseURL;
        },

        lastMessageIndex: function(text) {

            var ln2 =text.lastIndexOf('\n\n');
            var lr2 = text.lastIndexOf('\r\r');
            var lrln2 = text.lastIndexOf('\r\n\r\n');

            if (lrln2 > Math.max(ln2, lr2)) {
                return [lrln2, lrln2+4];
            }
            return [Math.max(ln2, lr2), Math.max(ln2, lr2) + 2]
        },

        trimWhiteSpace: function(str) {
            // to remove whitespaces left and right of string

            var reTrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
            return str.replace(reTrim, '');
        },

        normalizeToLF: function(str) {

            // replace \r and \r\n with \n
            return str.replace(/\r\n|\r/g, '\n');
        }

    };

    if (!isOldIE()){

        EventSource.isPolyfill = "XHR";

        // EventSource will send request using XMLHttpRequest
        EventSource.prototype.XHR = function(evs) {

            request = new XMLHttpRequest();
            this._request = request;
            evs._xhr = this;

            // set handlers
            request.onreadystatechange = function(){
                if (request.readyState > 1 && evs.readyState != evs.CLOSED) {
                    if (request.status == 200 || (request.status>=300 && request.status<400)){
                        evs._onxhrdata();
                    }
                    else {
                        request._failed = true;
                        evs.readyState = evs.CLOSED;
                        evs.dispatchEvent('error', {
                            type: 'error',
                            data: "The server responded with "+request.status
                        });
                        evs.close();
                    }
                }
            };

            request.onprogress = function () {
            };

            request.open('GET', evs.urlWithParams(evs.URL, evs.getArgs), true);

            var headers = evs.xhrHeaders; // maybe null
            for (var header in headers) {
                if (headers.hasOwnProperty(header)){
                    request.setRequestHeader(header, headers[header]);
                }
            }
            if (evs.lastEventId) {
                request.setRequestHeader('Last-Event-Id', evs.lastEventId);
            }

            request.send();
        };

        EventSource.prototype.XHR.prototype = {

            useXDomainRequest: false,

            _request: null,

            _failed: false, // true if we have had errors...

            isReady: function() {


                return this._request.readyState >= 2;
            },

            isDone: function() {

                return (this._request.readyState == 4);
            },

            hasError: function() {

                return (this._failed || (this._request.status >= 400));
            },

            getBuffer: function() {

                var rv = '';
                try {
                    rv = this._request.responseText || '';
                }
                catch (e){}
                return rv;
            },

            abort: function() {

                if ( this._request ) {
                    this._request.abort();
                }
            }
        };
    }
    else {

	EventSource.isPolyfill = "IE_8-9";

        // patch EventSource defaultOptions
        var defaults = EventSource.prototype.defaultOptions;
        defaults.xhrHeaders = null; // no headers will be sent
        defaults.getArgs['evs_preamble'] = 2048 + 8;

        // EventSource will send request using Internet Explorer XDomainRequest
        EventSource.prototype.XHR = function(evs) {

            request = new XDomainRequest();
            this._request = request;

            // set handlers
            request.onprogress = function(){
                request._ready = true;
                evs._onxhrdata();
            };

            request.onload = function(){
                this._loaded = true;
                evs._onxhrdata();
            };

            request.onerror = function(){
                this._failed = true;
                evs.readyState = evs.CLOSED;
                evs.dispatchEvent('error', {
                    type: 'error',
                    data: "XDomainRequest error"
                });
            };

            request.ontimeout = function(){
                this._failed = true;
                evs.readyState = evs.CLOSED;
                evs.dispatchEvent('error', {
                    type: 'error',
                    data: "XDomainRequest timed out"
                });
            };

            // XDomainRequest does not allow setting custom headers
            // If EventSource has enabled the use of GET arguments
            // we add parameters to URL so that server can adapt the stream...
            var reqGetArgs = {};
            if (evs.getArgs) {

                // copy evs.getArgs in reqGetArgs
                var defaultArgs = evs.getArgs;
                    for (var key in defaultArgs) {
                        if (defaultArgs.hasOwnProperty(key)){
                            reqGetArgs[key] = defaultArgs[key];
                        }
                    }
                if (evs.lastEventId){
                    reqGetArgs['evs_last_event_id'] = evs.lastEventId;
                }
            }
            // send the request

            request.open('GET', evs.urlWithParams(evs.URL,reqGetArgs));
            request.send();
        };

        EventSource.prototype.XHR.prototype = {

            useXDomainRequest: true,

            _request: null,

            _ready: false, // true when progress events are dispatched

            _loaded: false, // true when request has been loaded

            _failed: false, // true if when request is in error

            isReady: function() {

                return this._request._ready;
            },

            isDone: function() {

                return this._request._loaded;
            },

            hasError: function() {

                return this._request._failed;
            },

            getBuffer: function() {

                var rv = '';
                try {
                    rv = this._request.responseText || '';
                }
                catch (e){}
                return rv;
            },

            abort: function() {

                if ( this._request){
                    this._request.abort();
                }
            }
        };
    }

    function MessageEvent(type, data, origin, lastEventId) {

        this.bubbles = false;
        this.cancelBubble = false;
        this.cancelable = false;
        this.data = data || null;
        this.origin = origin || '';
        this.lastEventId = lastEventId || '';
        this.type = type || 'message';
    }

    function isOldIE () {

        //return true if we are in IE8 or IE9
        return (window.XDomainRequest && (window.XMLHttpRequest && new XMLHttpRequest().responseType === undefined)) ? true : false;
    }

    global[evsImportName] = EventSource;
})(this);


================================================
FILE: package.json
================================================
{
  "name": "eventsource-polyfill",
  "version": "0.9.7",
  "description": "A browser polyfill for W3C EventSource (http://www.w3.org/TR/eventsource/)",
  "main": "dist/browserify-eventsource.js",
  "directories": { "doc": "docs" },
  "files": [
    "dist/eventsource.js",
    "dist/browserify-eventsource.js"
  ],
  "scripts": {
    "test": "echo \"Error: to run browser tests, visit http://testevs.amvtek.com/\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/amvtek/EventSource.git"
  },
  "keywords": [
    "sse",
    "server sent events",
    "eventsource",
    "event-source",
    "polyfill"
  ],
  "author": "amvtek <devel@amvtek.com>",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/amvtek/EventSource/issues"
  },
  "homepage": "https://github.com/amvtek/EventSource",
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-contrib-uglify": "^0.6.0",
    "grunt-string-replace": "^1.0.0"
  }
}


================================================
FILE: test_server/etc/nginx/evs_tests.conf
================================================
upstream event_sources {
	
    # eventsource web server run from command line 
    server 127.0.0.1:7676;
}

server{

    listen 80; # this is for HTTP
    server_name testevs.amvtek.com;
    root /usr/local/www/testevs.amvtek.com/EventSource/javascript;
    index SpecRunner.html;

    location = /favicon.ico {

	return 404;
    }
    
    location = /robots.txt {

        alias /usr/local/www/robots/deny_all.txt;
    }	
        
    location = /test/eventsource/4-messages-with-seed-01{

	chunked_transfer_encoding off;
	proxy_http_version 1.1;
	proxy_buffering off;

	proxy_set_header Host $host;
	proxy_set_header X-Browser-Addr $remote_addr;
	proxy_set_header X-EVS-Test-Num-Message 4;


        proxy_pass http://event_sources;
        access_log /var/log/nginx/test_event_source.log time_upstream_fmt;
    }
    
    location = /test/eventsource/6-messages-with-seed-02{

	chunked_transfer_encoding off;
	proxy_http_version 1.1;
	proxy_buffering off;

	proxy_set_header Host $host;
	proxy_set_header X-Browser-Addr $remote_addr;
	proxy_set_header X-EVS-Test-Num-Message 6;


        proxy_pass http://event_sources;
        access_log /var/log/nginx/test_event_source.log time_upstream_fmt;
    }
    
    location = /test/eventsource/8-messages-closeat-4-with-seed-03{

	chunked_transfer_encoding off;
	proxy_http_version 1.1;
	proxy_buffering off;

	proxy_set_header Host $host;
	proxy_set_header X-Browser-Addr $remote_addr;
	proxy_set_header X-EVS-Test-Num-Message 8;
	proxy_set_header X-EVS-Test-CloseAt 4;


        proxy_pass http://event_sources;
        access_log /var/log/nginx/test_event_source.log time_upstream_fmt;
    }
    
    location = /test/eventsource/16-messages-closeat-5-with-seed-04{

	chunked_transfer_encoding off;
	proxy_http_version 1.1;
	proxy_buffering off;

	proxy_set_header Host $host;
	proxy_set_header X-Browser-Addr $remote_addr;
	proxy_set_header X-EVS-Test-Num-Message 16;
	proxy_set_header X-EVS-Test-CloseAt 5;


        proxy_pass http://event_sources;
        access_log /var/log/nginx/test_event_source.log time_upstream_fmt;
    }
}


================================================
FILE: test_server/etc/supervisor/evs_test_server.conf
================================================
[program:test_eventsource]
command=/usr/local/www/testevs.amvtek.com/bin/twistd 
    -n --pidfile= 
    test_eventsource
    --host=127.0.0.1
    --port=7676
user=eventsource
directory=/usr/local/www/testevs.amvtek.com/EventSource/test_server
process_name=%(program_name)s
num_procs=1


================================================
FILE: test_server/evsutils/__init__.py
================================================
# from protocol import *
# from utils import *

================================================
FILE: test_server/evsutils/log.py
================================================
# -*- coding: utf-8 -*-
"""
    shared.log
    ~~~~~~~~~~

    AmvTek attend to take control of twisted logging

    :copyright: (c) 2012 by sc AmvTek srl
    :email: devel@amvtek.com
"""

import sys

from logging import DEBUG,INFO,WARNING,ERROR,FATAL

from zope.interface import implements
from twisted.python import context, log as _log


ILogContext = _log.ILogContext
_FileLogObserver = _log.FileLogObserver  # local alias


class LeveledOnlyFileLogObserver(_FileLogObserver):
    """
    FileLogObserver that filters out non 'leveled' log events...
    This is our attend to eliminate unwanted twisted log messages...
    """

    def emit(self,eventDict):
        """skip logging if eventDict does not contain 'level' key..."""

        if eventDict.get('isError') or eventDict.has_key('level'):
            return _FileLogObserver.emit(self, eventDict)


def setLeveledLogging():
    """
    monkey patch twisted.python.log.FileLogObserver so that 'unleveled' log
    events are ignored...
    """
    _log.FileLogObserver = LeveledOnlyFileLogObserver


def buildContextAwareLogPrefix(prefix):
    """
    return logPrefix callable that :
        appends context retrieved 'system' to set prefix
    """

    def logPrefix():
        "logPrefix that adjust to current context"

        logCtx = context.get('system',"-")
        if logCtx is not "-":
            return "%s,%s"%(logCtx,prefix)
        return prefix

    return logPrefix


class LogPublisher(object):

    minLevel = DEBUG

    defaultLevel = INFO

    def _msg(self,*args,**kwargs):
        "wraps twisted.log.msg..."

        _log.msg(*args,**kwargs)

    def _err(self,_stuff=None,_why=None,**kwargs):
        "wraps twisted.log.err..."

        _log.err(_stuff,_why,**kwargs)

    def debug(self,*args,**kwargs):
        "bypass twisted log.msg in case DEBUG below minLevel..."

        if DEBUG >= self.minLevel:
            kwargs['level'] = DEBUG
            self._msg(*args,**kwargs)

    def msg(self,*args,**kwargs):
        "bypass twisted log.msg in case level below minLevel..."

        level = kwargs.setdefault('level',self.defaultLevel)
        if level >= self.minLevel:
            self._msg(*args,**kwargs)

    log = msg

    def err(self,_stuff=None,_why=None,**kwargs):
        """
        bypass twisted log.err in case level below minLevel
        uses ERROR as default for level
        """
        level = kwargs.setdefault('level',ERROR)
        if level >= self.minLevel:
            self._err(_stuff,_why,**kwargs)

    def getLogger(self,logPrefix):
        "return Logger instance"

        logger = Logger()
        logger.minLevel = self.minLevel
        logger.defaultLevel = self.defaultLevel
        if callable(logPrefix):
            logger.logPrefix = logPrefix
        else:
            logger.logPrefix = lambda :logPrefix
        return logger


class Logger(LogPublisher):
    "a Logger which inline 'level aware' debug, msg, err log methods"

    def logPrefix(self):
        return "?"

    def _msg(self,*args,**kwargs):
        "add 'system' into log 'event dict'..."

        kwargs['system'] = self.logPrefix()
        _log.msg(*args,**kwargs)

    def _err(self,_stuff=None,_why=None,**kwargs):
        "add 'system' into log 'event dict'..."

        kwargs['system'] = self.logPrefix()
        _log.err(_stuff,_why,**kwargs)


def setLevel(minLevel,defaultLevel):
    "set minimum and default levels for log publishing"

    minLevel = int(minLevel)
    defaultLevel = int(defaultLevel)

    # Initializes global publisher
    thePublisher.minLevel = minLevel
    thePublisher.defaultLevel = defaultLevel

    # Initializes Logger class, this simplify inlining Logger
    Logger.minLevel = minLevel
    Logger.defaultLevel = defaultLevel

# Install global LogPublisher
thePublisher = LogPublisher()
debug = thePublisher.debug
msg = thePublisher.msg
err = thePublisher.err
getLogger = thePublisher.getLogger

# Add globals to ease replacing twisted.python.log with this module
callWithContext = _log.callWithContext
callWithLogger = _log.callWithLogger

startConsoleLogging = lambda :_log.startLogging(sys.stdout)


================================================
FILE: test_server/evsutils/protocol.py
================================================
#  -*- coding: utf-8 -*-
import random as RND

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

from zope.interface import implements
from twisted.protocols import basic, policies
from twisted.internet import reactor, protocol, interfaces
from twisted.internet.task import deferLater, cooperate

import log as customLog

from utils import SimpleHTTPRequest, encode_http_response, TestSource, split_by


class EventSourceRequest(SimpleHTTPRequest):

    ALLOWED_METHODS = frozenset(["GET"])

    def parse(self, httpMsg):

        super(EventSourceRequest, self).parse(httpMsg)

        if self._error is not None:
            return

        self.evsArgs = {}
        
        # continue parsing to read test parameters
        try:

            errReason = "Bad Request"
            
            # local aliases
            path = self.path
            reqArgs = self.args
            reqHeaders = self.headers

            # read seed
            errReason = "Invalid seed"
            self.evsArgs['seed'] = hash(path.rsplit('/',1)[-1])

            # read message sequence length
            errReason = "Can not parse sequence length"
            seqlength = reqArgs.get('evs_num_messages', None) or \
                        reqHeaders.get('X-EVS-Test-Num-Message'.lower(), None) or None
            self.evsArgs['length'] = int(seqlength)

            # read closeAt
            errReason = "Can not parse closeAt"
            closeAt = reqArgs.get('evs_close_at', None) or\
                      reqHeaders.get('X-EVS-Test-CloseAt'.lower(), None)
            self.evsArgs['closeAt'] = int(closeAt) if closeAt else None
            
            # read sendPreamble
            self.evsArgs['sendPreamble'] = bool(reqArgs.get('evs_preamble'))

            # read Last-Event-Id
            errReason = "Invalid Last-Event-Id"
            lastEventId = reqArgs.get('evs_last_event_id', None) or\
                          reqHeaders.get('Last-Event-Id'.lower(), None) or -1
            self.evsLastId = int(lastEventId)

        except ValueError:

            self.set_error(400, errReason)

        except:

            self.set_error(500, "Server side error")


class SimpleHTTPServerProtocol(basic.LineReceiver, policies.TimeoutMixin):

    START_EVENT_STREAM = \
            "HTTP/1.1 200 OK\r\n"\
            "Content-Type: text/event-stream\r\n"\
            "Access-Control-Allow-Origin: *\r\n"\
            "Cache-Control: no-cache\r\n"\
            "Transfert-Encoding: identity\r\n"\
            "Connection: close\r\n\r\n"

    MAX_LENGTH_ERROR = encode_http_response(413, 'Request Entity Too Large')

    MAX_REQUEST_TRANSMIT_TIME_ERROR = encode_http_response(408, 'Request Timeout')

    delimiter = "\r\n\r\n"

    request = None
    
    def __init__(self, maxLength, timeout):

        self.MAX_LENGTH = maxLength
        self.MAX_REQUEST_TRANSMIT_TIME = timeout

    def lineLengthExceeded(self, line):

        self.log.msg("request too large, disconnecting")
        self.sendError(self.MAX_LENGTH_ERROR)

    def connectionMade(self):
        
        self.setTimeout(self.MAX_REQUEST_TRANSMIT_TIME)
        
        self.log = customLog.getLogger(self.transport.logstr)
        self.log.msg('connectionMade')

    def timeoutConnection(self):

        self.log.msg("request transmission takes too long, timing out")
        self.sendError(self.MAX_REQUEST_TRANSMIT_TIME_ERROR)

    def connectionLost(self, reason=None):

        self.log.msg('Connection closed because %s' % reason)
        if hasattr(self, 'producer'):
            self.producer.stopProducing()

    def lineReceived(self, line):
        """parse incoming http request, and start streaming..."""

        self.log.msg("received HTTP request, attending to parse it")
        self.resetTimeout()
        
        self.request = EventSourceRequest(line)

        if self.request._error is not None:

            self.log.msg("Got HTTP error %(status)s" % self.request._error)
            self.sendError(self.request.error)

        else:

            # send response headers
            self.startResponse()

            # register EventSource producer
            self.producer = CooperativePushProducer(self.buildEventStream())
            self.transport.registerProducer(self.producer, True)
            d = self.producer.whenDone()
            d.addCallback(lambda _: self.transport.loseConnection())

    def sendError(self, error):
        """send error response and close connection"""

        self.transport.write(error)
        self.transport.loseConnection()

    def startResponse(self):
        """send response that starts EventSource stream..."""

        self.transport.write(self.START_EVENT_STREAM)

    def buildEventStream(self):

        lastEvtId = self.request.evsLastId

        evtSource = TestSource(messages=self.factory.messages, **self.request.evsArgs)
        evtSequence = evtSource.visit_from(lastEvtId+1)

        if lastEvtId > -1:

            self.log.msg("restart streaming from : %d" % lastEvtId)

        else:

            self.log.msg("new eventsource stream...")

        restart = lambda: None

        for message in evtSequence:

            # extract start of message...
            msgstart = message[:message.find(":", 0, 8)+12]
            self.log.msg("new event line : %s..." % msgstart)

            for part in split_by(message, RND.randint(1, 3)):
                self.transport.write(part)
                yield deferLater(reactor, RND.uniform(0.05, 0.3), restart)


class CooperativePushProducer(object):

    implements(interfaces.IPushProducer)

    def __init__(self, iterator):

        self.task = cooperate(iterator)

    def getTaskState(self):

        return self.task._completionState

    def whenDone(self):

        return self.task.whenDone()

    def pauseProducing(self):

        self.task.pause()

    def resumeProducing(self):

        self.task.resume()

    def stopProducing(self):

        if self.task._completionState is None:

            self.task.stop()


class SimpleHTTPServerProtocolFactory(protocol.Factory):

    def __init__(self):

        self.MAX_LENGTH = 100000
        self.MAX_REQUEST_TRANSMIT_TIME = 20000  # seconds
        self.messages = None

    def buildProtocol(self, addr):

        proto = SimpleHTTPServerProtocol(self.MAX_LENGTH, self.MAX_REQUEST_TRANSMIT_TIME)
        proto.factory = self
        return proto

if __name__ == "__main__":

    import doctest
    doctest.testmod()


================================================
FILE: test_server/evsutils/utils.py
================================================
#  -*- coding: utf-8 -*-

from urlparse import urlparse, parse_qsl
from rfc822 import Message as MimeMessage
import random as RND
import json

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

import log as customLog


def encode_http_response(
        status, reason, version="HTTP/1.1",
        headers=None, entity=None, **kwargs):
    """return http message encoding response"""

    buf = []

    # 'dictify' headers
    headers = dict(headers or [])

    # add status line
    buf.append("%s %i %s\r\n" % (version, status, reason))

    # add entity description in headers
    if entity:
        headers["Content-Length"] = len(entity)
        headers.setdefault("Content-Type", "text/plain")

    # render headers
    for name, value in headers.items():
        buf.append("%s: %s\r\n" % (name.title(), value))

    # add empty line
    buf.append("\r\n")

    if entity:
        buf.append(entity)

    return "".join(buf)


class SimpleHTTPRequest(object):
    """Simple HTTPRequest object to help parsing HTTP message"""

    ALLOWED_METHODS = frozenset([
        "OPTIONS", "GET", "HEAD", "POST",
        "PUT", "DELETE", "TRACE", "CONNECT"])

    method = None
    path = None
    headers = {}
    version = None
    args = None

    _error = None

    def __init__(self, httpMsg):
        """
        >>> reqText = "GET /path/to/my/eventsource?arg1=1&arg2=2 HTTP/1.1\\r\\nheader: 3\\r\\n\\r\\n"
        >>> req = SimpleHTTPRequest(reqText)
        >>> req.path, req.args, req.method, req.version, req.headers
        ('/path/to/my/eventsource', {'arg1': '1', 'arg2': '2'}, 'GET', (1, 1), {'header': '3'})
        """

        self.log = customLog.getLogger('Processing HTTP request')
        try:
            self.parse(httpMsg)
        except:
            self.set_error(400, "Bad Request")

    def parse(self, httpMsg):
        """parse and validate http request out of httpMsg"""

        f = StringIO(httpMsg)

        # parse request line
        reqline = f.readline()
        parts = reqline.split()

        if len(parts) == 3:

            method, path, version = parts

        elif len(parts) == 2:

            method, path = parts
            version = "HTTP/0.9"

        else:

            return self.set_error(400, "Invalid Request line")

        # validates method
        method = method.strip().upper()
        if method not in self.ALLOWED_METHODS:
            hdrs = {"Allow": ", ".join(self.ALLOWED_METHODS)}
            return self.set_error(405, "Method Not Allowed", hdrs)
        self.method = method

        # validates path
        self.path = urlparse(path).path
        self.args = dict(parse_qsl(urlparse(path).query))

        # validates version
        version = version.strip().upper()
        if not version.startswith("HTTP/"):

            return self.set_error(400, "Invalid HTTP version")
        majmin = version[5:].split(".")
        try:
            major, minor = [int(v) for v in majmin]
        except:

            return self.set_error(400, "Invalid HTTP version")
        self.version = (major, minor)

        # parse headers
        self.headers = dict(MimeMessage(f))
        self.log.msg('\nFound headers: {}'.format(self.headers))

    def set_error(self, status, reason, headers=None, entity=None):
        """helper method allowing to define 'shortcut' response"""

        status = int(status)
        self._error = locals()

    def get_error(self):
        """return string encoding error response if any"""

        if self._error:
            return encode_http_response(**self._error)
    error = property(get_error)

    def has_error(self):
        """return True if request is not valid"""

        return bool(self._error)


class TestSource(object):

    lineSep = ['\n', '\r', '\r\n']

    messages = [u'one line. one',
                u'two lines. one\ntwo lines. two',
                u'three lines. one\nthree lines. two\nthree lines. three',
                u'four lines. one\nfour. two\nfour lines. three\nfour lines. four',
                u'spam, ham and eggs',
                u"spam, șuncă și ouă",
                u'spam\nham\neggs',
                u'Nobody expects the spanish inquisition',
                u"Personne ne s'attend à l'Inquisition espagnole",
                u'always look on the bright side of bugs',
                u"toujours regarder le côté lumineux de bugs"]

    def __init__(self, seed, length, closeAt=None,
                 sendPreamble=False, messages=None):
        """
        test if we can recreate the exact scenario with the same seed
        >>> source1 = TestSource(2014, 2)
        >>> source2 = TestSource(2014, 2)
        >>> source1.sequence == source2.sequence
        True
        """

        RND.seed(seed)
        self.sendPreamble = sendPreamble
        self.length = int(length)
        self.closeAt = closeAt
        self.messages = [unicode(mess) for mess in messages or self.messages]
        self.chosenLnSep = RND.choice(self.lineSep)

        # self.chosenEvtSep = RND.choice(self.lineSep).rjust(RND.randint(2, 10))
        self.chosenEvtSep = self.chosenLnSep  # RND.choice(self.lineSep)
        self.sequence = ["Message %02i%s" %
                         (n, RND.choice(self.messages))
                         for n in xrange(self.length)]
        self.encoder = EventSourceEncoder(self.chosenLnSep, self.chosenEvtSep)

    def visit_from(self, fromId=0):
        """
        generator function, let us iterate sequence from identifier

        helper lambda to simulate event encoding
        >>> ev = lambda mylist, sep: ["%s%s" % (el, sep) for el in mylist]

        start from fromId=0 to closeAt=1
        >>> source = TestSource(2014, 10, 1)  # closeAt is 1
        >>> source.sequence = [1, 2, 3]

        >>> actual = list(source.visit_from())
        >>> sep = source.chosenLnSep + source.chosenEvtSep
        >>> expected = ev(['event: testmeta\\ndata: [1, 2, 3]', 'data: 1\\nid: 1'], sep)
        >>> actual == expected
        True

        start from fromId=7 to closeAt=4
        >>> source = TestSource(2014, 10, 4)  # closeAt is 4
        >>> source.sequence = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

        >>> actual = list(source.visit_from(7))
        >>> sep = source.chosenLnSep + source.chosenEvtSep
        >>> expected = ev(['data: 7\\nid: 7', 'data: 8\\nid: 8'], sep)
        >>> actual == expected
        True

        start from fromId=7 to closeAt=10
        >>> source = TestSource(2014, 10)  # to the end
        >>> source.sequence = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

        >>> actual = list(source.visit_from(7))
        >>> sep = source.chosenLnSep + source.chosenEvtSep
        >>> expected = ev(['data: 7\\nid: 7', 'data: 8\\nid: 8', 'data: 9\\nid: 9', 'data: 10\\nid: 10', 'event: testend\\ndata: This is the end'], sep)
        >>> actual == expected
        True

        start from fromId=5 to closeAt=4
        >>> source = TestSource(2014, 8, 4)  # to the end
        >>> source.sequence = [1, 2, 3, 4, 5, 6, 7, 8]

        >>> actual = list(source.visit_from(5))
        >>> sep = source.chosenLnSep + source.chosenEvtSep
        >>> expected = ev(['data: 5\\nid: 5', 'data: 6\\nid: 6', 'data: 7\\nid: 7',  'data: 8\\nid: 8', 'event: testend\\ndata: This is the end'], sep)
        >>> actual == expected
        True

        tart from fromId=5 to closeAt=4
        >>> source = TestSource(2014, 16, 4)  # to the end
        >>> source.sequence = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]

        >>> actual = list(source.visit_from(5))
        >>> sep = source.chosenLnSep + source.chosenEvtSep
        >>> expected = ev(['data: 5\\nid: 5', 'data: 6\\nid: 6', 'data: 7\\nid: 7', 'data: 8\\nid: 8'], sep)
        >>> actual == expected
        True
        """

        fromId = int(fromId)
        encoder = self.encoder
        sequence = self.sequence
        closeAt = self.closeAt
        encode = encoder.encode_event
        sendPreamble = self.sendPreamble

        events = [encode(message, None, index+1) for index, message in enumerate(sequence)]
        events.insert(0, encode(json.dumps(sequence), 'testmeta', 0))
        events.append(encode("This is the end", 'testend'))

        if sendPreamble:
            yield encoder.encode_preamble()

        seqEnd = fromId - fromId % closeAt + closeAt + 1 if closeAt else len(events)+1
        seqEnd = seqEnd if seqEnd < len(events)-1 else len(events)

        for event in events[fromId:seqEnd]:
            yield event


class EventSourceEncoder(object):

    preamble = "mypreamble"

    def __init__(self, linesep='\n', eventsep='\n'):
        self.linesep = linesep
        self.eventsep = eventsep

    def encode_preamble(self, size=2056):
        """
        return preamble comment aiming at resetting IE 8 9 XDomainRequest

        >>> EventSourceEncoder().encode_preamble(15)
        ':mypreamble    \\n'
        """

        return (u"%s%s" % ((":%s" % self.preamble).ljust(size), self.linesep)).encode('utf-8')

    def encode_comment(self, comment):
        """
        return comment line

        >>> EventSourceEncoder().encode_comment('spam, ham and eggs')
        u': spam, ham and eggs\\n'
        """

        return u": %s%s" % (comment, self.linesep)

    def encode_mark(self, evtId):
        """
        return line encoding event id

        >>> EventSourceEncoder().encode_mark(2014)
        u'id: 2014\\n'
        """

        return u"id: %s%s" % (evtId, self.linesep)

    def encode_name(self, evtName):
        """
        return line encoding event name

        >>> EventSourceEncoder().encode_name('myevent')
        u'event: myevent\\n'
        """
        return u'event: %s%s' % (evtName, self.linesep)

    def encode_data(self, datas):
        ur"""
        return lines encoding event data

        >>> datas = 'first data\nsecond data\nthird data'
        >>> EventSourceEncoder().encode_data(datas)
        u'data: first data\ndata: second data\ndata: third data\n'

        >>> datas = u"toujours regarder le côté lumineux de bugs"
        >>> EventSourceEncoder().encode_data(datas)
        u'data: toujours regarder le c\xf4t\xe9 lumineux de bugs\n'
        """

        return "data: %s%s" % (("%sdata: " % self.linesep).join(unicode(datas).splitlines()), self.linesep)

    def encode_event(self, datas, evtName=None, evtId=None):
        """
        return block encoding event

        >>> datas = 'first data\\nsecond data'
        >>> EventSourceEncoder().encode_event(datas, 'myevent', 2014)
        'event: myevent\\ndata: first data\\ndata: second data\\nid: 2014\\n\\n'

        >>> datas = 'only one line of data'
        >>> EventSourceEncoder().encode_event(datas)
        'data: only one line of data\\n\\n'
        """

        return (u"%s%s%s%s" % (
            self.encode_name(evtName) if evtName else '',
            self.encode_data(datas),
            self.encode_mark(evtId) if evtId else '',
            self.eventsep
        )).encode('utf-8')


def split_by(msg, n):
    """
    return msg divided in nchunk if msg size allow so...

    >>> split_by('123456789', 3)
    ['123', '456', '789']

    >>> split_by('123456789', 2)
    ['1234', '56789']

    >>> split_by('12345', 2)
    ['12', '345']

    >>> len(split_by('123456789', 4))
    4

    Generate all randomly(msg, msg length and pieces)
    and check if the final length matches
    >>> import random,string
    >>> pieces = random.randint(1, 20)
    >>> len(split_by(''.join(random.choice(string.ascii_uppercase) for i in range(random.randint(1, 100))), pieces)) == pieces
    True
    """

    return [(msg[len(msg)*i//n:len(msg)*(i+1)//n]) for i in range(n)]


if __name__ == "__main__":

    import doctest
    doctest.testmod()


================================================
FILE: test_server/requirements.txt
================================================
Twisted==19.7.0
argparse==1.2.1
distribute==0.6.24
wsgiref==0.1.2
zope.interface==4.1.1


================================================
FILE: test_server/twisted/plugins/test_eventsource.py
================================================
#  -*- coding: utf-8 -*-

from zope.interface import implements

from twisted.application.service import IServiceMaker
from twisted.application import internet
from twisted.plugin import IPlugin
from twisted.python import usage

from evsutils.protocol import SimpleHTTPServerProtocolFactory


class Options(usage.Options):

    optParameters = [
        ['host', 'h', '0.0.0.0', "host"],
        ['port', 'p', 7676, "port"]
    ]


class TestEventsourceServiceMaker(object):

    implements(IServiceMaker, IPlugin)
    tapname = "test_eventsource"
    description = "test eventsource"
    options = Options

    def makeService(self, options):

        return internet.TCPServer(int(options["port"]), SimpleHTTPServerProtocolFactory())

serviceMaker = TestEventsourceServiceMaker()
Download .txt
gitextract_h5s0yxmq/

├── .gitignore
├── .gitmodules
├── Gruntfile.js
├── LICENSE
├── README.md
├── bower.json
├── dist/
│   ├── browserify-eventsource.js
│   └── eventsource.js
├── javascript/
│   ├── SpecConcurrentRunner.html
│   ├── SpecRunner.html
│   ├── spec/
│   │   ├── concurrentSpec.js
│   │   └── eventsourceSpec.js
│   └── src/
│       ├── browserify-eventsource.js
│       └── eventsource.js
├── package.json
└── test_server/
    ├── etc/
    │   ├── nginx/
    │   │   └── evs_tests.conf
    │   └── supervisor/
    │       └── evs_test_server.conf
    ├── evsutils/
    │   ├── __init__.py
    │   ├── log.py
    │   ├── protocol.py
    │   └── utils.py
    ├── requirements.txt
    └── twisted/
        └── plugins/
            └── test_eventsource.py
Download .txt
SYMBOL INDEX (66 symbols across 8 files)

FILE: dist/eventsource.js
  function MessageEvent (line 603) | function MessageEvent(type, data, origin, lastEventId) {
  function isOldIE (line 614) | function isOldIE () {

FILE: javascript/spec/concurrentSpec.js
  function EVS (line 40) | function EVS(srcUrl){

FILE: javascript/spec/eventsourceSpec.js
  function addEventListeners (line 562) | function addEventListeners(evs, done) {

FILE: javascript/src/eventsource.js
  function MessageEvent (line 603) | function MessageEvent(type, data, origin, lastEventId) {
  function isOldIE (line 614) | function isOldIE () {

FILE: test_server/evsutils/log.py
  class LeveledOnlyFileLogObserver (line 24) | class LeveledOnlyFileLogObserver(_FileLogObserver):
    method emit (line 30) | def emit(self,eventDict):
  function setLeveledLogging (line 37) | def setLeveledLogging():
  function buildContextAwareLogPrefix (line 45) | def buildContextAwareLogPrefix(prefix):
  class LogPublisher (line 62) | class LogPublisher(object):
    method _msg (line 68) | def _msg(self,*args,**kwargs):
    method _err (line 73) | def _err(self,_stuff=None,_why=None,**kwargs):
    method debug (line 78) | def debug(self,*args,**kwargs):
    method msg (line 85) | def msg(self,*args,**kwargs):
    method err (line 94) | def err(self,_stuff=None,_why=None,**kwargs):
    method getLogger (line 103) | def getLogger(self,logPrefix):
  class Logger (line 116) | class Logger(LogPublisher):
    method logPrefix (line 119) | def logPrefix(self):
    method _msg (line 122) | def _msg(self,*args,**kwargs):
    method _err (line 128) | def _err(self,_stuff=None,_why=None,**kwargs):
  function setLevel (line 135) | def setLevel(minLevel,defaultLevel):

FILE: test_server/evsutils/protocol.py
  class EventSourceRequest (line 19) | class EventSourceRequest(SimpleHTTPRequest):
    method parse (line 23) | def parse(self, httpMsg):
  class SimpleHTTPServerProtocol (line 76) | class SimpleHTTPServerProtocol(basic.LineReceiver, policies.TimeoutMixin):
    method __init__ (line 94) | def __init__(self, maxLength, timeout):
    method lineLengthExceeded (line 99) | def lineLengthExceeded(self, line):
    method connectionMade (line 104) | def connectionMade(self):
    method timeoutConnection (line 111) | def timeoutConnection(self):
    method connectionLost (line 116) | def connectionLost(self, reason=None):
    method lineReceived (line 122) | def lineReceived(self, line):
    method sendError (line 146) | def sendError(self, error):
    method startResponse (line 152) | def startResponse(self):
    method buildEventStream (line 157) | def buildEventStream(self):
  class CooperativePushProducer (line 185) | class CooperativePushProducer(object):
    method __init__ (line 189) | def __init__(self, iterator):
    method getTaskState (line 193) | def getTaskState(self):
    method whenDone (line 197) | def whenDone(self):
    method pauseProducing (line 201) | def pauseProducing(self):
    method resumeProducing (line 205) | def resumeProducing(self):
    method stopProducing (line 209) | def stopProducing(self):
  class SimpleHTTPServerProtocolFactory (line 216) | class SimpleHTTPServerProtocolFactory(protocol.Factory):
    method __init__ (line 218) | def __init__(self):
    method buildProtocol (line 224) | def buildProtocol(self, addr):

FILE: test_server/evsutils/utils.py
  function encode_http_response (line 16) | def encode_http_response(
  class SimpleHTTPRequest (line 47) | class SimpleHTTPRequest(object):
    method __init__ (line 62) | def __init__(self, httpMsg):
    method parse (line 76) | def parse(self, httpMsg):
    method set_error (line 126) | def set_error(self, status, reason, headers=None, entity=None):
    method get_error (line 132) | def get_error(self):
    method has_error (line 139) | def has_error(self):
  class TestSource (line 145) | class TestSource(object):
    method __init__ (line 161) | def __init__(self, seed, length, closeAt=None,
    method visit_from (line 185) | def visit_from(self, fromId=0):
  class EventSourceEncoder (line 264) | class EventSourceEncoder(object):
    method __init__ (line 268) | def __init__(self, linesep='\n', eventsep='\n'):
    method encode_preamble (line 272) | def encode_preamble(self, size=2056):
    method encode_comment (line 282) | def encode_comment(self, comment):
    method encode_mark (line 292) | def encode_mark(self, evtId):
    method encode_name (line 302) | def encode_name(self, evtName):
    method encode_data (line 311) | def encode_data(self, datas):
    method encode_event (line 326) | def encode_event(self, datas, evtName=None, evtId=None):
  function split_by (line 347) | def split_by(msg, n):

FILE: test_server/twisted/plugins/test_eventsource.py
  class Options (line 13) | class Options(usage.Options):
  class TestEventsourceServiceMaker (line 21) | class TestEventsourceServiceMaker(object):
    method makeService (line 28) | def makeService(self, options):
Condensed preview — 23 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (109K chars).
[
  {
    "path": ".gitignore",
    "chars": 663,
    "preview": "# vim swp files\n*.swp\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n\n# C extensions\n*.so\n\n# Distributi"
  },
  {
    "path": ".gitmodules",
    "chars": 82,
    "preview": "[submodule \"docs\"]\n\tpath = docs\n\turl = git@github.com:amvtek/EventSource.wiki.git\n"
  },
  {
    "path": "Gruntfile.js",
    "chars": 731,
    "preview": "module.exports = function(grunt) {\n\n    \"use strict\";\n    \n    grunt.initConfig({\n\n\tpkg: grunt.file.readJSON('package.js"
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014 AmvTek\n\nPermission is hereby granted, free of charge, to any person obtaining "
  },
  {
    "path": "README.md",
    "chars": 1969,
    "preview": "EventSource Polyfill\n====================\n\nProvide polyfill to support EventSource in browser where it is not available."
  },
  {
    "path": "bower.json",
    "chars": 615,
    "preview": "{\n  \"name\": \"eventsource-polyfill\",\n  \"homepage\": \"https://github.com/amvtek/EventSource\",\n  \"authors\": [\n    \"amvtek <d"
  },
  {
    "path": "dist/browserify-eventsource.js",
    "chars": 852,
    "preview": "/*\n   * CommonJS module that exports EventSource polyfill version 0.9.7\n   * This module is intended for browser side us"
  },
  {
    "path": "dist/eventsource.js",
    "chars": 17758,
    "preview": "/*\n   * EventSource polyfill version 0.9.7\n   * Supported by sc AmvTek srl\n   * :email: devel@amvtek.com\n */\n;(function "
  },
  {
    "path": "javascript/SpecConcurrentRunner.html",
    "chars": 1495,
    "preview": "<!DOCTYPE HTML>\n<html>\t\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n  <title>Polyfill E"
  },
  {
    "path": "javascript/SpecRunner.html",
    "chars": 1485,
    "preview": "<!DOCTYPE HTML>\n<html>\t\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n  <title>Polyfill E"
  },
  {
    "path": "javascript/spec/concurrentSpec.js",
    "chars": 4060,
    "preview": "\ndescribe('Test concurrent operation of various EventSource against real stream', function(){\n\n    var polyfillEventSour"
  },
  {
    "path": "javascript/spec/eventsourceSpec.js",
    "chars": 26903,
    "preview": "/**\n * Created by vasi on 4/8/14.\n */\n\nvar evsName = \"EventSource\",\n    evsImportName = (window._eventSourceImportPrefix"
  },
  {
    "path": "javascript/src/browserify-eventsource.js",
    "chars": 953,
    "preview": "/*\n   * CommonJS module that exports EventSource polyfill version {{VERSION}}\n   * This module is intended for browser s"
  },
  {
    "path": "javascript/src/eventsource.js",
    "chars": 17764,
    "preview": "/*\n   * EventSource polyfill version {{VERSION}}\n   * Supported by sc AmvTek srl\n   * :email: devel@amvtek.com\n */\n;(fun"
  },
  {
    "path": "package.json",
    "chars": 957,
    "preview": "{\n  \"name\": \"eventsource-polyfill\",\n  \"version\": \"0.9.7\",\n  \"description\": \"A browser polyfill for W3C EventSource (http"
  },
  {
    "path": "test_server/etc/nginx/evs_tests.conf",
    "chars": 2089,
    "preview": "upstream event_sources {\n\t\n    # eventsource web server run from command line \n    server 127.0.0.1:7676;\n}\n\nserver{\n\n  "
  },
  {
    "path": "test_server/etc/supervisor/evs_test_server.conf",
    "chars": 285,
    "preview": "[program:test_eventsource]\ncommand=/usr/local/www/testevs.amvtek.com/bin/twistd \n    -n --pidfile= \n    test_eventsource"
  },
  {
    "path": "test_server/evsutils/__init__.py",
    "chars": 46,
    "preview": "# from protocol import *\n# from utils import *"
  },
  {
    "path": "test_server/evsutils/log.py",
    "chars": 4140,
    "preview": "# -*- coding: utf-8 -*-\n\"\"\"\n    shared.log\n    ~~~~~~~~~~\n\n    AmvTek attend to take control of twisted logging\n\n    :co"
  },
  {
    "path": "test_server/evsutils/protocol.py",
    "chars": 6529,
    "preview": "#  -*- coding: utf-8 -*-\nimport random as RND\n\ntry:\n    from cStringIO import StringIO\nexcept ImportError:\n    from Stri"
  },
  {
    "path": "test_server/evsutils/utils.py",
    "chars": 11778,
    "preview": "#  -*- coding: utf-8 -*-\n\nfrom urlparse import urlparse, parse_qsl\nfrom rfc822 import Message as MimeMessage\nimport rand"
  },
  {
    "path": "test_server/requirements.txt",
    "chars": 88,
    "preview": "Twisted==19.7.0\nargparse==1.2.1\ndistribute==0.6.24\nwsgiref==0.1.2\nzope.interface==4.1.1\n"
  },
  {
    "path": "test_server/twisted/plugins/test_eventsource.py",
    "chars": 781,
    "preview": "#  -*- coding: utf-8 -*-\n\nfrom zope.interface import implements\n\nfrom twisted.application.service import IServiceMaker\nf"
  }
]

About this extraction

This page contains the full source code of the amvtek/EventSource GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 23 files (100.7 KB), approximately 23.6k tokens, and a symbol index with 66 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!