Jump to content
SneakyPeaky99

IOHandler.Write() problems with SocketError # 10053 exception

Recommended Posts

Hello, I have a problem with TIdTCPConnection.IOHandler.Write() function. I have migrated an application from Delphi 6, and currently I am facing a problem with certain module of this application that is responsible for hosting website. Problem occurs when html parser returns data that should be used to create website (html content). Then this data is passed to IOHandler.Write(). For simplicity, I have swapped actual html code with "0123456789" string (For html content that was passed, this worked the same but I could notice any content created on website because buffer was so big, that will be explained later). After first call of IOHandler.Write() function, everything seems OK, content is displayed on website.
image.png.db33f149a11375e7fa03d5d675b5eb6a.png

After that another call is made and again everything looks OK.
image.png.899b920b145c6dba0dd868c66621a06e.png
And again.
image.png.cd0711486d2e43480896f898136160dd.png

But after another call.
image.png.1120241e7a3de7e2c4364a012eceb4f7.png

Last digit is missing, it looks like whenever length is greater than 39, content is not displayed. I have tested it for various data and it looks the same every time. After that IOHandler.Write() function is called again but this time it doesn't add anything to html content. Next call after that is causing application to throw exception:
image.thumb.png.1c80b7811fc4e999b599e4d6a58afef2.png

This of course causes application to crash and no more parsing is done.

As far as im concerned sending data works but it looks like there is something wrong with size of this thing.

This is the code of function in question:

procedure TWRStringBuilder.AddStr(const NewStr: string);
var
  tmp_string: String;
begin
  try
    // Write headers first
    if not ResponseInfo.HeaderHasBeenWritten then
      ResponseInfo.WriteHeader;

    // Don't send block under 500 bytes directly, buffer first
    if System.Length(NewStr) < 500 then
    begin
      inherited AddStr(NewStr);
      if Length > 2000 then // Send buffers over 2000 bytes only
      begin
        tmp_string := '0123456789';
        Context.IOHandler.Write(tmp_string);
        tmp_string := '';
        Clear;
      end;
    end
    else // Block over 500 bytes, send current buffer and Str
    begin
      //tmp_string := Str;
      tmp_string := '0123456789';
      Context.IOHandler.Write(tmp_string); // Send and flush buffer
      Clear;
      //tmp_string := #$D#$A + '<script type="text/javascript">' + #$D#$A + #$D#$A + '</script>' + #$D#$A;
      Context.IOHandler.Write(tmp_string); // and send current data after that
      tmp_string := '';
    end;
  except
    on E: Exception do
      OnException(E);
  end;
end;

Clear procedure only cleans Str variable, it doesn't affect the IOHandler at all.

 

procedure TFMPStringBuilder.Clear;
begin
  FStr := '';
  StrSize := 10000;
  StrPos := 0;
  SetLength(FStr, StrSize);
end;

 

Do you have any idea what could be causing such an error? Thank you very much!

 

 

Share this post


Link to post

You appear to be using TIdHTTPServer, so why are you using IOHandler.Write() directly at all? You should be using ResponseInfo.ContentText or ResponseInfo.ContentStream instead. Unless you are streaming data that is dynamically generated in chunks while the response is still being written to the client? That is not a common thing to do for HTML.

 

Can you please show a more complete example of how exactly this AddStr() method is being used in relation to your server? How are you configuring the ResponseInfo before calling AddStr() the 1st time?

 

Without more info, my guess is that you are probably violating the HTTP protocol in some way and the error is just a side effect. The error simply means the connection has been dropped on the server side, but that could happen for any number of reasons not directly related to your code.

Share this post


Link to post

Back in the days when this program was written function AddStr looked like this but unfortunately .Write() for IdTCPConnection is not available in Indy10, because of that I am using IOHandler directly.
image.thumb.png.e3f50ca332768d1faaed5237e9b0cebc.png

Server module code was changed as well because of upgrading to Indy10.
image.thumb.png.244f7f4e19ba77624cfb9fab4a72bc86.png

This was code that was used a long time ago. I tried to update it and what i came up with is pasted below.
 

Information about running AddStr() method.
1) Server module - line 52 calling IdHTTPServerCommandGet upon OnCommandGet.

image.thumb.png.32dea6ba6f5927b6d72cb12dc1f792b9.png


2) Body of IdHTTPServerCOmmandGet function:
image.thumb.png.4b8db982ac7830cbb420e0462b9f1ca8.png

3)
DoGet function of TWRController class:
image.thumb.png.40dcd79c81fd7f5574f63caa49cd01c4.png

4) By calling GetResult function, HTML processing is triggered
image.thumb.png.f015c719ea9438d3051f12a6f7549603.png

5) Inside ProcessHTMLForWebServer function:
image.thumb.png.6c61ed66a9a3353469838079ebe98ef9.png

6) Constructor for script engine with result type class
image.thumb.png.6b5361eafdc34ae9d546e93bd046b3ba.png
image.thumb.png.3484bc495ae5cb245e733f84598cc3b5.png

7) After that ScriptEngine.Execu
te is called which causes AddStr() to be triggered whenever data is ready.


I was facing 10053 error earlier, to solve it i used such function (found it on stackoverflow). It seemed to solve the problem in the short term (because exception is no longer displayed), but this causes ResponseInfo to be null, so meh.
image.thumb.png.f98976b9d8424ae284461f0c8fe2b742.png

 

I am kind of walking in the dark with this problem and it's hard for me to wrap my head around it. Thank you for your response.

Share this post


Link to post

First, that is a lot of code to go through, and blue text on black a background is very difficult to read.

 

Second, there is no mention of this failing AddStr() method being called anywhere in those screenshots.

Edited by Remy Lebeau

Share this post


Link to post

This is the function that is calling AddStr().

Stringbuilder.AddStr() is calling the function in first question.

procedure TFMPStringResult.AddStr(const Str: string);
begin
  Stringbuilder.AddStr(str);
  inherited AddStr(Str);
end;

 

After that inherited AddStr() is called:

procedure Tdws2StringResult.AddStr(const Str: string);
begin
  FStr := FStr + Str;
  debugString('Tdws2StringResult.AddStr: ', FStr);
  if Assigned(Tdws2StringResultType(ResultType).OnAddString) then
    Tdws2StringResultType(ResultType).OnAddString(Self, Str)
end;

 

Function TFMPStringResult.AddStr() is called from code in dws2.htmlfilter.pas. It doesnt use any .Write() like functions, it just provides fragment of parsed code via the argument of the Stringbuilder.AddStr() function.

procedure TSendFunction.Execute;
begin
  Tdws2StringResult(Info.Caller.Result).AddStr(VarToStr(Info['s']));
end;

 

Deeper inside dws2functions.pas. I can paste rest of the call-stack but i don't think it is relevant since it gives good results (i think) and is not related to problem (i think).

procedure TInternalFunction.Call(Caller: TProgram; Func: TFuncSymbol);
begin
  FInfo.Caller := Caller;
  Execute;
end;

Share this post


Link to post

I'm having a really hard time understanding what this code is trying to accomplish in the first place, let alone why it is writing directly to the TCP connection instead of letting TIdHTTPServer handle all of the writing like it is designed to.

 

In SIMPLE terms, can you please explain the goal of this code?

Share this post


Link to post

Hello,

Sorry for taking so long to respond. In very simple terms, this code should create a web server and then place html content on this web server. Content is dependent on files that are parsed by dws2, said files are located on a harddrive, they include html and script that is executed in brower. Application walkthrough:
    1) Set up a web server
    2) Wait untill the user refreshes the website.
    3) If the website is refreshed then "TWRServer.IdHttpServerCommandGet() is called. This function, among other things, calls ScriptEngine, that parses files from the harddrive.
    4) The result of the compilation is returned via the TFMPStringResults class and loaded into a chunk of data to be placed on a website. This chunk is passed to TWRStringBuilder, this class has access to ResponseInfo and Context. Whenever the chunk size of the compiled data reaches a specified size, it is placed on the website. If it doesnt, it is stored in variable that is appended everytime the parser returns more data. As far as I'm aware, the compilatoin is executed once at the beggining of the function, but the process of returning chunks of that compiled data takes place over and over, untill the end of the file.
Some time later, after calling the Context.IOHandler.Write() function from InDy, the 100053 exception is thrown.

image.thumb.png.7fe3e2f3e7234311f00658dbd868f478.png

 

I have spotted an interesting issue. The size of data placed on the website is always equal to 39 characters. After that no more characters are placed on the website, an exception is not thrown after this limit is reached, but rather some time later. When the .WriteHeader function is used (this function is called only once), there's a parameter called ContentLength, that is passed to the InDy TIdIOHandler.Write() function, containing the number 39.

image.thumb.png.5eb4b6f93cfdc6ba9659925ec5616090.png

 This parameter is in some way connected to the limit of characters that are able to be loaded onto the website.

 

When I manually set the ResponseInfo.ContentLength value to 1000, I was able to load 165 characters onto the website until I encountered the following error:

image.thumb.png.907b542b4b604165a20a3d20891c306f.png

 

Here's the code assigning the ContentLength value:

ResponseInfo.ContentLength := 1000;
if not ResponseInfo.HeaderHasBeenWritten then
	ResponseInfo.WriteHeader;

If you need any more info, please feel free to let me know.

   

Edited by SneakyPeaky99

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×