I am developing an extension for Mozilla Firefox, where I override the native listener with my own and monitor all HTTP requests, as shown in the post here:
http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
I monitor those requests that reside under a specific domain and change their corresponding response body, with the response body I receive from my own XMLHTTPRequest. For text files, everything is working fine.
However, I face a problem while downloading images. For some reason, when I write the incoming data to the stream and then, try to open the image, I get the error that the image contains errors and cannot be displayed.
What am I possibly doing wrong?
Update: I provide some code, in order to clarify my approach.
var xmlRequest = Cc['#mozilla.org/xmlextras/xmlhttprequest;1'].createInstance(Ci.nsIXMLHttpRequest);
...
xmlRequest.open('GET', xmlRequestURL, false);
xmlRequest.send(null);
function TracingListener() {}
TracingListener.prototype = {
originalListener: null,
onStartRequest: function (request, context) {
httpChannel = request.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
try {
this.originalListener.onStartRequest(request, context);
}
catch (ex) {
request.cancel(ex);
}
},
onDataAvailable: function (request, context, inputStream, offset, count) {
httpChannel = request.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
//Read the contents from the stream, but ignore them.
var binaryInputStream = CCIN('#mozilla.org/binaryinputstream;1', 'nsIBinaryInputStream');
binaryInputStream.setInputStream(inputStream);
var binaryOutputStream = CCIN('#mozilla.org/binaryoutputstream;1', 'nsIBinaryOutputStream');
var data = binaryInputStream.readBytes(count);
//Delay the call to the original listener.
},
onStopRequest: function (request, context, statusCode) {
httpChannel = request.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
//Check if the response is successful.
if(xmlRequest.status == 200) {
try {
var responseLen = xmlRequest.getResponseHeader("Content-Length");
var response = xmlRequest.response;
var storageStream = CCIN('#mozilla.org/storagestream;1', 'nsIStorageStream');
storageStream.init(8192, responseLen, null);
var binaryOutputStream = CCIN('#mozilla.org/binaryoutputstream;1', 'nsIBinaryOutputStream');
binaryOutputStream.setOutputStream(storageStream.getOutputStream(0));
binaryOutputStream.writeBytes(response, responseLen);
//Make the call to the original listener.
this.originalListener.onDataAvailable(request, context, storageStream.newInputStream(0), 0, responseLen);
}
catch (e) {
dumpError(e);
}
//Pass it to the original listener
this.originalListener.onStopRequest(request, context, statusCode);
}
else {
console.log('[INFO] onStopRequest not processed, status is ' + xmlRequest.status + ', for URL: ' + requestURL);
}
}
}
var observer = {
httpRequestObserver: {
observe: function (request, aTopic, aData) {
httpChannel = request.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
if(mustBeMonitored(requestURL)) {
console.log('[INFO] Observing URL: ' + requestURL);
if (aTopic == 'http-on-modify-request') {
console.log('[INFO] ' + aTopic + ' for URL: ' + requestURL);
var newListener = new TracingListener();
request.QueryInterface(Ci.nsITraceableChannel);
newListener.originalListener = request.setNewListener(newListener);
}
}
},
register: function () {
observerService.addObserver(observer.httpRequestObserver, 'http-on-modify-request', false);
},
unregister: function () {
observerService.removeObserver(observer.httpRequestObserver, 'http-on-modify-request');
},
QueryInterface: function (aIID) {
/*if (typeof Cc == "undefined") {
var Cc = components.classes;
}
if (typeof Ci == "undefined") {
var Ci = components.interfaces;
}*/
if (aIID.equals(Ci.nsIObserver) || aIID.equals(Ci.nsISupports))
return this;
throw components.results.NS_NOINTERFACE;
}
}
};
Finally, I was able to detect the problem. For the XMLHttpRequest, I had to specify its response type as follows:
xmlRequest.responseType = 'arraybuffer';
Then, the response was stored in a JavaScript ArrayBuffer, which I had to transform into a Uint8Array and then, store it into the stream.
This solution applies for both binary and text files.
Related
So I have used the below function to detect an ajax call.
var oldXHR = window.XMLHttpRequest;
function newXHR() {
var realXHR = new oldXHR();
realXHR.addEventListener("readystatechange", function() {
if(realXHR.readyState==1){
alert('server connection established');
}
if(realXHR.readyState==2){
alert('request received');
}
if(realXHR.readyState==3){
alert('processing request');
}
if(realXHR.readyState==4){
alert('request finished and response is ready');
}
}, false);
return realXHR;
}
window.XMLHttpRequest = newXHR;
It is working but now I need the url of that particular ajax request. I have functions like below:-
function loadFundSimulation(num_days = ''){
var url = "<?php echo site_url('investment_plan/simulation/FUND'); ?>";
$.post(url, data).done(function (response,status,xhr) {
#....code....#
}).fail(function (data) {
#....code....#
});
}
When the ajax is being called at that time I want url of this functions. I have many functions like this. When I get the url I want to append ?debug = 1 at the end of the url. I have tried alert(this.url); but it was returning undefined. Any help will appreciated. Thanks in advance.
Edit
var open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, uri, async, user, pass) {
this.addEventListener("readystatechange", function(event) {
if(this.readyState == 4){
var self = this;
var response = {
method: method,
uri: uri,
responseText: self.responseText
};
response.uri = uri + '?debug=1';
console.log(response);
} else {
console.log(this.readyState);
}
}, false);
open.call(this, method, uri, async, user, pass);
};
I have got the url of that ajax request and I appended ?debug=1 as well. When I console.log(response); I see the url is being changed but I still don't see any error. Please let me know I have to do anything else for that.
After searching a lot this is the best way to do this. Though only tested on chrome.
(function() {
var proxied = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function() {
arguments[1] = arguments[1] + '&debug=1';
console.log( arguments[1] );
return proxied.apply(this, [].slice.call(arguments));
};
})();
I have this Dropzone declaration below. The problem I have now is that when I submit the form with multiple files to the server, it will not take it as one request but multiple requests going in. That is why when the server returns a response, I get duplicate results. Am I able to change anything on the javascript code or serverside to prevent this from happening? Is this something wrong with Dropzone instead?
To be clear: When I upload 1 file, I get 1 response from the server. This is correct. When I upload 3 files, I get 9 responses from the server because the server takes it as each request carrying 3 files 3 times. This is wrong
Attached is an example
<script>
Dropzone.options.dropzone = {
url:"#Url.Action("Save", #ViewContext.RouteData.Values["controller"].ToString())",
autoProcessQueue: false,
addRemoveLinks: true,
uploadMultiple: true,
parallelUploads: 100,
init: function (e) {
var results = Array();
var submitButton = document.querySelector("#submit");
var token = $('input[name="__RequestVerificationToken"]').val();
var wrapperThis = this;
submitButton.addEventListener("click", function (e) {
console.log("submitted");
wrapperThis.processQueue();
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
return false;
});
this.on("addedfile", function (file) {
console.log("Added file");
}),
this.on('sendingmultiple', function (data, xhr, formData) {
formData.append("__RequestVerificationToken",token);
formData.append("CategoryId", $("#CategoryId").val());
formData.append("RepositoryId", $("#RepositoryId").val());
});;
this.on('error', function (file, message) {
toastr.error(message);
console.log(message);
});
this.on('success', function (file, message) {
//toastr.success(message);
console.log(message);
});
}
};
</script>
Here is my server code:
[ValidateAntiForgeryToken]
[HttpPost]
public async Task<ActionResult> Save(UploadDocumentViewModel Input)
{
JsonResult result = new JsonResult(null);
List<string> uploadResults = new List<string>();
var repo = repositoriesData.Get(Input.RepositoryId);
if (repo != null)
{
foreach (var item in Request.Form.Files)
{
var file = ContentDispositionHeaderValue.Parse(item.ContentDisposition);
var fileName = file.FileName.Trim('"');
var fullFilePath = Path.Combine(repo.Path, fileName);
SaveDocumentViewModel sdvm = new SaveDocumentViewModel
{
ApplicationName = configuration["ApplicationConfiguration:Name"],
ApplicationSecretKey = configuration["ApplicationConfiguration:SecretKey"],
DocumentCategoriesId = Input.CategoryId,
Name = fileName,
Filetype = item.ContentType,
Link = Url.Content("~" + RepositoryManager.GetRequestPath() + "/" + repo.Name + "/" + fileName),
Location = fullFilePath,
};
var documentId = documentsData.Save(sdvm);
if (documentId != null)
{
documentCategoriesData.ProcessFile(Input.CategoryId, documentId, fileName);
using (var fileStream = new FileStream(fullFilePath, FileMode.Create))
{
await item.CopyToAsync(fileStream);
}
uploadResults.Add(fileName + " " + LoggingGlobals.Upload);
}
else
{
return BadRequest(LoggingGlobals.UploadFailed);
}
}
}
else
{
return BadRequest(LoggingGlobals.UploadFailed);
}
return Ok(uploadResults);
}
I'm trying to use WebSocket for replacing Backbone.sync method.
Then I created three instance of "Sample" model named sample1, sample2 and sample3 and call fetch() method. Each request was successfully sent to the server.
However, when response data coming from the server, every response is set to the sample1. In other word, backbone.js triggered change event and only sample1 instance catch it.
I know there's good library named backbone.WS. However i would like to know why below code didn't work properly. Please give me any comments.
$(document).ready(function() {
var ws;
var url = ws://localhost:8080/application/sample;
Backbone.sync = function(method, model, options) {
var data = JSON.stringify({
'method' : method,
'data' : model.attributes}
);
if(ws == null) {
console.log("Create a new connection to the server.");
ws = new WebSocket(url);
console.log("Binding callback methods.");
ws.onopen = onOpen;
ws.onmessage = onMessage;
ws.onclose = onClose;
ws.onerror = onError;
}
function send(message) {
// Wait until the state of the socket is not ready and send the message when it is...
waitForSocketConnection(ws, function(){
console.log("message sent!!!");
ws.send(message);
});
}
// Make the function wait until the connection is made...
function waitForSocketConnection(socket, callback){
setTimeout(
function () {
if (socket.readyState === 1) {
console.log("Connection is made")
if(callback != null){
callback();
}
return;
} else {
console.log("Waiting for connecting...")
waitForSocketConnection(socket, callback);
}
}, 10); // wait 5 milisecond for the connection...
}
function onOpen(event) {
console.info("Connection established to " + url);
};
function onMessage(event) {
console.info("Message Received!");
var message = JSON.parse(event.data);
console.log("model: " + JSON.stringify(model.attributes));
model.set(message);
};
function onClose(event) {
console.log(event.code);
};
function onError() {
console.warn("There was an error with your websocket.");
}
send(data);
console.log("Request is sent to the server.");
options.success(message);
};
var Sample = Backbone.Model.extend({
defaults : {
id : 0,
name : "default"
}
});
var sample1 = new Sample({id:1});
var sample2 = new Sample({id:2});
var sample3 = new Sample({id:3});
sample1.fetch();
sample2.fetch();
sample3.fetch();
sample1.on("change", function(model) {
console.log("--- onChange method in sample 1");
});
sample2.on("change", function(model) {
console.log("+++ onChange method in sample 2");
});
sample3.on("change", function(model) {
console.log("=== onChange method in sample 3");
});
}
Hy
I need to find the current user from my SharePoint.
I have tried many things :
SP.Utilities.PrincipalInfo.get_loginName()
_spPageContextInfo.userId
...
At all times, I have the same result Undefined =(
When using CSOM API to retrieve current user object,wrap your code inside
SP.SOD.executeOrDelayUntilScriptLoaded method to make sure that the specified code is executed after SharePoint JS library (sp.js) is loaded:
SP.SOD.executeOrDelayUntilScriptLoaded(function(){
//your code goes here..
}, 'sp.js');
How to retrieve current user object using CSOM API
function getCurrentUser(success,error)
{
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var currentUser = web.get_currentUser();
ctx.load(currentUser);
ctx.executeQueryAsync(function(){
success(currentUser);
},
error);
}
Usage
SP.SOD.executeOrDelayUntilScriptLoaded(function(){
getCurrentUser(
function(currentUser){
console.log(currentUser.get_loginName());
},
function(sender, args)
{
console.log('Request failed ' + args.get_message() + ':'+ args.get_stackTrace());
});
}, 'sp.js');
The answer is probably here. The only thing i changed is getting LoginName instead of Title:
https://stackoverflow.com/a/21002895/1680288
var userid= _spPageContextInfo.userId;
var requestUri = _spPageContextInfo.webAbsoluteUrl + "/_api/web/getuserbyid(" + userid + ")";
var requestHeaders = { "accept" : "application/json;odata=verbose" };
$.ajax({
url : requestUri,
contentType : "application/json;odata=verbose",
headers : requestHeaders,
success : onSuccess,
error : onError
});
function onSuccess(data, request){
var loginName = data.d.LoginName;
alert(loginName);
}
function onError(error) {
alert("error");
}
If you are getting undefined.. Maybe you are not authenticated or did not include some relevant javascript files in your master page.
Without jquery:
var userid= _spPageContextInfo.userId;
var requestUri = _spPageContextInfo.webAbsoluteUrl + "/_api/web/getuserbyid(" + userid + ")";
function createXMLHttp() {
//If XMLHttpRequest is available then using it
if (typeof XMLHttpRequest !== undefined) {
return new XMLHttpRequest;
//if window.ActiveXObject is available than the user is using IE...so we have to create the newest version XMLHttp object
} else if (window.ActiveXObject) {
var ieXMLHttpVersions = ['MSXML2.XMLHttp.5.0', 'MSXML2.XMLHttp.4.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp', 'Microsoft.XMLHttp'],
xmlHttp;
//In this array we are starting from the first element (newest version) and trying to create it. If there is an
//exception thrown we are handling it (and doing nothing ^^)
for (var i = 0; i < ieXMLHttpVersions.length; i++) {
try {
xmlHttp = new ActiveXObject(ieXMLHttpVersions[i]);
return xmlHttp;
} catch (e) {
}
}
}
}
function getData() {
var xmlHttp = createXMLHttp();
xmlHttp.open('get', requestUri , true);
xmlHttp.setRequestHeader("Content-Type", "application/json;odata=verbose");
xmlHttp.setRequestHeader("accept", "application/json;odata=verbose");
xmlHttp.send(null);
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState === 4) {
if (xmlHttp.status === 200) {
var data = JSON.parse(xmlHttp.responseText);
var loginName = data.d.LoginName;
alert(loginName);
} else {
}
} else {
//still processing
}
};
}
getData();
I have a web worker that I'm using to poll for information.
Here is the code for starting and stopping the web worker:
var eftWorker = undefined;
that.StartWorker = function () {
if (eftWorker === undefined) {
eftWorker = new Worker('../scripts/ETEL.EftWorker.js');
eftWorker.addEventListener('message', function(e) {
EftWorkerResponseHandler(e.data);
}, false);
eftWorker.addEventListener('error', function(e) {
EftWorkerErrorResponseHandler(e);
}, false);
}
eftWorker.postMessage({ cmd: eftCmdStart });
};
that.StopWorker = function () {
if (eftWorker !== undefined) {
eftWorker.postMessage({ cmd: eftCmdStop });
eftWorker.terminate();
}
eftWorker = undefined;
};
When I call terminate on the worker, because the worker is polling there seems to be a backlog of unprocessed postmessage events.
I'm setting the web worker to "undefined" on initialisation of the containing view and on termination of the web worker. I believe because of the latter, those unprocessed events are shown as ABORT_ERRORs.
Is there an intermediate state that I can use to test the existence of the web worker so that I can allow it to process the outstanding events and therefore avoid the errors?
Or is there a different approach that I might use to avoid the accumulation of errors after terminate is called?
Here is my solution to the problem.
I'm recording the state of the worker in a separate variable in order that I can keep it alive to handle the outstanding messages that are causing the errors.
Also I'm trapping and discarding any errors generated inside the worker itself.
var eftWorker = undefined;
var eftWorkerState = undefined;
var workerStateStarted = 'started';
var workerStateStopped = 'stopped';
var StartWorker = function () {
if (eftWorker === undefined | eftWorkerState !== workerStateStarted) {
eftWorker = new Worker('/scripts/ETEL.EftWorker.js');
eftWorker.addEventListener('message', function(e) {
EftWorkerResponseHandler(e.data);
}, false);
eftWorker.addEventListener('error', function (e) {
EftWorkerErrorResponseHandler(e);
}, false);
}
eftWorker.postMessage({ cmd: eftCmdStart });
eftWorkerState = workerStateStarted;
};
that.StopWorker = function () {
if (eftWorker !== undefined) {
eftWorker.postMessage({ cmd: eftCmdStop });
eftWorker.terminate();
}
eftWorkerState = workerStateStopped;
//eftWorker = undefined;
};
var EftWorkerResponseHandler = function (msg) {
try {
if (eftWorkerState === workerStateStarted) {
if (msg != '' && msg !== undefined) {
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(msg, 'text/xml');
var json = $.xmlToJSON(xmlDoc);
AlterPaymentUIAccordingToEftWorkerStatus(json);
}
}
} catch (exception) { }
};
And here's the code from the worker responsible for sending back the status messages.
EftSendGetRequest = function(passedUrl) {
if (xmlHttpReq === undefined) {
xmlHttpReq = new XMLHttpRequest();
}
try {
xmlHttpReq.open("GET", passedUrl, false);
xmlHttpReq.send();
} catch (e) { }
return xmlHttpReq.responseText;
};
This is old but I was looking at it while searching for a different answer..
Why not let the worker handle its own state and termination? The original question asks how to let the worker finish outstanding requests. So let it finish its requests and indicate when it is done.
If the javascript was something like this:
var eftWorker = undefined;
var StartWorker = function () {
if (eftWorker === undefined) {
eftWorker = new Worker('/scripts/ETEL.EftWorker.js');
eftWorker.addEventListener('message', function(e) {
EftWorkerResponseHandler(e.data);
}, false);
eftWorker.addEventListener('error', function (e) {
EftWorkerErrorResponseHandler(e);
}, false);
// i'm assuming we don't want to trigger start multiple times,
// so this is moved inside the IF.
eftWorker.postMessage({ cmd: eftCmdStart });
}
};
that.StopWorker = function () {
if (eftWorker !== undefined) {
eftWorker.postMessage({ cmd: eftCmdStop });
}
};
var EftWorkerResponseHandler = function (msg) {
try {
if (msg && msg === 'readyToTerminate') {
eftWorker.terminate();
eftWorker = undefined;
} else {
// handle other situations.
}
} catch (exception) { }
};
and the worker was like this:
;(function(self, undefined) {
var receivedStopCmd = false;
self.addEventListener('message', function(e){
if (e.data.cmd === 'eftCmdStart') {
// kick off processing here
EftSendGetRequest('...');
}
if (e.data.cmd === 'eftCmdStop') {
// xhr might be in process, this just updates what
// the onload function does.
receivedStopCmd = true;
}
}, false);
var EftSendGetRequest = function(passedUrl) {
if (xmlHttpReq === undefined) {
xmlHttpReq = new XMLHttpRequest();
}
try {
xmlHttpReq.open("GET", passedUrl, false);
xmlHttpReq.onload = function(){
if (!receivedStopCmd) {
// post response and/or keep polling
self.postMessage('whatever the message is');
} else {
// (1) stop polling so no more
// requests are sent..if this
// requires doing anyhting
// (2) Send a message that this worker can be terminated.
self.postMessage('readyToTerminate');
}
};
xmlHttpReq.send();
} catch (e) { }
return xmlHttpReq.responseText;
};
})(self);
This would allow the XHR to manage itself. I didn't run this of course.. its just an example of the approach I would take on the question.