text: var Stream = require('stream').Stream,
      util = require('util'),
      driver = require('websocket-driver'),
      Headers = require('websocket-driver/lib/websocket/driver/headers'),
      API = require('./websocket/api'),
      EventTarget = require('./websocket/api/event_target'),
      Event = require('./websocket/api/event');
  
  var EventSource = function (request, response, options) {
      this.writable = true;
      options = options || {};
  
      this._stream = response.socket;
      this._ping = options.ping || this.DEFAULT_PING;
      this._retry = options.retry || this.DEFAULT_RETRY;
  
      var scheme = driver.isSecureRequest(request) ? 'https:' : 'http:';
      this.url = scheme + '//' + request.headers.host + request.url;
      this.lastEventId = request.headers['last-event-id'] || '';
      this.readyState = API.CONNECTING;
  
      var headers = new Headers(),
          self = this;
  
      if (options.headers) {
          for (var key in options.headers) headers.set(key, options.headers[key]);
      }
  
      if (!this._stream || !this._stream.writable) return;
      process.nextTick(function () {
          self._open()
      });
  
      this._stream.setTimeout(0);
      this._stream.setNoDelay(true);
  
      var handshake = 'HTTP/1.1 200 OK\r\n' +
          'Content-Type: text/event-stream\r\n' +
          'Cache-Control: no-cache, no-store\r\n' +
          'Connection: close\r\n' +
          headers.toString() +
          '\r\n' +
          'retry: ' + Math.floor(this._retry * 1000) + '\r\n\r\n';
  
      this._write(handshake);
  
      this._stream.on('drain', function () {
          self.emit('drain')
      });
  
      if (this._ping)
          this._pingTimer = setInterval(function () {
              self.ping()
          }, this._ping * 1000);
  
      ['error', 'end'].forEach(function (event) {
          self._stream.on(event, function () {
              self.close()
          });
      });
  };
  util.inherits(EventSource, Stream);
  
  EventSource.isEventSource = function (request) {
      if (request.method !== 'GET') return false;
      var accept = (request.headers.accept || '').split(/\s*,\s*/);
      return accept.indexOf('text/event-stream') >= 0;
  };
  
  var instance = {
      DEFAULT_PING: 10,
      DEFAULT_RETRY: 5,
  
      _write: function (chunk) {
          if (!this.writable) return false;
          try {
              return this._stream.write(chunk, 'utf8');
          } catch (e) {
              return false;
          }
      },
  
      _open: function () {
          if (this.readyState !== API.CONNECTING) return;
  
          this.readyState = API.OPEN;
  
          var event = new Event('open');
          event.initEvent('open', false, false);
          this.dispatchEvent(event);
      },
  
      write: function (message) {
          return this.send(message);
      },
  
      end: function (message) {
          if (message !== undefined) this.write(message);
          this.close();
      },
  
      send: function (message, options) {
          if (this.readyState > API.OPEN) return false;
  
          message = String(message).replace(/(\r\n|\r|\n)/g, '$1data: ');
          options = options || {};
  
          var frame = '';
          if (options.event) frame += 'event: ' + options.event + '\r\n';
          if (options.id)    frame += 'id: ' + options.id + '\r\n';
          frame += 'data: ' + message + '\r\n\r\n';
  
          return this._write(frame);
      },
  
      ping: function () {
          return this._write(':\r\n\r\n');
      },
  
      close: function () {
          if (this.readyState > API.OPEN) return false;
  
          this.readyState = API.CLOSED;
          this.writable = false;
          if (this._pingTimer) clearInterval(this._pingTimer);
          if (this._stream) this._stream.end();
  
          var event = new Event('close');
          event.initEvent('close', false, false);
          this.dispatchEvent(event);
  
          return true;
      }
  };
  
  for (var method in instance) EventSource.prototype[method] = instance[method];
  for (var key in EventTarget) EventSource.prototype[key] = EventTarget[key];
  
  module.exports = EventSource;
  
  