var spdy = require('spdy'),
    http = require('http'),
    path = require('path'),
    util = require('util'),
    net = require('net'),
    url = require('url'),
    fs = require('fs');

var SPDYProxy = function(options) {
  var self = this;

  this.setAuthHandler = function(handler) {
    self._authHandler = handler;
    console.log('AuthHandler'.green, handler.friendly_name.yellow, 
                'will be used.'.green);
  }

  this.setLogHandler = function(handler) {
    self._logHandler = handler;
    console.log('Requests will be logged into file'.green, handler._filename.yellow);
  }

  function logRequest(req) {
    console.log(req.method.green + ' ' + req.url.yellow);
    for (var i in req.headers)
      console.log(' > '.grey + i.cyan + ': ' + req.headers[i]);
    console.log();
  }

  function synReply(socket, code, reason, headers, cb) {
    try {
      socket._lock(function() {
        var socket = this;
  
        this._framer.replyFrame(
          this.id, code, reason, headers,
          function (err, frame) {
            socket.connection.write(frame);
            socket._unlock();
            cb.call();
          }
        );
      });
    } catch(error) {
      cb.call();
    }
  }

  function handlePlain(req, res) {
    var path = req.headers.path || url.parse(req.url).path;
    var requestOptions = {
      host: req.headers.host.split(':')[0],
      port: req.headers.host.split(':')[1] || 80,
      path: path,
      method: req.method,
      headers: req.headers
    };

    var rreq = http.request(requestOptions, function(rres) {
      delete rres.headers['transfer-encoding'];
      rres.headers['proxy-agent'] = 'SPDY Proxy ' + options.version;

      // write out headers to handle redirects
      res.writeHead(rres.statusCode, '', rres.headers);
      rres.pipe(res);

      // Res could not write, but it could close connection
      res.pipe(rres);
    });

    rreq.on('error', function(e) {
      console.log("Client error: " + e.message);
      res.writeHead(502, 'Proxy fetch failed');
      res.end();
    });

    req.pipe(rreq);

    // Just in case if socket will be shutdown before http.request will connect
    // to the server.
    res.on('close', function() {
      rreq.abort();
    });
  }

  function handleSecure(req, socket) {
    var dest = req.url.split(':');
    var tunnel = net.createConnection(dest[1] || 443, dest[0], function() {
      synReply(socket, 200, 'Connection established',
        {
          'Connection': 'keep-alive',
          'Proxy-Agent': 'SPDY Proxy ' + options.version
        },
        function() {
          tunnel.pipe(socket);
          socket.pipe(tunnel);
        }
      );
    });

    tunnel.setNoDelay(true);

    tunnel.on('error', function(e) {
      console.log("Tunnel error: ".red + e);
      synReply(socket, 502, "Tunnel Error", {}, function() {
        socket.end();
      });
    });
  }

  function handleRequest(req, res) {
    var socket = (req.method == 'CONNECT') ? res : res.socket;
    console.log("%s:%s".yellow + " - %s - " + "stream ID: " + "%s".yellow + " - priority: " + "%s".yellow,
      socket.connection ? socket.connection.socket.remoteAddress : socket.socket.remoteAddress,
      socket.connection ? socket.connection.socket.remotePort : socket.socket.remotePort,
      req.method, res.streamID || socket.streamID,
      res.priority || socket.priority
    );

    var dispatcher = function(req, res) {
      req.method == 'CONNECT' ? handleSecure(req, res) : handlePlain(req, res);
    }

    if (options.verbose) logRequest(req);

    if(typeof self._logHandler == 'object') {
      self._logHandler.log(socket, req);
    }

    if(typeof self._authHandler == 'object') { // an AuthHandler is defined
      // perform basic proxy auth (over established SSL tunnel)
      // - http://www.chromium.org/spdy/spdy-authentication
      var header = req.headers['proxy-authorization'] || '',
          token = header.split(/\s+/).pop() || '',
          auth = new Buffer(token, 'base64').toString(),
          parts = auth.split(/:/),
          username = parts[0],
          password = parts[1];

      // don't pass proxy-auth headers upstream
      delete req.headers['proxy-authorization'];

      self._authHandler.authUser(username, password, function(authPassed) {
        if (authPassed)
          return dispatcher(req, res);

        synReply(socket, 407, 'Proxy Authentication Required',
          {'Proxy-Authenticate': 'Basic realm="SPDY Proxy"'},
          function() {
            socket.end();
          }
        );
      });
    } else { // auth is not necessary, simply go ahead and dispatch to funcs
      dispatcher(req, res);
    }

  }

  spdy.server.Server.call(this, options);

  this.on("connect", handleRequest);
  this.on("request", handleRequest);
};

util.inherits(SPDYProxy, spdy.server.Server);

var createServer = function(options) {
  return new SPDYProxy(options);
};

exports.SPDYProxy = SPDYProxy;
exports.createServer = createServer;

@@ -867,16 +867,56 @@
   try %7B%0A
+      if(socket._lock)%7B //SPDY socket%0A  
       so
@@ -931,32 +931,34 @@
 ck(function() %7B%0A
+  
         var sock
@@ -970,17 +970,16 @@
 this;%0A  
-%0A
         
@@ -1009,24 +1009,26 @@
 (%0A          
+ 
+ 
 this.id, cod
@@ -1047,16 +1047,18 @@
 eaders,%0A
+  
         
@@ -1091,24 +1091,26 @@
             
+  
 socket.conne
@@ -1133,32 +1133,34 @@
 e);%0A            
+ 
+ 
 socket._unlock()
@@ -1169,24 +1169,26 @@
             
+  
 cb.call();%0A 
@@ -1182,24 +1182,26 @@
  cb.call();%0A
+  
           %7D%0A
@@ -1200,35 +1200,39 @@
       %7D%0A        
+  
 );%0A
+  
       %7D);%0A    %7D 
@@ -1221,24 +1221,317 @@
         %7D);%0A
+      %7Delse%7B //raw SSL socket%0A        var statusLine = 'HTTP/1.1 ' + code + ' ' + reason + '%5Cr%5Cn';%0A        var headerLines='';%0A        for(key in headers)%7B%0A            headerLines+=key+': '+headers%5Bkey%5D+'%5Cr%5Cn';%0A        %7D%0A        socket.write(statusLine+headerLines+'%5Cr%5Cn','UTF-8',cb);%0A      %7D%0A
     %7D catch(

fix websocket bug, Issue #26