UNPKG

splunk-sdk

Version:

SDK for usage with the Splunk REST API

387 lines (342 loc) 13.8 kB
/*!*/ // Copyright 2012 Splunk, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. (function() { "use strict"; var Class = require('./jquery.class').Class; var logger = require('./log').Logger; var utils = require('./utils'); var CookieHandler = require('cookie'); var root = exports || this; var Http = null; var queryBuilderMap = { "5": function(message) { var query = message.query || {}; var post = message.post || {}; var outputMode = query.output_mode || post.output_mode || "json"; query.output_mode = outputMode; return query; }, "4": function(message) { return message.query || {}; }, "default": function(message) { return queryBuilderMap["5"](message); }, "none": function(message) { return message.query || {}; } }; /** * A base class for HTTP abstraction that provides the basic functionality * for performing GET, POST, DELETE, and REQUEST operations, and provides * utilities to construct uniform responses. * * Base classes should only override `makeRequest` and `parseJSON`. * * @class splunkjs.Http */ module.exports = root = Http = Class.extend({ /** * Constructor for `splunkjs.Http`. * * @constructor * @return {splunkjs.Http} A new `splunkjs.Http` instance. * * @method splunkjs.Http */ init: function() { // We perform the bindings so that every function works properly this.get = utils.bind(this, this.get); this.del = utils.bind(this, this.del); this.post = utils.bind(this, this.post); this.request = utils.bind(this, this.request); this._buildResponse = utils.bind(this, this._buildResponse); // Set our default version to "none" this._setSplunkVersion("none"); // Cookie store for cookie based authentication. this._cookieStore = {}; }, /*!*/ _setSplunkVersion: function(version) { this.version = version; }, /** * Returns all cookies formatted as a string to be put into the Cookie Header. */ _getCookieString: function() { let cookieString = ""; utils.forEach(this._cookieStore, function (cookieValue, cookieKey) { cookieString += cookieKey; cookieString += '='; cookieString += cookieValue; cookieString += '; '; }); return cookieString; }, /** * Takes a cookie header and returns an object of form { key: $cookieKey value: $cookieValue } */ _parseCookieHeader: function(cookieHeader) { // Returns an object of form { $cookieKey: $cookieValue, $optionalCookieAttributeName: $""value, ... } let parsedCookieObject = CookieHandler.parse(cookieHeader); let cookie = {}; // This gets the first key value pair into an object and just repeatedly returns thereafter utils.forEach(parsedCookieObject, function(cookieValue, cookieKey) { if(cookie.key) { return; } cookie.key = cookieKey; cookie.value = cookieValue; }); return cookie; }, /** * Performs a GET request. * * @param {String} url The URL of the GET request. * @param {Object} headers An object of headers for this request. * @param {Object} params Parameters for this request. * @param {Number} timeout A timeout period. * @param {Number} response_timeout A timeout period for aborting a request in milisecs (0 means no timeout). * * @method splunkjs.Http */ get: function(url, headers, params, timeout, response_timeout, isAsync) { let message = { method: "GET", headers: headers, timeout: timeout, response_timeout: response_timeout, query: params, }; return this.request(url, message, isAsync); }, /** * Performs a POST request. * * @param {String} url The URL of the POST request. * @param {Object} headers An object of headers for this request. * @param {Object} params Parameters for this request. * @param {Number} timeout A timeout period. * @param {Number} response_timeout A timeout period for aborting a request in milisecs (0 means no timeout). * * @method splunkjs.Http */ post: function(url, headers, params, timeout, response_timeout) { headers["Content-Type"] = "application/x-www-form-urlencoded"; let message = { method: "POST", headers: headers, timeout: timeout, response_timeout: response_timeout, post: params }; return this.request(url, message); }, /** * Performs a DELETE request. * * @param {String} url The URL of the DELETE request. * @param {Object} headers An object of headers for this request. * @param {Object} params Query parameters for this request. * @param {Number} timeout A timeout period. * @param {Number} response_timeout A timeout period for aborting a request in milisecs (0 means no timeout). * * @method splunkjs.Http */ del: function(url, headers, params, timeout, response_timeout) { let message = { method: "DELETE", headers: headers, timeout: timeout, response_timeout: response_timeout, query: params }; return this.request(url, message); }, /** * Performs a request. * * This function sets up how to handle a response from a request, but * delegates calling the request to the `makeRequest` subclass. * * @param {String} url The encoded URL of the request. * @param {Object} message An object with values for method, headers, timeout, and encoded body. * * @method splunkjs.Http * @see makeRequest */ request: function(url, message, isAsync) { var that = this; let query = utils.getWithVersion(this.version, queryBuilderMap)(message); let post = message.post || {}; let encodedUrl = url + "?" + Http.encode(query); let body = message.body ? message.body : Http.encode(post); let cookieString = that._getCookieString(); if (cookieString.length !== 0) { message.headers["Cookie"] = cookieString; } let options = { method: message.method, headers: message.headers, timeout: message.timeout, response_timeout: message.response_timeout, query: message.query, body: body }; if(isAsync) { return this.makeRequestAsync(encodedUrl, options); } else { let res = this.makeRequest(encodedUrl, options); return res.then((response) => { if(!response){ return; } // Handle cookies if 'set-cookie' header is in the response let cookieHeaders = response.response.headers['set-cookie']; if (cookieHeaders) { utils.forEach(cookieHeaders, function (cookieHeader) { let cookie = that._parseCookieHeader(cookieHeader); that._cookieStore[cookie.key] = cookie.value; }); } if (response.status < 400 && response.status !== "abort") { return response; } else { throw response; } }).catch((error) => { throw error; }); } }, /** * Encapsulates the client-specific logic for performing a request. This * function is meant to be overriden by subclasses. * * @param {String} url The encoded URL of the request. * @param {Object} message An object with values for method, headers, timeout, and encoded body. * * @method splunkjs.Http */ makeRequest: function(url, message) { throw new Error("UNDEFINED FUNCTION - OVERRIDE REQUIRED"); }, /** * Encapsulates the client-specific logic for parsing the JSON response. * * @param {String} json The JSON response to parse. * @return {Object} The parsed JSON. * * @method splunkjs.Http */ parseJson: function(json) { throw new Error("UNDEFINED FUNCTION - OVERRIDE REQUIRED"); }, /** * Generates a unified response with the given parameters. * * @param {Object} error An error object, if one exists for the request. * @param {Object} response The response object. * @param {Object} data The response data. * @return {Object} A unified response object. * * @method splunkjs.Http */ _buildResponse: function(error, response, data) { let complete_response, json = {}; let contentType = null; if (response && response.headers) { contentType = utils.trim(response.headers["content-type"] || response.headers["Content-Type"] || response.headers["Content-type"] || response.headers["contentType"]); } if (utils.startsWith(contentType, "application/json") && data) { try { json = this.parseJson(data) || {}; } catch(e) { logger.error("Error in parsing JSON:", data, e); json = data; } } else { json = data; } if (json) { logger.printMessages(json.messages); } complete_response = { response: response, status: (response ? response.statusCode : 0), data: json, error: error }; return complete_response; } }); /** * Encodes a dictionary of values into a URL-encoded format. * * @example * * // should be a=1&b=2&b=3&b=4 * encode({a: 1, b: [2,3,4]}) * * @param {Object} params The parameters to URL encode. * @return {String} The URL-encoded string. * * @function splunkjs.Http */ Http.encode = function(params) { let encodedStr = ""; // We loop over all the keys so we encode them. for (let key in params) { if (params.hasOwnProperty(key)) { // Only append the ampersand if we already have // something encoded, and the last character isn't // already an ampersand if (encodedStr && encodedStr[encodedStr.length - 1] !== "&") { encodedStr = encodedStr + "&"; } // Get the value let value = params[key]; // If it's an array, we loop over each value // and encode it in the form &key=value[i] if (value instanceof Array) { for (let i = 0; i < value.length; i++) { encodedStr = encodedStr + key + "=" + encodeURIComponent(value[i]) + "&"; } } else if (typeof value === "object") { for(let innerKey in value) { if (value.hasOwnProperty(innerKey)) { let innerValue = value[innerKey]; encodedStr = encodedStr + key + "=" + encodeURIComponent(value[innerKey]) + "&"; } } } else { // If it's not an array, we just encode it encodedStr = encodedStr + key + "=" + encodeURIComponent(value); } } } if (encodedStr[encodedStr.length - 1] === '&') { encodedStr = encodedStr.substr(0, encodedStr.length - 1); } return encodedStr; }; })();