SneakyPeaky99 0 Posted January 19 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. After that another call is made and again everything looks OK. And again. But after another call. 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: 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
Remy Lebeau 1396 Posted January 19 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
SneakyPeaky99 0 Posted January 23 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. Server module code was changed as well because of upgrading to Indy10. 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. 2) Body of IdHTTPServerCOmmandGet function: 3) DoGet function of TWRController class: 4) By calling GetResult function, HTML processing is triggered 5) Inside ProcessHTMLForWebServer function: 6) Constructor for script engine with result type class 7) After that ScriptEngine.Execute 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. 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
Remy Lebeau 1396 Posted January 27 (edited) 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 January 27 by Remy Lebeau Share this post Link to post
SneakyPeaky99 0 Posted February 2 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
Remy Lebeau 1396 Posted February 2 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
SneakyPeaky99 0 Posted February 21 (edited) 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. 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. 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: 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 February 21 by SneakyPeaky99 Share this post Link to post