Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2014-04

CustHttpApp again, error: "Missing HTTP protocol version in request"

9 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

silvioprog

Hello,

I've tested the CustHttpApp unit in new released FPC 2.6.4 on Windows, and the error "Missing HTTP protocol version in request" still continues.

I'll show detailed steps (including my tools) to reproduce it:

1. on Windows (I'm using 8), download and install the latest Lazarus/FPC version: "
http://downloads.sourceforge.net/project/lazarus/Lazarus%20Windows%2032%20bits/Lazarus%201.2.2/lazarus-1.2.2-fpc-2.6.4-win32.exe ";
2. download this source code (the project to reproduce the bug) from link " https://www.dropbox.com/s/rmfitgh7b58cf1g/es_bug.zip" or in attachment;
3. in "Tools | Options ..." menu, got to the "Debugger > Language Exceptions" page, and check the "Notify on Lazarus Exceptions" option;
4. run the project with F9 (notice that I left a breakpoint);
5. using the "Advanced REST client" Chrome plugin " https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo", fill the URL with "http://localhost:1025" and click in "Send" button;
6. push F9 until the Lazarus exits from breakpoint;
7. wait a few moments, that Lazarus will go to the breakpoint after some secounds;

When Lazarus go to the breakpoint again, push F8 several times, and notice that in line 363, the "R" variable will receive the zero value:

...
  Procedure FillBuffer;
  Var
    R : Integer;
  begin
    SetLength(FBuffer,ReadBufLen);
    r:=FSocket.Read(FBuffer[1],ReadBufLen); <<< HERE!
    If r<0 then
      Raise EHTTPServer.Create(SErrReadingSocket);
    if (r<ReadBuflen) then
      SetLength(FBuffer,r);
  end;
...

And the FBuffer will be empty, so in line 485, the "S" variable will be empty:

...
Var
  S : String;
begin
  Request.Method:=GetNextWord(AStartLine);
  Request.URL:=GetNextWord(AStartLine);
  Request.PathInfo:=Request.URL;
  S:=GetNextWord(AStartLine); <<< HERE!
  If (Pos('HTTP/',S)1) then
    Raise Exception.Create(SErrMissingProtocol);
  Delete(S,1,5);
  Request.ProtocolVersion:=trim(S);
end;
...

Causing the related error.

Thanks and sorry for the long e-mail!

Silvio Clécio My public projects - github.com/silvioprog

Reply by email

Michael Van Canneyt
to silvioprog
silvioprog wrote 37 lines
Hello, I've tested the CustHttpApp unit in new released FPC 2.6.4 on Windows, and the error "Missing HTTP protocol version in request" still continues. I'll show detailed steps (including my tools) to reproduce it: 1. on Windows (I'm using 8), download and install the latest Lazarus/FPC version: "http://downloads.sourceforge.net/project/lazarus/Lazarus%20Windows%2032%20bits/Lazarus%201.2.2/lazarus-1.2.2-fpc-2.6.4-win32.exe"; 2. download this source code (the project to reproduce the bug) from link "https://www.dropbox.com/s/rmfitgh7b58cf1g/es_bug.zip" or in attachment; 3. in "Tools | Options ..." menu, got to the "Debugger > Language Exceptions" page, and check the "Notify on Lazarus Exceptions" option;  4. run the project with F9 (notice that I left a breakpoint); 5. using the "Advanced REST client" Chrome plugin "https://chrome.google.com/webstore/detail/advanced-rest-client/hgmloofddffdnphfgcellkdfbfbjeloo", fill the URL with "http://localhost:1025" and click in "Send" button; 6. push F9 until the Lazarus exits from breakpoint; 7. wait a few moments, that Lazarus will go to the breakpoint after some secounds; When Lazarus go to the breakpoint again, push F8 several times, and notice that in line 363, the "R" variable will receive the zero value: ...   Procedure FillBuffer;   Var     R : Integer;   begin     SetLength(FBuffer,ReadBufLen);     r:=FSocket.Read(FBuffer[1],ReadBufLen); <<< HERE!     If r<0 then       Raise EHTTPServer.Create(SErrReadingSocket);     if (r<ReadBuflen) then       SetLength(FBuffer,r);   end;

Damn windows returning 0 on read...
Can you please test with the following change:

    Procedure FillBuffer;
    Var
      R : Integer;
    begin
      SetLength(FBuffer,ReadBufLen);
      Repeat
        r:=FSocket.Read(FBuffer[1],ReadBufLen);
        If r<0 then
          Raise EHTTPServer.Create(SErrReadingSocket);
        if R=0 then
          Sleep(10);
      Until (R0);
      if (R<ReadBuflen) then
        SetLength(FBuffer,r);
    end;

Michael.

Reply by email

silvioprog
to Michael Van Canneyt

2014-04-25 3:39 GMT-03:00 Michael Van Canneyt :
[...]

Show quoted text 23 lines
Damn windows returning 0 on read... Can you please test with the following change: Procedure FillBuffer; Var R : Integer; begin SetLength(FBuffer,ReadBufLen); Repeat r:=FSocket.Read(FBuffer[1],ReadBufLen); If r<0 then Raise EHTTPServer.Create(SErrReadingSocket); if R=0 then Sleep(10); Until (R0); if (R<ReadBuflen) then SetLength(FBuffer,r); end;

After apply this change, now when read returns 0, it freezes the app., because the "sleep(10)" command is infinitely called. :/

Damn windows!

Silvio Clécio My public projects - github.com/silvioprog

Reply by email

silvioprog
to silvioprog

2014-04-25 10:05 GMT-03:00 silvioprog :

Show quoted text 32 lines
2014-04-25 3:39 GMT-03:00 Michael Van Canneyt : [...] > Damn windows returning 0 on read... Can you please test with the > following change: > > > Procedure FillBuffer; > > Var > R : Integer; > > begin > SetLength(FBuffer,ReadBufLen); > Repeat > r:=FSocket.Read(FBuffer[1],ReadBufLen); > > If r<0 then > Raise EHTTPServer.Create(SErrReadingSocket); > if R=0 then > Sleep(10); > Until (R0); > if (R<ReadBuflen) then > SetLength(FBuffer,r); > end; After apply this change, now when read returns 0, it freezes the app., because the "sleep(10)" command is infinitely called. :/ Damn windows!

I have no much experience with sockets on Windows, but I saw two links:

http://stackoverflow.com/questions/10526382/recv-returns-0

http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable

Seems that error occurs when client disconnects from server. :/

Silvio Clécio My public projects - github.com/silvioprog

Reply by email

Michael Van Canneyt
to silvioprog: 2014-04-25 3:39 GMT-03:00 Michael Van Canneyt : [...] After apply this change, now when…
silvioprog wrote 24 lines
2014-04-25 3:39 GMT-03:00 Michael Van Canneyt : [...]  Damn windows returning 0 on read... Can you please test with the following change:    Procedure FillBuffer;    Var      R : Integer;    begin      SetLength(FBuffer,ReadBufLen);      Repeat        r:=FSocket.Read(FBuffer[1],ReadBufLen);        If r<0 then          Raise EHTTPServer.Create(SErrReadingSocket);        if R=0 then          Sleep(10);      Until (R0);      if (R<ReadBuflen) then        SetLength(FBuffer,r);    end; After apply this change, now when read returns 0, it freezes the app., because the "sleep(10)" command is infinitely called. :/

OK, what we can do is add a timeout:

Procedure FillBuffer;
Const
   MaxReadRetries = 150;
Var
  R,C : Integer;
begin
   C:=0;
  SetLength(FBuffer,ReadBufLen);
   Repeat
    r:=FSocket.Read(FBuffer[1],ReadBufLen);
    If r<0 then
      Raise EHTTPServer.Create(SErrReadingSocket);
     if R=0 then
       begin
       Sleep(10);
       Inc(C);
       end;
   Until (R0) or (C>MaxReadRetries);
  if (R<ReadBuflen) then
    SetLength(FBuffer,r);
end;

It will then try 150 times, and then error out.

BUT, reading this:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms740121%28v=vs.85%29.aspx

"If the connection has been gracefully closed, the return value is zero."

So, this means then thay the original implementation is correct. You just need to catch the error using the OnRequestError handler...

Michael.

Reply by email

Michael Van Canneyt
to silvioprog: 2014-04-25 10:05 GMT-03:00 silvioprog : I have no much experience with sockets on…
silvioprog wrote 36 lines
2014-04-25 10:05 GMT-03:00 silvioprog : 2014-04-25 3:39 GMT-03:00 Michael Van Canneyt : [...]  Damn windows returning 0 on read... Can you please test with the following change:    Procedure FillBuffer;    Var      R : Integer;    begin      SetLength(FBuffer,ReadBufLen);      Repeat        r:=FSocket.Read(FBuffer[1],ReadBufLen);        If r<0 then          Raise EHTTPServer.Create(SErrReadingSocket);        if R=0 then          Sleep(10);      Until (R0);      if (R<ReadBuflen) then        SetLength(FBuffer,r);    end; After apply this change, now when read returns 0, it freezes the app., because the "sleep(10)" command is infinitely called. :/ Damn windows! I have no much experience with sockets on Windows, but I saw two links: http://stackoverflow.com/questions/10526382/recv-returns-0 http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable Seems that error occurs when client disconnects from server. :/

See my other post...

Michael.

Reply by email

silvioprog
to Michael Van Canneyt: OK, what we can do is add a timeout: Procedure FillBuffer; Const MaxReadRetries = 150; Var

2014-04-25 10:24 GMT-03:00 Michael Van Canneyt :

Show quoted text 71 lines
On Fri, 25 Apr 2014, silvioprog wrote: > 2014-04-25 3:39 GMT-03:00 Michael Van Canneyt : > [...] > Damn windows returning 0 on read... Can you please test with the > following change: > > Procedure FillBuffer; > > Var > R : Integer; > > begin > SetLength(FBuffer,ReadBufLen); > Repeat > r:=FSocket.Read(FBuffer[1],ReadBufLen); > If r<0 then > Raise EHTTPServer.Create(SErrReadingSocket); > if R=0 then > Sleep(10); > Until (R0); > if (R<ReadBuflen) then > SetLength(FBuffer,r); > end; > > > After apply this change, now when read returns 0, it freezes the app., > because the "sleep(10)" command is infinitely called. :/ > OK, what we can do is add a timeout: Procedure FillBuffer; Const MaxReadRetries = 150; Var R,C : Integer; begin C:=0; SetLength(FBuffer,ReadBufLen); Repeat r:=FSocket.Read(FBuffer[1],ReadBufLen); If r<0 then Raise EHTTPServer.Create(SErrReadingSocket); if R=0 then begin Sleep(10); Inc(C); end; Until (R0) or (C>MaxReadRetries); if (R<ReadBuflen) then SetLength(FBuffer,r); end; It will then try 150 times, and then error out. BUT, reading this: http://msdn.microsoft.com/en-us/library/windows/desktop/ ms740121%28v=vs.85%29.aspx "If the connection has been gracefully closed, the return value is zero." So, this means then thay the original implementation is correct. You just need to catch the error using the OnRequestError handler...

Sorry for my ignorance, but, would not it be better to read the HTTP data only if the client is still connected or if read bytes > 0?

In original implementation, I made a ''quick fix" to solve my problem, please see the "ADDED" lines:

[code]
...
TFPHTTPConnection = Class(TObject)

  private

{$IFDEF MSWINDOWS} << ADDED
FCanHandleRequest: Boolean; << ADDED
{$ENDIF} << ADDED
...
[/code]

[code]
...
function TFPHTTPConnection.ReadRequestHeaders: TFPHTTPConnectionRequest;

Var
  StartLine,S : String;
begin
  Result:=Server.CreateRequest;
  try
    Server.InitRequest(Result);
    Result.FConnection:=Self;
    StartLine:=ReadString;
{$IFDEF MSWINDOWS}
    FCanHandleRequest := (StartLine  '');
    If not FCanHandleRequest Then
      Exit;
{$ENDIF}
...
[/code]

[code]
...
procedure TFPHTTPConnection.HandleRequest;

Var
  Req : TFPHTTPConnectionRequest;
  Resp : TFPHTTPConnectionResponse;
begin
  Try
    SetupSocket;
    // Read headers.
    Req:=ReadRequestHeaders;
    try
{$IFDEF MSWINDOWS} << ADDED
      if not FCanHandleRequest then << ADDED
        Exit; << ADDED
{$ENDIF} << ADDED
...
[/code]
Silvio Clécio My public projects - github.com/silvioprog

Reply by email

Michael Van Canneyt
to silvioprog
silvioprog wrote 67 lines
2014-04-25 10:24 GMT-03:00 Michael Van Canneyt : On Fri, 25 Apr 2014, silvioprog wrote: 2014-04-25 3:39 GMT-03:00 Michael Van Canneyt : [...]        Damn windows returning 0 on read... Can you please test with the following change:          Procedure FillBuffer;          Var            R : Integer;          begin            SetLength(FBuffer,ReadBufLen);      Repeat        r:=FSocket.Read(FBuffer[1],ReadBufLen);        If r<0 then          Raise EHTTPServer.Create(SErrReadingSocket);        if R=0 then          Sleep(10);      Until (R0);      if (R<ReadBuflen) then        SetLength(FBuffer,r);    end; After apply this change, now when read returns 0, it freezes the app., because the "sleep(10)" command is infinitely called. :/ OK, what we can do is add a timeout: Procedure FillBuffer; Const   MaxReadRetries = 150; Var   R,C : Integer; begin   C:=0;   SetLength(FBuffer,ReadBufLen);   Repeat     r:=FSocket.Read(FBuffer[1],ReadBufLen);     If r<0 then       Raise EHTTPServer.Create(SErrReadingSocket);     if R=0 then       begin       Sleep(10);       Inc(C);       end;   Until (R0) or (C>MaxReadRetries);   if (R<ReadBuflen) then     SetLength(FBuffer,r); end; It will then try 150 times, and then error out. BUT, reading this: http://msdn.microsoft.com/en-us/library/windows/desktop/ms740121%28v=vs.85%29.aspx "If the connection has been gracefully closed, the return value is zero." So, this means then thay the original implementation is correct. You just need to catch the error using the OnRequestError handler... Sorry for my ignorance, but, would not it be better to read the HTTP data only if the client is still connected or if read bytes > 0?

Yes and no.
Yes in the sense that you have a more 'clean' experience. No, because it IS an error condition (client went away unexpectedly) and you should be aware of it.

I will make it a property that is settable: 'IgnoreLostConnections' or so.

Michael.

Reply by email

silvioprog
to Michael Van Canneyt

2014-04-25 11:11 GMT-03:00 Michael Van Canneyt :

Show quoted text 82 lines
On Fri, 25 Apr 2014, silvioprog wrote: > 2014-04-25 10:24 GMT-03:00 Michael Van Canneyt : > On Fri, 25 Apr 2014, silvioprog wrote: > 2014-04-25 3:39 GMT-03:00 Michael Van Canneyt < > >: > [...] > Damn windows returning 0 on read... Can you please test > with the following change: > > Procedure FillBuffer; > > Var > R : Integer; > > begin > SetLength(FBuffer,ReadBufLen); > Repeat > r:=FSocket.Read(FBuffer[1],ReadBufLen); > If r<0 then > Raise EHTTPServer.Create(SErrReadingSocket); > if R=0 then > Sleep(10); > Until (R0); > if (R<ReadBuflen) then > SetLength(FBuffer,r); > end; > > > After apply this change, now when read returns 0, it freezes > the app., because the "sleep(10)" command is infinitely called. :/ > > > OK, what we can do is add a timeout: > > Procedure FillBuffer; > > Const > MaxReadRetries = 150; > > Var > R,C : Integer; > > begin > C:=0; > SetLength(FBuffer,ReadBufLen); > Repeat > r:=FSocket.Read(FBuffer[1],ReadBufLen); > If r<0 then > Raise EHTTPServer.Create(SErrReadingSocket); > if R=0 then > begin > Sleep(10); > Inc(C); > end; > Until (R0) or (C>MaxReadRetries); > if (R<ReadBuflen) then > SetLength(FBuffer,r); > end; > > It will then try 150 times, and then error out. > > BUT, reading this: > > http://msdn.microsoft.com/en-us/library/windows/desktop/ > ms740121%28v=vs.85%29.aspx > > "If the connection has been gracefully closed, the return value is zero." > > So, this means then thay the original implementation is correct. You just > need to catch the error using the OnRequestError handler... > > Sorry for my ignorance, but, would not it be better to read the HTTP data > only if the client is still connected or if read bytes > 0? > Yes and no. Yes in the sense that you have a more 'clean' experience. No, because it IS an error condition (client went away unexpectedly) and you should be aware of it. I will make it a property that is settable: 'IgnoreLostConnections' or so.

The 'IgnoreLostConnections' is a nice solution! :)

Silvio Clécio My public projects - github.com/silvioprog

Reply by email