frankly-js
Version:
JavaScript library providing building blocks to write applications using the Frankly API.
168 lines (144 loc) • 5.21 kB
JavaScript
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Frankly Inc.
*
* 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.
*/
var EventEmitter = require('events').EventEmitter
var url = require('url')
var Packet = require('./packet.js')
var Cookie = require('./cookie.js')
var http = require('./http.js')
function HttpBackend (address, session) {
var self = this
var u = undefined
var i = undefined
var c = undefined
EventEmitter.call(this)
u = url.parse(address)
switch (u.protocol) {
case 'http:':
case 'https:':
break
default:
throw new Error('http connection cannot be established to ' + address)
}
this.closed = false
this.path = session.path
this.headers = { }
this.host = u.hostname
this.port = u.port
this.protocol = u.protocol
if (session.identityToken) {
// This authentication scheme is not supported for http requests.
throw new Error("HTTP requests don't support authentication through an identity token")
}
else if (session.xsrf) {
// If the xsrf value is set on the session then we're run from a browser
// or something simulating it, we set the prefix to the path cookies should
// have been set for so they get sent with HTTP requests.
this.path += '/a/' + session.app.id + '/u/' + session.user.id + '/s/' + session.seed
this.headers['frankly-app-xsrf'] = session.xsrf
}
else if (session.cookies) {
// If cookies are available we're run as a client that was authenticated from
// generating an identity token, all we need to do is set these cookies back
// in each HTTP request submitted.
c = Cookie.get(session.cookies, 'app-token')
if (c !== undefined) {
this.headers['cookie'] = [Cookie.format(c)]
}
} else {
// In any other case we assume that we're run from a Node.js backend that
// is providing the app key and secret so no pre-authentication was done.
// We simply set all the required headers so that HTTP requests can be
// authenticated individually.
if (session.app.key !== undefined) {
this.headers['frankly-app-key'] = session.app.key
}
if (session.app.secret !== undefined) {
this.headers['frankly-app-secret'] = session.app.secret
}
if (session.user.id !== undefined) {
this.headers['frankly-app-user-id'] = session.user.id
}
if (session.role !== undefined) {
this.headers['frankly-app-user-role'] = session.role
}
}
// Simulate the connection establishment that happens on websocket backends.
setTimeout(function () {
if (!self.closed) {
self.emit('open')
}
}, 1)
}
HttpBackend.prototype = Object.create(EventEmitter.prototype)
HttpBackend.prototype.constructor = HttpBackend
HttpBackend.prototype.send = function (packet) {
var self = this
var path = this.path + '/' + packet.path.join('/')
var method = makeMethod(packet.type)
if (this.closed) {
throw new Error('http backend was already closed')
}
// Transforms a packet request into a HTTP request, when the promise gets
// resolved or rejected we emit an event simulating an asynchronous response
// packet.
http.request({
method: method,
path: path,
headers: this.headers,
host: this.host,
port: this.port,
protocol: this.protocol,
params: packet.params,
}, packet.payload)
.then(function (res) {
self.emit('packet', new Packet(0, packet.seed, packet.id, packet.path, packet.params, res.content))
})
.catch(function (error) {
self.emit('packet', new Packet(1, packet.seed, packet.id, packet.path, packet.params, {
status: error.status,
error: error.reason,
}))
})
}
HttpBackend.prototype.close = function (code, reason) {
if (!this.closed) {
this.closed = true
this.emit('close', code, reason)
}
}
function makeMethod (type) {
switch (type) {
case 0: return 'GET'
case 1: return 'POST'
case 2: return 'PUT'
case 3: return 'DELETE'
default:
throw new TypeError('invalid packet type: ' + type)
}
}
function makePath (prefix, path) {
return prefix + '/' + path.join('/')
}
module.exports = HttpBackend