3 * Copyright 2016 RIFT.IO Inc
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
22 * Utils for use across the api_server.
23 * @module framework/core/utils
26 var fs
= require('fs');
27 var request
= require('request');
28 var Promise
= require('promise');
29 var CONSTANTS
= require('./constants.js');
30 var CONFD_PORT
= '8008';
31 var APIVersion
= '/v1';
32 var _
= require('lodash');
34 var requestWrapper = function(request
) {
35 if (process
.env
.HTTP_PROXY
&& process
.env
.HTTP_PROXY
!= '') {
36 request
= request
.defaults({
37 'proxy': process
.env
.HTTP_PROXY
43 var confdPort = function(api_server
) {
45 api_server
= api_server
.replace(api_server
.match(/[0-9](:[0-9]+)/)[1], '')
49 return api_server
+ ':' + CONFD_PORT
;
52 var projectContextUrl = function(req
, url
) {
53 //NOTE: We need to go into the sessionStore because express-session
54 // does not reliably update the session.
55 // See https://github.com/expressjs/session/issues/450
56 var projectId
= (req
.session
&&
58 req
.sessionStore
.sessions
&&
59 req
.sessionStore
.sessions
[req
.session
.id
] &&
60 JSON
.parse(req
.sessionStore
.sessions
[req
.session
.id
])['projectId']) ||
63 projectId
= encodeURIComponent(projectId
);
64 return url
.replace(/(\/api\/operational\/|\/api\/config\/)(.*)/, '$1project/' + projectId
+ '/$2');
69 var addProjectContextToRPCPayload = function(req
, url
, inputPayload
) {
70 //NOTE: We need to go into the sessionStore because express-session
71 // does not reliably update the session.
72 // See https://github.com/expressjs/session/issues/450
73 var projectId
= (req
.session
&&
75 req
.sessionStore
.sessions
&&
76 req
.sessionStore
.sessions
[req
.session
.id
] &&
77 JSON
.parse(req
.sessionStore
.sessions
[req
.session
.id
])['projectId']) ||
80 if (url
.indexOf('/api/operations/')) {
81 inputPayload
['project-name'] = projectId
;
88 var validateResponse = function(callerName
, error
, response
, body
, resolve
, reject
) {
92 console
.log('Problem with "', callerName
, '": ', error
);
95 error
: 'Problem with ' + callerName
+ ': ' + error
99 } else if (response
.statusCode
>= CONSTANTS
.HTTP_RESPONSE_CODES
.ERROR
.BAD_REQUEST
) {
100 console
.log('Problem with "', callerName
, '": ', response
.statusCode
, ':', body
);
101 res
.statusCode
= response
.statusCode
;
104 if (response
.statusCode
== CONSTANTS
.HTTP_RESPONSE_CODES
.ERROR
.UNAUTHORIZED
) {
106 error
: 'Authentication needed' + body
113 error
: 'Problem with ' + callerName
+ ': ' + response
.statusCode
+ ': ' + typeof(body
) == 'string' ? body
: JSON
.stringify(body
),
119 } else if (response
.statusCode
== CONSTANTS
.HTTP_RESPONSE_CODES
.SUCCESS
.NO_CONTENT
) {
121 statusCode
: response
.statusCode
,
131 var checkAuthorizationHeader = function(req
) {
132 return new Promise(function(resolve
, reject
) {
133 if (req
.session
&& req
.session
.authorization
== null) {
141 if (process
.env
.LOG_REQUESTS
) {
142 var logFile
= process
.env
.REQUESTS_LOG_FILE
;
144 if (logFile
&& logFile
!= '') {
145 validateResponse = function(callerName
, error
, response
, body
, resolve
, reject
) {
149 console
.log('Problem with "', callerName
, '": ', error
);
150 res
.statusCode
= 500;
152 error
: 'Problem with ' + callerName
+ ': ' + error
155 fs
.appendFileSync(logFile
, 'Request API: ' + response
.request
.uri
.href
+ ' ; ' + 'Error: ' + error
);
157 } else if (response
.statusCode
>= CONSTANTS
.HTTP_RESPONSE_CODES
.ERROR
.BAD_REQUEST
) {
158 console
.log('Problem with "', callerName
, '": ', response
.statusCode
, ':', body
);
159 res
.statusCode
= response
.statusCode
;
162 if (response
.statusCode
== CONSTANTS
.HTTP_RESPONSE_CODES
.ERROR
.UNAUTHORIZED
) {
164 error
: 'Authentication needed' + body
167 fs
.appendFileSync(logFile
, 'Request API: ' + response
.request
.uri
.href
+ ' ; ' + 'Error Body: ' + body
);
172 error
: 'Problem with ' + callerName
+ ': ' + response
.statusCode
+ ': ' + body
176 fs
.appendFileSync(logFile
, 'Request API: ' + response
.request
.uri
.href
+ ' ; ' + 'Error Body: ' + body
);
178 } else if (response
.statusCode
== CONSTANTS
.HTTP_RESPONSE_CODES
.SUCCESS
.NO_CONTENT
) {
180 fs
.appendFileSync(logFile
, 'Request API: ' + response
.request
.uri
.href
+ ' ; ' + 'Response Body: ' + body
);
183 fs
.appendFileSync(logFile
, 'Request API: ' + response
.request
.uri
.href
+ ' ; ' + 'Response Body: ' + body
);
191 * Serve the error response back back to HTTP requester
192 * @param {Object} error - object of the format
194 * statusCode - HTTP code to respond back with
195 * error - actual error JSON object to serve
197 * @param {Function} res - a handle to the express response function
199 var sendErrorResponse = function(error
, res
) {
200 if (!error
.statusCode
) {
201 console
.error('Status Code has not been set in error object: ', error
);
203 res
.status(error
.statusCode
);
208 * Serve the success response back to HTTP requester
209 * @param {Object} response - object of the format
211 * statusCode - HTTP code to respond back with
212 * data - actual data JSON object to serve
214 * @param {Function} res - a handle to the express response function
216 var sendSuccessResponse = function(response
, res
) {
217 res
.status(response
.statusCode
);
218 res
.send(response
.data
);
221 var passThroughConstructor = function(app
) {
222 app
.get('/passthrough/:type/*', function(req
, res
) {
223 var url
= req
.params
[0];
224 var type
= req
.params
.type
;
225 var api_server
= req
.query
["api_server"];
226 var uri
= confdPort(api_server
) + APIVersion
+ '/' + url
+ '?deep';
227 // Check that type is valid
236 new Promise(function(resolve
, reject
) {
238 uri
: projectContextUrl(req
, uri
),
240 headers
: _
.extend({}, CONSTANTS
.HTTP_HEADERS
.accept
[type
], {
241 'Authorization': req
.session
&& req
.session
.authorization
,
242 forever
: CONSTANTS
.FOREVER_ON
,
243 rejectUnauthorized
: false,
245 }, function(error
, response
, body
) {
246 if (validateResponse('Passthrough: ' + url
, error
, response
, body
, resolve
, reject
)) {
247 resolve(JSON
.parse(response
.body
))
250 }).then(function(data
) {
253 res
.send({'error': error
, uri
: uri
})
258 var getPortForProtocol = function(protocol
) {
267 var buildRedirectURL = function(req
, globalConfiguration
, plugin
, extra
) {
268 var api_server
= req
.query
['api_server'] || (req
.protocol
+ '://' + globalConfiguration
.get().api_server
);
269 var download_server
= req
.query
['dev_download_server'] || globalConfiguration
.get().dev_download_server
;
272 url
+= '/?api_server=' + api_server
;
273 url
+= download_server
? '&dev_download_server=' + download_server
: '';
278 var getHostNameFromURL = function(url
) {
279 var match
= url
.match(/^(https?\:)\/\/(([^:\/?#]*)(?:\:([0-9]+))?)([^?#]*)(\?[^#]*|)(#.*|)$/);
280 return match
&& match
[3];
283 var dataToJsonSansPropNameNamespace = function(s
) {
284 var a
= JSON
.parse(s
);
285 var b
= JSON
.stringify(a
);
286 var c
= b
.replace(/{"[-\w]+:/g, '{"');
287 var d
= c
.replace(/,"[-\w]+:/g, ',"');
288 var j
= JSON
.parse(d
);
294 * Ensure confd port is on api_server variable.
296 confdPort
: confdPort
,
298 validateResponse
: validateResponse
,
300 checkAuthorizationHeader
: checkAuthorizationHeader
,
302 request
: requestWrapper
.call(null, request
),
304 sendErrorResponse
: sendErrorResponse
,
306 sendSuccessResponse
: sendSuccessResponse
,
308 passThroughConstructor
: passThroughConstructor
,
310 getPortForProtocol
: getPortForProtocol
,
312 projectContextUrl
: projectContextUrl
,
314 addProjectContextToRPCPayload
: addProjectContextToRPCPayload
,
316 buildRedirectURL
: buildRedirectURL
,
318 getHostNameFromURL
: getHostNameFromURL
,
320 dataToJsonSansPropNameNamespace
: dataToJsonSansPropNameNamespace