Showing posts with label node.js. Show all posts
Showing posts with label node.js. Show all posts

Tuesday 28 July 2015

OAuth1.0a node.js signature and utc timestamp

Got chance to work on OAuth1.0a creating signature with nonce and timestamp.

OAuth-1.0a.js file for correct signature creation and timestamp should be in UTC. Sharing my code.

if (typeof (module!== 'undefined' && typeof (exports!== 'undefined'{
    module.exports = OAuth;
    var CryptoJS = require("crypto-js");
}

/**
 * Constructor
 * @param {Object} opts consumer key and secret
 */
function OAuth(opts) {
    if (!(this instanceof OAuth)) {
        return new OAuth(opts);
    }

    if (!opts{
        opts = {};
    }

    if (!opts.consumer{
        throw new Error('consumer option is required');
    }

    this.consumer = opts.consumer;
    this.signature_method = opts.signature_method || 'HMAC-SHA1';
    this.nonce_length = opts.nonce_length || 32;
    this.version = opts.version || '1.0';
    this.parameter_seperator = opts.parameter_seperator || '';

    if (typeof opts.last_ampersand === 'undefined'{
        this.last_ampersand = true;
    } else {
        this.last_ampersand = opts.last_ampersand;
    }

    switch (this.signature_method{
        case 'HMAC-SHA1':
            this.hash = function (base_string, key) {
                return CryptoJS.HmacSHA1(base_string, key).toString(CryptoJS.enc.Base64);
            };
            break;

        case 'HMAC-SHA256':
            this.hash = function (base_string, key) {
                return CryptoJS.HmacSHA256(base_string, key).toString(CryptoJS.enc.Base64);
            };
            break;

        case 'PLAINTEXT':
            this.hash = function (base_string, key) {
                return key;
            };
            break;

        case 'RSA-SHA1':
            throw new Error('oauth-1.0a does not support this signature method right now. Coming Soon...');
        default:
            throw new Error('The OAuth 1.0a protocol defines three signature methods: HMAC-SHA1, RSA-SHA1, and PLAINTEXT only');
    }
}

/**
 * OAuth request authorize
 * @param  {Object} request data
 * {
 *     method,
 *     url,
 *     data
 * }
 * @param  {Object} public and secret token
 * @return {Object} OAuth Authorized data
 */
OAuth.prototype.authorize = function (request, token) {
    var oauth_data = {
        oauth_callback: 'about:blank', //    added by sourabh
        oauth_consumer_key: this.consumer.public,
        oauth_nonce: this.getNonce(),
        oauth_signature_method: this.signature_method,
        oauth_timestamp: this.getTimeStamp(),
        oauth_version: this.version
    };

    if (!token{
        token = {};
    }

    if (token.public{
        //oauth_data.oauth_token = token.public;    //    commented by sourabh
    }

    if (!request.data{
        request.data = {};
    }

    oauth_data.oauth_signature = this.getSignature(request, token.secret, oauth_data);

    return oauth_data;
};

/**
 * Create a OAuth Signature
 * @param  {Object} request data
 * @param  {Object} token_secret public and secret token
 * @param  {Object} oauth_data   OAuth data
 * @return {String} Signature
 */
OAuth.prototype.getSignature = function (request, token_secret, oauth_data) {
    return this.hash(this.getBaseString(request, oauth_data), this.getSigningKey(token_secret));
};

/**
 * Base String = Method + Base Url + ParameterString
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {String} Base String
 */
OAuth.prototype.getBaseString = function (request, oauth_data) {
    return request.method.toUpperCase() + '&' + this.percentEncode(this.getBaseUrl(request.url)) + '&' + this.percentEncode(this.getParameterString(request, oauth_data));
};

/**
 * Get data from url
 * -> merge with oauth data
 * -> percent encode key & value
 * -> sort
 *
 * @param  {Object} request data
 * @param  {Object} OAuth data
 * @return {Object} Parameter string data
 */
OAuth.prototype.getParameterString = function (request, oauth_data) {
    var base_string_data = this.sortObject(this.percentEncodeData(this.mergeObject(oauth_data, this.mergeObject(request.data, this.deParamUrl(request.url)))));

    var data_str = '';

    //base_string_data to string
    for (var key in base_string_data{
        data_str += key + '=' + base_string_data[key+ '&';
    }

    //remove the last character
    data_str = data_str.substr(0, data_str.length - 1);
    return data_str;
};

/**
 * Create a Signing Key
 * @param  {String} token_secret Secret Token
 * @return {String} Signing Key
 */
OAuth.prototype.getSigningKey = function (token_secret) {
    token_secret = token_secret || '';

    if (!this.last_ampersand && !token_secret{
        return this.percentEncode(this.consumer.secret);
    }
    // commented by Sourabh
    //return this.percentEncode(this.consumer.secret) + '&' + this.percentEncode(token_secret);
    return this.percentEncode(this.consumer.secret+ '&';
};

/**
 * Get base url
 * @param  {String} url
 * @return {String}
 */
OAuth.prototype.getBaseUrl = function (url) {
    return url.split('?')[0];
};

/**
 * Get data from String
 * @param  {String} string
 * @return {Object}
 */
OAuth.prototype.deParam = function (string) {
    var arr = decodeURIComponent(string).split('&');
    var data = {};

    for (var i = 0; i < arr.length; i++{
        var item = arr[i].split('=');
        data[item[0]] = item[1];
    }
    return data;
};

/**
 * Get data from url
 * @param  {String} url
 * @return {Object}
 */
OAuth.prototype.deParamUrl = function (url) {
    var tmp = url.split('?');

    if (tmp.length === 1)
        return {};

    return this.deParam(tmp[1]);
};

/**
 * Percent Encode
 * @param  {String} str
 * @return {String} percent encoded string
 */
OAuth.prototype.percentEncode = function (str) {
    return encodeURIComponent(str)
        .replace(/\!/g, "%21")
        .replace(/\*/g, "%2A")
        .replace(/\'/g, "%27")
        .replace(/\(/g, "%28")
        .replace(/\)/g, "%29");
};

/**
 * Percent Encode Object
 * @param  {Object} data
 * @return {Object} percent encoded data
 */
OAuth.prototype.percentEncodeData = function (data) {
    var result = {};

    for (var key in data{
        result[this.percentEncode(key)] = this.percentEncode(data[key]);
    }

    return result;
};

/**
 * Get OAuth data as Header
 * @param  {Object} oauth_data
 * @return {String} Header data key - value
 */
OAuth.prototype.toHeader = function (oauth_data) {
    oauth_data = this.sortObject(oauth_data);

    var header_value = 'OAuth ';

    for (var key in oauth_data{
        if (key.indexOf('oauth_'=== -1)
            continue;
        header_value += this.percentEncode(key+ '="' + this.percentEncode(oauth_data[key]) + '"' + this.parameter_seperator;
    }

    return {
        Authorization: header_value.substr(0, header_value.length - this.parameter_seperator.length//cut the last chars
    };
};

/**
 * Create a random word characters string with input length
 * @return {String} a random word characters string
 */
OAuth.prototype.getNonce = function () {
    var word_characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    var result = '';

    for (var i = 0; i < this.nonce_length; i++{
        result += word_characters[parseInt(Math.random() * word_characters.length, 10)];
    }

    return result;
};

/**
 * Get Current Unix TimeStamp
 * @return {Int} current unix timestamp
 */
OAuth.prototype.getTimeStamp = function () {
    //return parseInt(new Date().getTime()/1000, 10);
    // changed by sourabh
    var now = new Date;
    return utc_timestamp = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(),
        now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds(), now.getUTCMilliseconds());
};

////////////////////// HELPER FUNCTIONS //////////////////////

/**
 * Merge object
 * @param  {Object} obj1
 * @param  {Object} obj2
 * @return {Object}
 */
OAuth.prototype.mergeObject = function (obj1, obj2) {
    var merged_obj = obj1;
    for (var key in obj2{
        merged_obj[key= obj2[key];
    }
    return merged_obj;
};

/**
 * Sort object by key
 * @param  {Object} data
 * @return {Object} sorted object
 */
OAuth.prototype.sortObject = function (data) {
    var keys = Object.keys(data);
    var result = {};

    keys.sort();

    for (var i = 0; i < keys.length; i++{
        var key = keys[i];
        result[key= data[key];
    }

    return result;
};

Monday 10 November 2014

Webcam Broadcast using WebRTC and JavaScript

Webcam Broadcast using WebRTC and JavaScript


Introduction:

This document is created to describe technologies used and process flow of Video Broadcasting project.

Prerequisites:

Basic knowledge: HTML5, CSS, JQuery/JavaScript, experience in Online Chat application
Experience of Node.js would also be useful.
Installed on your development machine:
  • Google Chrome or Firefox.
  • Code editor.
  • Web cam.
  • Node.js with socket.io and express.

Technologies:

Node.JS

Node.js is a platform built on Chrome's JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices. We used Node.js for making signalling server.

Download latest node.js - http://nodejs.org/download/. Install Node.Js using windows binary

WebRTC

Its a free, open project that enables web browsers with Real-Time Communications (RTC) capabilities via simple JavaScript APIs. The WebRTC components have been optimized to best serve this purpose. 

Socket.IO

Aims to make Realtime Apps possible in every browser and mobile device.
WEBRTC Video Conference chat app requires some modules i.e. socket.io, express

INSTALLATION

1. create folder videoconf
2. open command prompt. Go to path of videoconf folder.
3. command to install socket.io – [path to nodejs folder]/npm install socket.io
4. command to install express - [path to nodejs folder]/npm install express

Note: socket.io and express modules will install by node.js in folder videoconf/node_modules.

Node.js automatically assigns Apache’s Server Host to Socket.io.js and will use as follows:

Client side: 
script src = "http://locahost/socket.io/socket.io.js" > < /script>


Server.js

Server.js file is used for Communication/Chat Server. Server.js makes socket connections, receives messages to and from clients, can run any Javascript functionality on Server side like making AJAX requests from Server side only, converting video formats using FFMPEG command.

Server.js sample:

var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
    socket.emit('news', {
        hello: 'world'
    });
    socket.on('my other event', function (data) {
        console.log(data);
    });
});


Note: any unique port can assign Multiple servers can create on one platform using multiple server.js files but need to give different ports.

5. RUN SERVER - [path to nodejs folder]/node [path to webchat folder]/server.js.
6. Server will run in command prompt
7. on Linux to run server in background, I used: nohup command

Command to run any Linux command oin background:

nohup node server.js

client.js sample:

<script src="http://localhost/socket.io/socket.io.js"></script>

<!-- socket.io.js will run on your localhost and this path is automatically set by node.js. Its node.js magic. -->

var socket = io.connect('http://localhost');
socket.on('news', function (data) {
    console.log(data);
    socket.emit('my other event', {
        my: 'data'
    });
});

navigator.getUserMedia

navigator.getUserMedia function of Mozilla and Chrome bowser us used to open webcam in the browser.


RTCPeerConnection.js

Javascript class RTCPeerConnection is the WebRTC API for video and audio calling.



HTML5

HTML5 is used at client side to get stream and give stream to Video element.

Process flow for Webcam Broadcasting:

1.    We run server.js file on server using node command[described above]. The code is in javascript. We can use jquery also we need to install jquery module in node. We assign one unique port in server.js which is used to make connection on client side.
2.    On client side A user using HTML5, JQuery and Socket.io creates socket connection on server using unique port
3.    Also A user make global object of Conference class [conference.js].
4.    On connect method, A user asked to open the webcam.
5.    Once browser gets A user’s webcam stream, stream get attach with conference attach using config.attachstream method.
6.    Conference object sends offer to other peer.
7.    When other peer B user connects to server with above procedure, he gets offer sent by A user peer connection and also receives A user’s stream
8.    B user in return sends answer to A user that he joins him. But we are not asking B user to open the webcam because he will be viewer only.
9.    Once both users get joined and B User receives A user’s webcam stream using socket.getremotestream function, and puts stream in video element.
10.  B user starts viewing A’s webcam.
11.  A’s stream stops when user call leave function of conference.
12.  If A user close his browser, his stream will stop sending.
13.  We can notify B user and other peer connections that host has closed his webcam
14.  Any number of peer connections can be made. We need to test load.

File Structure in Fleshcast for Webcam Broadcasting:

1.            Server.js - placed on root
2.            RTCPeerConnection.js and conference.js JS files placed in assets/js folder
3.            HTML5 and Javascript code to make connection and open webcam is done in golive method in sitemodels controller.

References:

Create .ICS file using PHP code

Recently worked on creating a .ics file in PHP after a very long time, code so thought to share with everybody. Please find below the comple...