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 | ;
|
18 |
|
19 | const URL = require('url');
|
20 | const http = require('http');
|
21 | const https = require('https');
|
22 | const q = require('q');
|
23 |
|
24 | /**
|
25 | * @module
|
26 | */
|
27 | module.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 | };
|