Form blocked while HTTP post does not finish - json

I am sending JSON to an endpoint and I have a problem. The execution works, but the form gets stuck until HTTP.Post() ends, after it has finished the screen releases for use. I'm sure I'm doing something wrong.
Here is the button action that sends the JSON:
procedure TForm1.Button1Click(Sender: TObject);
var
HTTP: TIdHTTP;
vJsonAEnviar: TStringStream;
Json:String;
begin
Json := '{ '+
' "user":"Lucy"'+
' "execute":"ok"'+
' } ';
HTTP := TIdHTTP.Create;
HTTP.Request.ContentType := 'application/json';
HTTP.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0';
vJsonAEnviar := TStringStream.Create(UTF8Encode(Json));
HTTP.Post('http://localhost:8080/exportaManual', vJsonAEnviar);
FreeAndNil(HTTP);
FreeAndNil(vJsonAEnviar);
end;
On the other side, it takes time to finish and the screen stays blockrd a long time.

You are not doing anything wrong (well, except for a complete lack of any error handling). This is simply how Indy is designed to operate (see Introduction to Indy). Indy uses blocking socket operations. When you perform an operation, the calling thread is blocked until the operation is complete. This is normal.
If you don't want your UI frozen while the POST is in progress, you can either:
drop a TIdAntiFreeze component onto your Form. It will pump UI messages in the background while Indy is blocking the main UI thread.
move the POST code to its own worker thread, using TThread, TTask, TIdThreadComponent, etc, and have it notify the main UI thread when finished.

Related

Do I need to free instance of TJSONArray

The answer is obviously yes, but I have this code that has been running for almost a month now 24/7 and everything is fine. Here is the code :
var
jsonArray : TJSONArray;
jsonValue : TJSONValue;
json : string;
begin
json := 'JSON_MASTER';
jsonArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(json), 0) as TJSONArray;
for jsonValue in jsonArray do
begin
// do the thing 1
end;
json := 'JSON_DETAIL';
jsonArray := TJSONObject.ParseJSONValue(TEncoding.UTF8.GetBytes(json), 0) as TJSONArray;
for jsonValue in jsonArray do
begin
// do the thing 2
end;
end;
The application is a web service(SOAP). The function is executed around 2K per day. I am aware of the issue in the code but because the SOAP is not crushing I'm not fixing it yet. The task manager performance report shows fine. There is no sign of growing memory usage. Why is there no sign of memory leaks? Is there such thing as garbage collection in TJSONArray?
It depends on which platform you are running on.
If your app is running on a non-ARC platform, such as Windows or OSX, then YES, you need to manually free the TJSONArray when you are done using it, or else it will be leaked.
If your app is running on an ARC platform, such as iOS, Android, or Linux, then NO, you do not need to manually free the TJSONArray when you are done using it, as it will be freed automatically when all references to it have gone out of scope.

IdHTTP.Get(url, ss) give 403 Forbidden

I'm using IdHTTP to execute php files on server. Worked fine for years. Suddenly getting 403 Forbidden errors with all my programs. Archived versions from year ago now fail also. Web host says they have changed nothing. To test, placed a simple php file that simply echoes a value on 3 separate host platforms (none SSL). Calls to all 3 fail with 403 error. If the url is placed in a browser address and called from there, call succeeds with expected value returned. Also tried running program connected via different ISPs. These failures just popped up in the last few days. Happens on many different computers.
Here is a very simple example that fails when sent to all 3 test servers
procedure TForm1.Button1Click(Sender: TObject);
var url: string;
H: TIdHttp;
SS: TStringStream;
begin
url := 'http://www.somesite.com/test.php';
H := TIdHttp.Create(nil);
SS := TStringStream.Create;
try
H.Get(url, SS);
Edit1.Text := SS.DataString;
finally
H.Free;
SS.Free;
end;
end;
Any help greatly appreciated.

why DeferredResult ends on setResult() on trying to use SSE

i am trying to implement a Server Sent Events (SSE) webpage which is powered by Spring. My test code does the following:
Browser uses EventSource(url) to connect to server. Spring accepts the request with the following controller code:
#RequestMapping(value="myurl", method = RequestMethod.GET, produces = "text/event-stream")
#ResponseBody
public DeferredResult<String> subscribe() throws Exception {
final DeferredResult<String> deferredResult = new DeferredResult<>();
resultList.add(deferredResult);
deferredResult.onCompletion(() -> {
logTimer.info("deferedResult "+deferredResult+" completion");
resultList.remove(deferredResult);
});
return deferredResult;
}
So mainly it puts the DeferredResult in a List and register a completion callback so that i can remove this thing from the List in case of completion.
Now i have a timer method, that will periodically output current timestamp to all registered "browser" via their DeferredResults.
#Scheduled(fixedRate=10000)
public void processQueues() {
Date d = new Date();
log.info("outputting to "+ LoginController.resultList.size()+ " connections");
LoginController.resultList.forEach(deferredResult -> deferredResult.setResult("data: "+d.getTime()+"\n\n"));
}
The data is sent to the browser and the following client code works:
var source = new EventSource('/myurl');
source.addEventListener('message', function (e) {
console.log(e.data);
$("#content").append(e.data).append("<br>");
});
Now the problem:
The completion callback on the DeferredResult is called on every setResult() call in the timer thread. So for some reason the connection is closed after the setResult() call. SSE in the browser reconnects as per spec and then same thing again. So on client side i have a polling behavior, but i want an kept open request where i can push data on the same DeferredResult over and over again.
Do i miss something here? Is DeferredResult not capable of sending multiple results? i put in a 10 seconds delay in the timer thread to see if the request only terminates after setResult(). So in the browser the request is kept open until the timer pushes the data but then its closed.
Thanks for any hint on that. One more note: I added async-supported to all filters/servlets in tomcat.
Indeed DeferredResult can be set only once (notice that setResult returns a boolean). It completes processing with the full range of Spring MVC processing options, i.e. meaning that all you know about what happens during a Spring MVC request remains more or less the same, except for the asynchronously produced return value.
What you need for SSE is something more focused, i.e. write each value to the response using an HttpMessageConverter. I've created a ticket for that https://jira.spring.io/browse/SPR-12212.
Note that Spring's SockJS support does have an SSE transport which takes care of a few extras such as cross-domain requests with cookies (important for IE). It's also used on top of a WebSocket API and WebSocket-style messaging (even if WebSocket is not available on either the client or the server side) which fully abstracts the details of HTTP long polling.
As a workaround you can also write directly to the Servlet response using an HttpMessageConverter.

Load HTML source code

How do I load HTML source code? I tried a few functions with no luck. This one works for me on many sites, but not all of them:
function LoadWebPageToString(MyUrl: String): String; //load HTML content from a webpage to a string
begin
Result := 'Error';
with TIdHTTP.Create do
begin
try
Result := Get(MyUrl);
finally
Free;
end;
end;
end;
When it fails, I get this error:
HTTP/1.1 403 forbidden
The target page is just a normal page. It loads normally via HTTP, doesn't require (think nor supports) HTTPS. Maybe it is about cookies or something? I don't know.
One of the reasons of the HTTP/1.1 403 forbidden is which the server doesn't recognizes the user agent of the client, so try setting the useragent property like so .
Request.UserAgent:='Mozilla/5.0 (Windows NT 6.1; WOW64; rv:21.0) Gecko/20100101 Firefox/21.0';

SignalR client function not called when transport=serverSentEvents

On my machines I find that SignalR client functions are not called in Chrome.
My SignalR test app works fine with with IE9, Firefox and even Safari on my iphone. Looking at Fiddler, these browsers all seem to negotiate transport=longPolling. But Chrome negotiates a connection with transport=serverSentEvents, and I'm assuming this is why client functions are not called in Chrome.
More detail:
I'm using full IIS (not IIS express) on Windows 7. I'm using SignalR version 1.0.0-rc2. I've disabled my AVG firewall and the Windows firewall is not running. Chrome is version 24.0.1312.56, and was up-to-date at the time of writing. The app is invoked on localhost.
On Chrome, the signalR connection seems to take place OK - the $.connection.hub.start().done callback function is invoked. But after that, the client function is never called, even while the other browsers work just fine.
In the client-side code, I've turned on logging with
$.connection.hub.logging = true;
I can see log messages in Chrome's javascript console that correspond to a successful connection.
For reference, those log messages are
[20:22:16 GMT+0800 (W. Australia Standard Time)] SignalR: Negotiating with '/SignalRChat-RC/signalr/negotiate'. jquery.signalR-1.0.0-rc2.js:54
[20:22:16 GMT+0800 (W. Australia Standard Time)] SignalR: Attempting to connect to SSE endpoint 'http://localhost/SignalRChat-RC/signalr/connect?transport=serverSentEvents&…7-22c5dbf27e0d&connectionData=%5B%7B%22name%22%3A%22chathub%22%7D%5D&tid=3' jquery.signalR-1.0.0-rc2.js:54
[20:22:16 GMT+0800 (W. Australia Standard Time)] SignalR: EventSource connected jquery.signalR-1.0.0-rc2.js:54
[20:22:16 GMT+0800 (W. Australia Standard Time)] SignalR: Now monitoring keep alive with a warning timeout of 40000 and a connection lost timeout of 60000 jquery.signalR-1.0.0-rc2.js:54
But there are no messages logged in Chrome's javascript console when a client-side method is invoked.
Interestingly, the send method works OK on Chrome. The other clients display a message sent from Chrome even through Chrome itself can't see it.
The application is pretty much the chat application from the signalR tutorial at http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr
If I explicitly specify longPolling in the start method, i.e.
$.connection.hub.start({ transport: 'longPolling' })
then Chrome works OK. But my expectation was that I should be able to allow the browsers to negotiate their connection, and things would Just Work.
For reference, the relevant part of my client-side code looks like this:
$(function () {
// Turn on logging to the javascript console
$.connection.hub.logging = true;
// set up an error-handling function
$.connection.hub.error(function (err) {
alert("Error signalR:" + JSON.stringify(err));
});
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
// Create a function that the hub can call to broadcast messages.
// This function is never called when running in Chrome with the default signalR connection
chat.client.broadcastMessage = function (name, message) {
// Html encode display name and message.
var encodedName = $('<div />').text(name).html();
var encodedMsg = $('<div />').text(message).html();
// Add the message to the page.
$('#discussion').append('<li><strong>' + encodedName
+ '</strong>: ' + encodedMsg + '</li>');
};
// Get the user name and store it to prepend to messages.
$('#displayname').val(prompt('Enter your name:', ''));
// Set initial focus to message input box.
$('#message').focus();
// Start the connection.
// Use $.connection.hub.start({ transport: 'longPolling' }) for reliability
// Use $.connection.hub.start() to demonstrate that Chrome doesn't receive messages
$.connection.hub.start().done(function () {
// Enable the "Send" button
$('#sendmessage').removeAttr('disabled');
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send($('#displayname').val(), $('#message').val());
// Clear text box and reset focus for next comment.
$('#message').val('').focus();
});
});
});
Can anybody see what I'm doing wrong?
I tried the sample in Win7 Google Chrome 24 and it works fine.
You can troubleshoot installing Fiddler and setting breakpoints in the javascript
POST /signalr/send?transport=serverSentEvents&connectionId=6ff0bffa-c31e-4d85-9aff-24f4528555ee HTTP/1.1
Host: localhost:43637
Connection: keep-alive
Content-Length: 113
Accept: application/json, text/javascript, */*; q=0.01
Origin:
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17
Content-Type: application/x-www-form-urlencoded
Referer: /index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
data=%7B%22H%22%3A%22chathub%22%2C%22M%22%3A%22Send%22%2C%22A%22%3A%5B%22gus%22%2C%22hello%22%5D%2C%22I%22%3A0%7D
May be it is related with buffering the responses.
https://github.com/SignalR/SignalR/issues/1944
Try setting EnableJSONP = False on the server hub. This fixed a similar issue I was having.