I would like to port this open source Python library for Google Reminders to JavaScript:
https://github.com/jonahar/google-reminders-cli
I have ported the authorization with the help of https://developers.google.com/identity/protocols/OAuth2UserAgent
My JavaScript version: https://github.com/Jinjinov/google-reminders-js
Now I need to port the Python's oauth2client POST request:
body = {
'5': 1, # boolean field: 0 or 1. 0 doesn't work ¯\_(ツ)_/¯
'6': num_reminders, # number number of reminders to retrieve
}
HEADERS = {
'content-type': 'application/json+protobuf',
}
response, content = self.auth_http.request(
uri='https://reminders-pa.clients6.google.com/v1internalOP/reminders/list',
method='POST',
body=json.dumps(body),
headers=HEADERS,
)
My XMLHttpRequest POST request returns:
HTTP 400 - Bad Request - if I use 'application/x-www-form-urlencoded'
HTTP 401 - Unauthorized - if I use 'application/json'
My code (full code with authorization and access token is on GitHub):
function encodeObject(params) {
var query = [];
for (let key in params) {
let val = encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
query.push(val);
}
return query.join('&');
}
function list_reminders(num_reminders, access_token, callback) {
var body = {
'5': 1, // boolean field: 0 or 1. 0 doesn't work ¯\_(ツ)_/¯
'6': num_reminders, // number of reminders to retrieve
};
body['access_token'] = access_token;
//body = JSON.stringify(body);
body = encodeObject(body);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://reminders-pa.clients6.google.com/v1internalOP/reminders/list' + '?' + body);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
//xhr.open('POST', 'https://reminders-pa.clients6.google.com/v1internalOP/reminders/list');
//xhr.setRequestHeader('Content-type', 'application/json');
xhr.onreadystatechange = function (e) {
if (xhr.readyState === 4 && xhr.status === 200) {
var content_dict = JSON.parse(xhr.response);
if (!('1' in content_dict)) {
console.log('No reminders found');
}
else {
var reminders_dict_list = content_dict['1'];
var reminders = [];
for(var reminder_dict of reminders_dict_list) {
reminders.push(build_reminder(reminder_dict));
}
callback(reminders);
}
}
else if (xhr.readyState === 4 && xhr.status === 401) {
callback(null);
}
}
//xhr.send(body);
xhr.send(null);
}
I was trying to send both, the body and the access token in the same way.
The solution is to send the access token as url encoded and the body as json:
function list_reminders(num_reminders, access_token, callback) {
/*
returns a list of the last num_reminders created reminders, or
None if an error occurred
*/
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://reminders-pa.clients6.google.com/v1internalOP/reminders/list' + '?' + 'access_token=' + access_token);
xhr.setRequestHeader('Content-type', 'application/json+protobuf');
xhr.onreadystatechange = function (e) {
if (xhr.readyState === 4 && xhr.status === 200) {
var content_dict = JSON.parse(xhr.response);
if (!('1' in content_dict)) {
console.log('No reminders found');
}
else {
var reminders_dict_list = content_dict['1'];
var reminders = [];
for(var reminder_dict of reminders_dict_list) {
reminders.push(build_reminder(reminder_dict));
}
callback(reminders);
}
}
else if (xhr.readyState === 4 && xhr.status === 401) {
callback(null);
}
}
var body = {
'5': 1, // boolean field: 0 or 1. 0 doesn't work ¯\_(ツ)_/¯
'6': num_reminders, // number of reminders to retrieve
};
xhr.send(JSON.stringify(body));
}
I am using web workers for uploading file and I am sending the file in the form of formData object as got to know that the web worker doesn't have access to DOM I used formdata-polyfill in place of default FormData , it is throwing this error and I don't know how to use this polyill properly.
here is my code,
//trying to send the formdata-polyfill object to worker
require('formdata-polyfill');
let data = new FormData();
data.append('server-method', 'upload');
data.append('file', event.target.files[0]);
// let data = new FormData(event.target.files[0]);
if (this.state.headerActiveTabUid === '1')
this.props.dispatch(handleFileUpload({upload: 'assets', data}));
//worker.js
var file = [], p = true, url,token;
function upload(blobOrFile) {
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);//add url to upload
xhr.setRequestHeader('Authorization', token);
xhr.onload = function(e) {
};
xhr.send(blobOrFile);
}
function process() {
for (var j = 0; j <file.length; j++) {
var blob = file[j];
const BYTES_PER_CHUNK = 1024 * 1024;
// 1MB chunk sizes.
const SIZE = blob.size;
var start = 0;
var end = BYTES_PER_CHUNK;
while (start < SIZE) {
if ('mozSlice' in blob) {
var chunk = blob.mozSlice(start, end);
} else {
var chunk = blob.slice(start, end);
}
upload(chunk);
start = end;
end = start + BYTES_PER_CHUNK;
}
p = ( j === file.length - 1);
self.postMessage(blob.name + " Uploaded Succesfully");
}
}
self.addEventListener('message', function(e) {
url = e.data.url;
token = e.data.id;
file.push(e.data.files);
if (p) {
process();
}
});
So this how the app should work: with a Node.js script I call the coincap.io api, and I store the response of the various request in different files. Here is the script:
var request = require("request");
var fs = require("fs");
var cors = require("cors");
var parseString = require("xml2js").parseString;
var coinstore = [];
var endpoint = ["coins", "map", "front", "global"];
for (i = 0; i < endpoint.length; i++) {
request("http://coincap.io/" + endpoint[i], function(err, response, body) {
console.log("error", Error);
console.log("statusCode: ", response && response.statusCode);
//console.log("body: ", JSON.stringify(body));
var xml = body;
parseString(xml, function(err, result) {
console.log(xml);
coinstore.push(xml);
});
fs.writeFile("D:/bibblebit/response" + response.request.path.replace(/\//g, "") + ".json", coinstore,
function(err) {
if (err) {
console.log(err);
} else {
console.log("salvato!");
}
});
});
};
I then let the user make custom calls and retrieve data from those files.
Here is a working call:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
console.log("pronto!");
var alldata = [];
alldata.push(xhr.response);
var obj = JSON.parse(alldata);
console.log(obj);
document.getElementById("api-container").innerHTML += obj.altCap;
} else {
console.log("try again");
}
}
xhr.open("GET", "http://127.0.0.1:8081/responseglobal.json", true);
xhr.send(null);
This works because the file responseglobal.json has a single object.
Changing the last snippet into this one:
document.getElementById("api-container").innerHTML += obj.mktcap;
}
else {
console.log("try again");
}
}
xhr.open("GET", "http://127.0.0.1:8081/responsefront.json", true);
xhr.send(null);
returns a self explanatory error:
[Visualizza/nascondi dettagli messaggio.] SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data at line 1 column 264 of the JSON data
Every comma separating objects results in an unexpected character, which means I am not able to create one object for every object in the file or at least read a file with more than one object. What am I missing?
I may have missed the right approach. I can't find on the net a fitting explanation, since the questions I was able to find refer to manually created objects or so specific scenarios to become useless in my case to a different extent.
This is the server side script:
var request = require("request");
var fs = require("fs");
var cors = require ("cors");
var parseString = require("xml2js").parseString;
var endpoint = ["coins", "map","front", "global"];
var i = 0;
for (i=0 ; i<endpoint.length ; i++){
request("http://coincap.io/"+endpoint[i], function (err, response, body){
var coinstore = [];
console.log("error", Error);
console.log("statusCode: ", response && response.statusCode);
var xml =JSON.stringify(body);
parseString(xml, function (err, result){
console.log(xml);
coinstore.push(xml);
});
fs.writeFileSync("/response"+ response.request.path.replace(/\//g, "")+ ".json", coinstore, function(err){
if (err){
console.log(err);
}
else {
console.log("salvato!");
}
});
});
};
And this one is the client-side one:
var queries = [ "global", "coins", "map","front"];
for(i=0; i<queries.length; i++) {
pronto(i);
}
function pronto(i) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(xhr.readyState == XMLHttpRequest.DONE) {
console.log("pronto!");
var obj = JSON.parse(xhr.response);
//console.log(obj);
document.getElementById("api"+i+"-container").innerHTML+=obj;
console.log("stampato: "+ i);
} else {
console.log("bucato");
}
}
xhr.open("GET", "response"+ queries[i] +".json", true);
xhr.send(null);
}
They might not be the best solutions, but they work out.
Moving a topic from google groups to here so it can help someone who is asking.
imageshack api: http://api.imageshack.us/
the final http reqeust is returning json:
{"success":true,"process_time":325,"result":{"max_filesize":5242880,"space_limit":52428800,"space_used":0,"space_left":52428800,"passed":0,"failed":0,"total":0,"images":[]}}
which is not good, as it didn't upload :(
it should return an image object. http://api.imageshack.us/#h.ws82a1l6pp9g
as this is what the upload image section on the imageshack api says
please help :(
my extension code
var blobUrl;
var makeBlob = function () {
bigcanvas.toBlob(function (blob) {
var reader = new window.FileReader();
reader.readAsBinaryString(blob);
reader.onloadend = function () {
blobBinaryString = reader.result;
blobUrl = blobBinaryString;
Cu.reportError(blobUrl);
uploadBlob();
}
});
};
var uploadedImageUrl;
var uploadBlob = function () {
HTTP('POST', 'https://api.imageshack.us/v1/images', {
contentType: 'application/x-www-form-urlencoded',
//'album=' + urlencode('Stock History') + '&
body: 'auth_token=' + urlencode(auth_token) + 'file#=' + blobUrl,
onSuccess: function (status, responseXML, responseText, headers, statusText) {
Cu.reportError('XMLHttpRequest SUCCESS - imageshack uploadBlob\n' + statusText + '\n' + responseText);
eval('var json = ' + responseText);
uploadedImageUrl = json.direct_link;
submitBamdex();
},
onFailure: function (status, responseXML, responseText, headers, statusText) {
Cu.reportError('XMLHttpRequest FAILLLLLLLL - imageshack uploadBlob\n' + statusText + '\n' + responseText);
}
});
};
makeBlob(); //callllll the func
What worked for me was reading about
the differences between URI's, files, blobs and Base64's in this article: https://yaz.in/p/blobs-files-and-data-uris/ .
fetching a new blob: https://masteringjs.io/tutorials/axios/form-data
and much more closed tabs along the way
so in my react component onChange handler I use new FileReader to read event.target.files[0], readAsDataURL(file), and set the Base64 encoded string to state.
I conditionally render an img src={base64stringfromState} to offer confirmation of correct image, then onSubmit, I convert this "Data URI" (the Base64 string), to a blob with one of these two codes I found somewhere (didn't end up using the first one but this is useful af and took forever to find):
const dataURItoBlob = (dataURI) => {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0)
byteString = atob(dataURI.split(',')[1]);
else
byteString = unescape(dataURI.split(',')[1]);
// separate out the mime component
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
// write the bytes of the string to a typed array
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
And
const blob = await fetch(base64imageString).then(res => res.blob())
Instead of all that shit we can just do this fetch a new version of the image or whatever and blob it right there, in the middle of constructing our photo upload/request :
event.preventDefault()
const blob = await fetch(base64stringfromState).then(res => res.blob())
const formData = new FormData()
formData.append('file#', blob)
formData.append('api_key', 'XXXXX')
formData.append('auth_token', 'XXXXXXXXXX')
formData.append('album', 'youralbumname')
const res = await axios.post('https://api.imageshack.com/v2/images', formData, {headers{'Content-Type':'multipart/form-data'}})
then all we have to do to store the uploaded image is to append https:// to and record the returned direct link for storage alongside its id so you can delete it if you need to later. Per the code earlier they spit out at
res.data.result.images[0].direct_link
res.data.result.images[0].id
This was a bitch to solve so hopefully this helps someone else with uploading photos to imageshack api cuz it's potentially a great value considering the limits of the competitors.
this code uploads a drawing on a canvas to imageshack
Can copy paste but have to update some things:
update username
update password
uploads drawing from canvas with id "bigcanvas"
update your API key
...
//this code uploads a drawing on a canvas to imageshack
var auth_token;
var loginImageshack = function() {
HTTP('POST','https://api.imageshack.us/v1/user/login',{
contentType: 'application/x-www-form-urlencoded',
body: 'user=USERNAME_TO_IMAGESHACK_HERE&password=' + urlencode('PASSWORD_TO_USERNAME_FOR_IMAGESHACK_HERE'),
onSuccess: function(status, responseXML, responseText, headers, statusText) {
Cu.reportError('XMLHttpRequest SUCCESS - imageshack login\n' + statusText + '\n' + responseText);
eval('var json = ' + responseText);
auth_token = json.result.auth_token;
makeImageshackFile();
},
onFailure: function(status, responseXML, responseText, headers, statusText) {
Cu.reportError('XMLHttpRequest FAILLLLLLLL - imageshack login\n' + statusText + '\n' + responseText);
}
});
};
var uploadedImageUrl;
var makeImageshackFile = function() {
var fd = new window.FormData();
fd.append("api_key", 'A835WS6Bww584g3568efa2z9823uua5ceh0h6325'); //USE YOUR API KEY HERE
fd.append("auth_token", auth_token);
fd.append('album', 'Stock History');
fd.append('title', 'THE-title-you-want-showing-on-imageshack')
fd.append("file#", bigcanvas.mozGetAsFile("foo.png")); //bigcanvas is a canvas with the image drawn on it: var bigcanvas = document.querySelector('#bigcanvas');
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
switch (xhr.readyState) {
case 4:
if (xhr.status==0 || (xhr.status>=200 && xhr.status<300)) {
Cu.reportError('XHR SUCCESS - \n' + xhr.responseText);
eval('var json = ' + xhr.responseText);
//ensure it passed else redo it I didnt program in the redo thing yet
//succesful json == {"success":true,"process_time":1274,"result":{"max_filesize":5242880,"space_limit":52428800,"space_used":270802,"space_left":52157998,"passed":1,"failed":0,"total":1,"images":[{"id":1067955963,"server":703,"bucket":2397,"lp_hash":"jj9g5p","filename":"9g5.png","original_filename":"foo.png","direct_link":"imageshack.us\/a\/img703\/2397\/9g5.png","title":"082813 200AM PST","description":null,"tags":[""],"likes":0,"liked":false,"views":0,"comments_count":0,"comments_disabled":false,"filter":0,"filesize":1029,"creation_date":1377681549,"width":760,"height":1110,"public":true,"is_owner":true,"owner":{"username":"bamdex","avatar":{"server":0,"filename":null}},"next_images":[],"prev_images":[{"server":59,"filename":"06mm.png"},{"server":706,"filename":"a1fg.png"}],"related_images":[{"server":59,"filename":"06mm.png"},{"server":41,"filename":"xn9q.png"},{"server":22,"filename":"t20a.png"},{"server":547,"filename":"fipx.png"},{"server":10,"filename":"dg6b.png"},{"se
uploadedImageUrl = json.result.images[0].direct_link;
Cu.reportError('succesfully uploaded image');
} else {
Cu.reportError('XHR FAIL - \n' + xhr.responseText);
}
break;
default:
//blah
}
}
xhr.open("POST", "https://api.imageshack.us/v1/images");
xhr.send(fd);
}
loginImageshack();
important note for code above
should use JSON.parse instead of eval if you want to submit the addon to AMO
should also probably change from using window to Services.appShel.hiddenDOMWindow so like new window.FormData(); would become new Services.appShel.hiddenDOMWindow.FormData(); OR var formData = Components.classes["#mozilla.org/files/formdata;1"].createInstance(Components.interfaces.nsIDOMFormData); OR Cu.import('resource://gre/modules/FormData.jsm')
helper functions required for the code above:
const {classes: Cc, interfaces: Ci, utils: Cu, Components: components} = Components
Cu.import('resource://gre/modules/Services.jsm');
...
function urlencode(str) {
return escape(str).replace(/\+/g,'%2B').replace(/%20/g, '+').replace(/\*/g, '%2A').replace(/\//g, '%2F').replace(/#/g, '%40');
};
...
//http request
const XMLHttpRequest = Cc["#mozilla.org/xmlextras/xmlhttprequest;1"];
/**
* The following keys can be sent:
* onSuccess (required) a function called when the response is 2xx
* onFailure a function called when the response is not 2xx
* username The username for basic auth
* password The password for basic auth
* overrideMimeType The mime type to use for non-XML response mime types
* timeout A timeout value in milliseconds for the response
* onTimeout A function to call if the request times out.
* body A string containing the entity body of the request
* contentType The content type of the entity body of the request
* headers A hash of optional headers
*/
function HTTP(method,url,options)
{
var requester = new XMLHttpRequest();
var timeout = null;
if (!options.synchronizedRequest) {
requester.onreadystatechange = function() {
switch (requester.readyState) {
case 0:
if (options.onUnsent) {
options.onUnsent(requester);
}
break;
case 1:
if (options.onOpened) {
options.onOpened(requester);
}
break;
case 2:
if (options.onHeaders) {
options.onHeaders(requester);
}
break;
case 3:
if (options.onLoading) {
options.onLoading(requester);
}
break;
case 4:
if (timeout) {
clearTimeout(timeout);
}
if (requester.status==0 || (requester.status>=200 && requester.status<300)) {
options.onSuccess(
requester.status,
requester.responseXML,
requester.responseText,
options.returnHeaders ? _HTTP_parseHeaders(requester.getAllResponseHeaders()) : null,
requester.statusText
);
} else {
if (options.onFailure) {
options.onFailure(
requester.status,
requester.responseXML,
requester.responseText,
options.returnHeaders ? _HTTP_parseHeaders(requester.getAllResponseHeaders()) : null,
requester.statusText
);
}
}
break;
}
}
}
if (options.overrideMimeType) {
requester.overrideMimeType(options.overrideMimeType);
}
if (options.username) {
requester.open(method,url,!options.synchronizedRequest,options.username,options.password);
} else {
requester.open(method,url,!options.synchronizedRequest);
}
if (options.timeout && !options.synchronizedRequest) {
timeout = setTimeout(
function() {
var callback = options.onTimeout ? options.onTimeout : options.onFailure;
callback(0,"Operation timeout.");
},
options.timeout
);
}
if (options.headers) {
for (var name in options.headers) {
requester.setRequestHeader(name,options.headers[name]);
}
}
if (options.sendAsBinary) {
Cu.reportError('sending as binary');
requester.sendAsBinary(options.body);
} else if (options.body) {
requester.setRequestHeader("Content-Type",options.contentType);
requester.send(options.body);
} else {
requester.send(null);
}
if (options.synchronizedRequest) {
if (requester.status==0 || (requester.status>=200 && requester.status<300)) {
options.onSuccess(
requester.status,
requester.responseXML,
requester.responseText,
options.returnHeaders ? _HTTP_parseHeaders(requester.getAllResponseHeaders()) : null,
requester.statusText
);
} else {
if (options.onFailure) {
options.onFailure(
requester.status,
requester.responseXML,
requester.responseText,
options.returnHeaders ? _HTTP_parseHeaders(requester.getAllResponseHeaders()) : null,
requester.statusText
);
}
}
return {
abort: function() {
}
};
} else {
return {
abort: function() {
clearTimeout(timeout);
requester.abort();
}
};
}
}
function _HTTP_parseHeaders(headerText)
{
var headers = {};
if (headerText) {
var eol = headerText.indexOf("\n");
while (eol>=0) {
var line = headerText.substring(0,eol);
headerText = headerText.substring(eol+1);
while (headerText.length>0 && !headerText.match(_HTTP_HEADER_NAME)) {
eol = headerText.indexOf("\n");
var nextLine = eol<0 ? headerText : headerText.substring(0,eol);
line = line+' '+nextLine;
headerText = eol<0 ? "" : headerText.substring(eol+1);
}
// Parse the name value pair
var colon = line.indexOf(':');
var name = line.substring(0,colon);
var value = line.substring(colon+1);
headers[name] = value;
eol = headerText.indexOf("\n");
}
if (headerText.length>0) {
var colon = headerText.indexOf(':');
var name = headerText.substring(0,colon);
var value = headerText.substring(colon+1);
headers[name] = value;
}
}
return headers;
}
I am trying to retrieve the data of an image in Javascript using XMLHttpRequest.
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://www.celticfc.net/images/doc/celticcrest.png");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var resp = xhr.responseText;
console.log(resp.charCodeAt(0) & 0xff);
}
};
xhr.send();
The first byte of this data should be 0x89, however any high-value bytes return as 0xfffd (0xfffd & 0xff being 0xfd).
Questions such as this one offer solutions using the overrideMimeType() function, however this is not supported on the platform I am using (Qt/QML).
How can I download the data correctly?
Google I/O 2011: HTML5 Showcase for Web Developers: The Wow and the How
Fetch binary file: new hotness
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.celticfc.net/images/doc/celticcrest.png', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if (this.status == 200) {
var uInt8Array = new Uint8Array(this.response); // Note:not xhr.responseText
for (var i = 0, len = uInt8Array.length; i < len; ++i) {
uInt8Array[i] = this.response[i];
}
var byte3 = uInt8Array[4]; // byte at offset 4
}
}
xhr.send();
I'm not familiarized with Qt but i found this in their documentation
string Qt::btoa ( data )
Binary to ASCII - this function returns a base64 encoding of data.
So, if your image is a png you can try:
resp = "data:image/png;base64," + btoa(resp);
document.write("<img src=\""+resp+"\">");
Qt version 5.13.1(support native Promise in qml env), prue qml.
function fetch, like node-fetch
function hexdump, dump the data as hex, just use the linux command hexdump to check.
function createResponse(xhr) {
let res = {};
function headersParser() {
let headersRaw = {};
let lowerCaseHeaders = {};
let rawHeaderArray = xhr.getAllResponseHeaders().split("\n");
for(let i in rawHeaderArray) {
let rawHeader = rawHeaderArray[i];
let headerItem = rawHeader.split(":");
let name = headerItem[0].trim();
let value = headerItem[1].trim();
let lowerName = name.toLowerCase();
headersRaw[name] = value;
lowerCaseHeaders [lowerName] = value;
}
return {
"headersRaw": headersRaw,
"lowerCaseHeaders": lowerCaseHeaders
};
}
res.headers = {
__alreayParse : false,
raw: function() {
if (!res.headers.__alreayParse) {
let {headersRaw, lowerCaseHeaders} = headersParser();
res.headers.__alreayParse = true;
res.headers.__headersRaw = headersRaw;
res.headers.__lowerCaseHeaders = lowerCaseHeaders;
}
return res.headers.__headersRaw;
},
get: function(headerName) {
if (!res.headers.__alreayParse) {
let {headersRaw, lowerCaseHeaders} = headersParser();
res.headers.__alreayParse = true;
res.headers.__headersRaw = headersRaw;
res.headers.__lowerCaseHeaders = lowerCaseHeaders;
}
return res.headers.__lowerCaseHeaders[headerName.toLowerCase()];
}
};
res.json = function() {
if(res.__json) {
return res.__json;
}
return res.__json = JSON.parse(xhr.responseText);
}
res.text = function() {
if (res.__text) {
return res.__text;
}
return res.__text = xhr.responseText;
}
res.arrayBuffer = function() {
if (res.__arrayBuffer) {
return res.__arrayBuffer;
}
return res.__arrayBuffer = new Uint8Array(xhr.response);
}
res.ok = (xhr.status >= 200 && xhr.status < 300);
res.status = xhr.status;
res.statusText = xhr.statusText;
return res;
}
function fetch(url, options) {
return new Promise(function(resolve, reject) {
let requestUrl = "";
let method = "";
let headers = {};
let body;
let timeout;
if (typeof url === 'string') {
requestUrl = url;
method = "GET";
if (options) {
requestUrl = options['url'];
method = options['method'];
headers = options['headers'];
body = options['body'];
timeout = options['timeout'];
}
} else {
let optionsObj = url;
requestUrl = optionsObj['url'];
method = optionsObj['method'];
headers = optionsObj['headers'];
body = optionsObj['body'];
timeout = optionsObj['timeout'];
}
let xhr = new XMLHttpRequest;
if (timeout) {
xhr.timeout = timeout;
}
// must set responseType to arraybuffer, then the xhr.response type will be ArrayBuffer
// but responseType not effect the responseText
xhr.responseType = 'arraybuffer';
xhr.onreadystatechange = function() {
// readyState as follow: UNSENT, OPENED, HEADERS_RECEIVED, LOADING, DONE
if(xhr.readyState === XMLHttpRequest.DONE) {
resolve(createResponse(xhr));
}
};
xhr.open(method, requestUrl);
// todo check headers
for(var iter in headers) {
xhr.setRequestHeader(iter, headers[iter]);
}
if("GET" === method || "HEAD" === method) {
xhr.send();
} else {
xhr.send(body);
}
});
}
function hexdump(uint8array) {
let count = 0;
let line = "";
let lineCount = 0;
let content = "";
for(let i=0; i<uint8array.byteLength; i++) {
let c = uint8array[i];
let hex = c.toString(16).padStart (2, "0");
line += hex + " ";
count++;
if (count === 16) {
let lineCountHex = (lineCount).toString (16).padStart (7, "0") + "0";
content += lineCountHex + " " + line + "\n";
line = "";
count = 0;
lineCount++;
}
}
if(line) {
let lineCountHex = (lineCount).toString (16).padStart (7, "0") + "0";
content += lineCountHex + " " + line + "\n";
line = "";
// count = 0;
lineCount++;
}
content+= (lineCount).toString (16).padStart (7, "0") + count.toString(16) +"\n";
return content;
}
fetch("https://avatars2.githubusercontent.com/u/6630355")
.then((res)=>{
try {
let headers = res.headers.raw();
console.info(`headers:`, JSON.stringify(headers));
let uint8array = res.arrayBuffer();
let hex = hexdump(uint8array);
console.info(hex)
}catch(error) {
console.trace(error);
}
})
.catch((error)=>{
});