Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default body to querystring, null encoding #892

Merged
merged 5 commits into from
Mar 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 44 additions & 15 deletions spec/HTTPRequest.spec.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict';

var httpRequest = require("../src/cloud-code/httpRequest"),
bodyParser = require('body-parser'),
express = require("express");
Expand Down Expand Up @@ -158,31 +160,47 @@ describe("httpRequest", () => {
done();
})
});
it("should encode a JSON body", (done) => {

it("should encode a query string body by default", (done) => {
let options = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need 'use strict' at the top if you want to use let.

body: {"foo": "bar"},
}
let result = httpRequest.encodeBody(options);
expect(result.body).toEqual('foo=bar');
expect(result.headers['Content-Type']).toEqual('application/x-www-form-urlencoded');
done();

var result = httpRequest.encodeBody({"foo": "bar"}, {'Content-Type': 'application/json'});
expect(result).toEqual('{"foo":"bar"}');
})

it("should encode a JSON body", (done) => {
let options = {
body: {"foo": "bar"},
headers: {'Content-Type': 'application/json'}
}
let result = httpRequest.encodeBody(options);
expect(result.body).toEqual('{"foo":"bar"}');
done();

})
it("should encode a www-form body", (done) => {

var result = httpRequest.encodeBody({"foo": "bar", "bar": "baz"}, {'cOntent-tYpe': 'application/x-www-form-urlencoded'});
expect(result).toEqual("foo=bar&bar=baz");
let options = {
body: {"foo": "bar", "bar": "baz"},
headers: {'cOntent-tYpe': 'application/x-www-form-urlencoded'}
}
let result = httpRequest.encodeBody(options);
expect(result.body).toEqual("foo=bar&bar=baz");
done();
});
it("should not encode a wrong content type", (done) => {

var result = httpRequest.encodeBody({"foo": "bar", "bar": "baz"}, {'cOntent-tYpe': 'mime/jpeg'});
expect(result).toEqual({"foo": "bar", "bar": "baz"});
let options = {
body:{"foo": "bar", "bar": "baz"},
headers: {'cOntent-tYpe': 'mime/jpeg'}
}
let result = httpRequest.encodeBody(options);
expect(result.body).toEqual({"foo": "bar", "bar": "baz"});
done();
});
it("should not encode when missing content type", (done) => {
var result = httpRequest.encodeBody({"foo": "bar", "bar": "baz"}, {'X-Custom-Header': 'my-header'});
expect(result).toEqual({"foo": "bar", "bar": "baz"});
done();
});


it("should fail gracefully", (done) => {
httpRequest({
url: "http://not a good url",
Expand All @@ -196,6 +214,17 @@ describe("httpRequest", () => {
done();
}
});
});

it('should get a cat image', (done) => {
httpRequest({
url: 'http://thecatapi.com/api/images/get?format=src&type=jpg',
followRedirects: true
}).then((res) => {
expect(res.buffer).not.toBe(null);
expect(res.text).not.toBe(null);
done();
})
})

it("should params object to query string", (done) => {
Expand Down
21 changes: 21 additions & 0 deletions src/cloud-code/HTTPResponse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

export default class HTTPResponse {
constructor(response) {
this.status = response.statusCode;
this.headers = response.headers;
this.buffer = response.body;
this.cookies = response.headers["set-cookie"];
}

get text() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the lazy-loading piece here.

return this.buffer.toString('utf-8');
}
get data() {
if (!this._data) {
try {
this._data = JSON.parse(this.text);
} catch (e) {}
}
return this._data;
}
}
45 changes: 25 additions & 20 deletions src/cloud-code/httpRequest.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
var request = require("request"),
querystring = require('querystring'),
Parse = require('parse/node').Parse;
import request from 'request';
import Parse from 'parse/node';
import HTTPResponse from './HTTPResponse';
import querystring from 'querystring';

var encodeBody = function(body, headers = {}) {
var encodeBody = function({body, headers = {}}) {
if (typeof body !== 'object') {
return body;
return {body, headers};
}
var contentTypeKeys = Object.keys(headers).filter((key) => {
return key.match(/content-type/i) != null;
});

if (contentTypeKeys.length == 1) {
if (contentTypeKeys.length == 0) {
// no content type
// As per https://parse.com/docs/cloudcode/guide#cloud-code-advanced-sending-a-post-request the default encoding is supposedly x-www-form-urlencoded

body = querystring.stringify(body);
headers['Content-Type'] = 'application/x-www-form-urlencoded';
} else {
/* istanbul ignore next */
if (contentTypeKeys.length > 1) {
console.error('multiple content-type headers are set.');
}
// There maybe many, we'll just take the 1st one
var contentType = contentTypeKeys[0];
if (headers[contentType].match(/application\/json/i)) {
body = JSON.stringify(body);
} else if(headers[contentType].match(/application\/x-www-form-urlencoded/i)) {
body = Object.keys(body).map(function(key){
return `${key}=${encodeURIComponent(body[key])}`
}).join("&");
body = querystring.stringify(body);
}
}
return body;
return {body, headers};
}

module.exports = function(options) {
Expand All @@ -32,7 +42,7 @@ module.exports = function(options) {
delete options.success;
delete options.error;
delete options.uri; // not supported
options.body = encodeBody(options.body, options.headers);
options = Object.assign(options, encodeBody(options));
// set follow redirects to false by default
options.followRedirect = options.followRedirects == true;
// support params options
Expand All @@ -41,6 +51,8 @@ module.exports = function(options) {
} else if (typeof options.params === 'string') {
options.qs = querystring.parse(options.params);
}
// force the response as a buffer
options.encoding = null;

request(options, (error, response, body) => {
if (error) {
Expand All @@ -49,15 +61,8 @@ module.exports = function(options) {
}
return promise.reject(error);
}
var httpResponse = {};
httpResponse.status = response.statusCode;
httpResponse.headers = response.headers;
httpResponse.buffer = new Buffer(response.body);
httpResponse.cookies = response.headers["set-cookie"];
httpResponse.text = response.body;
try {
httpResponse.data = JSON.parse(response.body);
} catch (e) {}
let httpResponse = new HTTPResponse(response);

// Consider <200 && >= 400 as errors
if (httpResponse.status < 200 || httpResponse.status >= 400) {
if (callbacks.error) {
Expand Down