UNPKG

7.46 kBJavaScriptView Raw
1/**
2 * Copyright 2016-2018 F5 Networks, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17'use strict';
18
19const URL = require('url');
20const http = require('http');
21const https = require('https');
22const q = require('q');
23
24/**
25 * @module
26 */
27module.exports = {
28 /**
29 * Gets data from a URL.
30 *
31 * @param {String} url - URL from which to get the data.
32 * @param {Object} [options] - Optional parameters
33 * @param {Object} [options.headers] - Map of headers to add to the request. Format:
34 *
35 * {
36 * <header1_name>: <header1_value>,
37 * <header2_name>: <header2_value>
38 * }
39 *
40 * @returns {String} A promise which will be resolved with the data
41 * or rejected if an error occurs.
42 */
43 get(url, options) {
44 return this.request('GET', url, options);
45 },
46
47 /**
48 * Posts data to a URL.
49 *
50 * @param {String} url - URL from which to get the data.
51 * @param {Object} [options] - Optional parameters
52 * @param {Object} [options.headers] - Map of headers to add to the request. Format:
53 *
54 * {
55 * <header1_name>: <header1_value>,
56 * <header2_name>: <header2_value>
57 * }
58 * @param {Object} [options.body] - Body to send with request
59 *
60 * @returns {String} A promise which will be resolved with the data
61 * or rejected if an error occurs.
62 */
63 post(url, options) {
64 return this.request('POST', url, options);
65 },
66
67 /**
68 * Patches data to a URL.
69 *
70 * @param {String} url - URL from which to get the data.
71 * @param {Object} [options] - Optional parameters
72 * @param {Object} [options.headers] - Map of headers to add to the request. Format:
73 *
74 * {
75 * <header1_name>: <header1_value>,
76 * <header2_name>: <header2_value>
77 * }
78 * @param {Object} [options.body] - Body to send with request
79 *
80 * @returns {String} A promise which will be resolved with the data
81 * or rejected if an error occurs.
82 */
83 patch(url, options) {
84 return this.request('PATCH', url, options);
85 },
86
87 /**
88 * Puts data to a URL.
89 *
90 * @param {String} url - URL from which to get the data.
91 * @param {Object} [options] - Optional parameters
92 * @param {Object} [options.headers] - Map of headers to add to the request. Format:
93 *
94 * {
95 * <header1_name>: <header1_value>,
96 * <header2_name>: <header2_value>
97 * }
98 * @param {Object} [options.body] - Body to send with request
99 *
100 * @returns {String} A promise which will be resolved with the data
101 * or rejected if an error occurs.
102 */
103 put(url, options) {
104 return this.request('PUT', url, options);
105 },
106
107 /**
108 * Deletes data from a URL.
109 *
110 * @param {String} url - URL from which to get the data.
111 * @param {Object} [options] - Optional parameters
112 * @param {Object} [options.headers] - Map of headers to add to the request. Format:
113 *
114 * {
115 * <header1_name>: <header1_value>,
116 * <header2_name>: <header2_value>
117 * }
118 * @param {Object} [options.body] - Body to send with request
119 *
120 * @returns {String} A promise which will be resolved with the data
121 * or rejected if an error occurs.
122 */
123 delete(url, options) {
124 return this.request('DELETE', url, options);
125 },
126
127 request(method, url, options) {
128 const parsedUrl = URL.parse(url);
129 const deferred = q.defer();
130 const requestOptions = {};
131 let httpRequest;
132 let executor;
133
134 const headers = options ? options.headers : undefined;
135 let body = options ? options.body : undefined;
136
137 try {
138 if (parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:') {
139 executor = parsedUrl.protocol === 'http:' ? http : https;
140 requestOptions.protocol = parsedUrl.protocol;
141 requestOptions.hostname = parsedUrl.hostname;
142 requestOptions.port = parsedUrl.port;
143 requestOptions.path = parsedUrl.pathname + (parsedUrl.search ? parsedUrl.search : '');
144 requestOptions.headers = headers || {};
145 requestOptions.method = method;
146
147 if (body) {
148 if (requestOptions.headers['Content-Type'] === 'application/json') {
149 body = JSON.stringify(body);
150 }
151 requestOptions.headers['Content-Length'] = Buffer.byteLength(body);
152 }
153
154 httpRequest = executor.request(requestOptions, (response) => {
155 const statusCode = response.statusCode;
156 const contentType = response.headers['content-type'];
157 let rawData = '';
158 let data;
159
160
161 response.setEncoding('utf8');
162 response.on('data', (chunk) => {
163 rawData += chunk;
164 });
165 response.on('end', () => {
166 if (contentType && contentType.indexOf('application/json') !== -1) {
167 data = JSON.parse(rawData);
168 } else {
169 data = rawData.trim();
170 }
171 if (statusCode >= 300) {
172 /* eslint-disable max-len */
173 const message = `${url.toString()} status code ${statusCode}, status message ${response.statusMessage}, body: ${JSON.stringify(data)}`;
174 /* eslint-enable max-len */
175 deferred.reject(new Error(message));
176 } else {
177 deferred.resolve(data);
178 }
179 });
180 })
181 .on('error', (err) => {
182 deferred.reject(err);
183 });
184
185 if (body) {
186 httpRequest.write(body);
187 }
188
189 httpRequest.end();
190 } else {
191 deferred.reject(new Error('Only http, and https URLs are supported.'));
192 }
193 } catch (err) {
194 deferred.reject(err);
195 }
196
197 return deferred.promise;
198 }
199};