// Prepare http request for server chain
// - fetch POST data
// - unwrap payload (extract CSRF, method, params)


'use strict';


var _ = require('lodash');
var qs = require('qs');
var http = require('http');


var MAX_POST_DATA = 10 * 1000 * 1024; // Max post data in bytes


module.exports = function (N) {

  var logger = N.logger.getLogger('rpc');


  function formatParams(params, max) {
    var length = 0
      , result = [];

    function makePair(value, key) {
      return key + '=' + JSON.stringify(value);
    }

    _(params || {}).map(makePair).sortBy('length').forEach(function (pair) {
      if (!max || max > length + pair.length) {
        length += pair.length;
        result.push(pair);
      } else {
        result.push('...');
        return false;
      }
    });

    if (0 !== result.length) {
      return '?' + result.join('&');
    } else {
      return '';
    }
  }


  function log(env) {
    var err = env.err
      , req = env.origin.req
      , message = http.STATUS_CODES[env.status]
      , level = 'info';

    if (err) {
      if (N.io.APP_ERROR <= err.code) {
        message = err.stack || err.message || JSON.stringify(err);
        level = 'fatal';
      } else if (N.io.BAD_REQUEST <= err.code && N.io.APP_ERROR > err.code) {
        message = http.STATUS_CODES[err.code];
        level = 'error';
      }
    }

    logger[level]('%s - %s%s - "%s" - %s',
                  req.connection.remoteAddress,
                  env.method,
                  formatParams(env.params, 60),
                  req.headers['user-agent'],
                  message);
  }

  N.wire.before('responder:rpc', function rpc_prepare(env, callback) {
    var req = env.origin.req
      , data = '';

    env.log_request = log;

    //
    // invalid request
    //

    if ('POST' !== req.method) {
      env.err = N.io.BAD_REQUEST;
      callback();
      return;
    }

    //
    // Set encoding, to glue data chunks as strings.
    // In other case you need to work with buffer, to avoid
    // breaking unicode characters.
    //
    // We don't expect rpc to work with big uploads, so, strings are enougth
    //

    req.setEncoding('utf8');

    //
    // start harvesting POST data
    //

    req.on('data', function (chunk) {
      if (MAX_POST_DATA < Buffer.byteLength(data += chunk)) {
        // max allowed post data reached, drop request.
        req.removeAllListeners('data');
        req.removeAllListeners('end');

        env.err = { code: N.io.BAD_REQUEST, message: 'Too big post data' };

        // Force destroy incoming connection
        req.connection.destroy();

        callback();
      }
    });

    //
    // when done (on success) process POST data and handle request
    //

    req.on('end', function () {
      var payload = _.defaults(qs.parse(data), req.query)
        , params  = payload.params || {};

      env.params = params;

      // save CSRF token if it was sent
      req.csrf = payload.csrf;

      // invalid payload
      if (!payload.version || !payload.method) {
        env.err = N.io.BAD_REQUEST;
        callback();
        return;
      }

      env.method = payload.method;

      // invalid client version.
      // client will check server version by it's own,
      // so in fact this error is not used by client
      if (payload.version !== N.runtime.version) {
        env.err = N.io.BAD_REQUEST;
        callback();
        return;
      }

      callback();
    });
  });
};

@@ -269,16 +269,48 @@
 n bytes%0A
+var MAX_PARAMS_LOG_LENGTH = 60;%0A
 %0A%0Amodule
@@ -414,13 +414,8 @@
 rams
-, max
 ) %7B%0A
@@ -425,27 +425,8 @@
  var
- length = 0%0A      ,
  res
@@ -435,16 +435,34 @@
 t = 
-%5B%5D
+''
 ;%0A%0A   
+ _.forEach(params,
  fun
@@ -467,24 +467,16 @@
 unction 
-makePair
 (value, 
@@ -494,280 +494,177 @@
   re
-turn key + '=' + JSON.stringify(value);%0A    %7D%0A%0A    _(params %7C%7C %7B%7D).map(makePair).sortBy('length').forEach(function (pair) %7B%0A      if (!max %7C%7C max %3E length + pair.length) %7B%0A        length += pair.length;%0A        result.push(pair
+sult += (0 === result.length) ? '?' : '&';%0A      result += encodeURIComponent(key) + '=' + encodeURIComponent(value
 );%0A
+%0A
       
-%7D else %7B%0A        result.push('...');
+if (MAX_PARAMS_LOG_LENGTH %3C result.length) %7B
 %0A   
@@ -703,41 +703,8 @@
 );%0A%0A
-    if (0 !== result.length) %7B%0A  
     
@@ -713,22 +713,16 @@
 turn
- '?' +
  result.
 join
@@ -721,54 +721,40 @@
 ult.
-join('&');%0A    %7D else %7B%0A      return '';%0A    %7D
+slice(0, MAX_PARAMS_LOG_LENGTH);
 %0A  %7D
@@ -1242,12 +1242,26 @@
 s - 
-%25s%25s
+%22RPC %25s%25s HTTP/%25s%22
  - %22
@@ -1394,13 +1394,44 @@
 rams
-, 60)
+),%0A                  req.httpVersion
 ,%0A  

Correct RPC logger.