Tobias Rapp wrote: > Hi, > > I am currently debugging some problems in my application using the > THttpServer/THttpConnection components regarding the support for HEAD > requests. As far as I understand the specs no response body should be > returned for HEAD but it seems that THttpConnection does send response > bodies in procedure ProcessPost() in case of 400/404/etc. answers.
Indeed it's a mess and buggy. I just looked at the source and this is my SVN patch (a bit lengthy), what do you (all) think?: {code} Index: OverbyteIcsHttpSrv.pas =================================================================== --- OverbyteIcsHttpSrv.pas (revision 891) +++ OverbyteIcsHttpSrv.pas (working copy) @@ -9,7 +9,7 @@ check for '..\', '.\', drive designation and UNC. Do the check in OnGetDocument and similar event handlers. Creation: Oct 10, 1999 -Version: 7.43 +Version: 7.44 EMail: francois.pie...@overbyte.be http://www.overbyte.be Support: Use the mailing list twsocket@elists.org Follow "support" link at http://www.overbyte.be for subscription. @@ -336,6 +336,11 @@ Feb 04, 2012 V7.43 Tobias Rapp added method AnswerStreamAcceptRange which is similar to AnswerStream however doesn't ignore requested content range. Use this method only for OK responses. +Feb 07, 2012 V7.44 Arno - The HEAD method *MUST NOT* return a message-body in + the response. Do not skip compression on HEAD requests, we + need to send the correct size. Method SendDocument + simplified and added two overloads. AnswerStreamAcceptRange + got an overload too. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} unit OverbyteIcsHttpSrv; @@ -421,8 +426,8 @@ OverbyteIcsWndControl, OverbyteIcsWSocket, OverbyteIcsWSocketS; const - THttpServerVersion = 743; - CopyRight : String = ' THttpServer (c) 1999-2012 F. Piette V7.43 '; + THttpServerVersion = 744; + CopyRight : String = ' THttpServer (c) 1999-2012 F. Piette V7.44 '; CompressMinSize = 5000; { V7.20 only compress responses within a size range, these are defaults only } CompressMaxSize = 5000000; MinSndBlkSize = 8192 ; { V7.40 } @@ -593,6 +598,7 @@ THttpConnection = class(TBaseHttpConnection) protected FHttpVerNum : Integer; { V1.6 } + FSendType : THttpSendType; { V7.44 } FPostRcvBuf : array [0..1023] of Byte; { V7.30 }{V7.39} FPostCounter : Int64; { V7.30 }{V7.39} {$IFNDEF NO_AUTHENTICATION_SUPPORT} @@ -746,6 +752,8 @@ constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure SendStream; virtual; + procedure SendDocument; overload; virtual; { V7.44 } + procedure SendDocument(const CustomHeaders: String); overload; virtual; { V7.44 } procedure SendDocument(SendType : THttpSendType); overload; virtual; procedure SendDocument(SendType : THttpSendType; const CustomHeaders: String); overload; virtual; { V7.29 } procedure SendHeader(Header : String); virtual; @@ -813,8 +821,12 @@ procedure AnswerStreamAcceptRange( var Flags : THttpGetFlag; const ContType : String; + LastModified : TDateTime = 0); overload; virtual; { V7.44 } + procedure AnswerStreamAcceptRange( + var Flags : THttpGetFlag; + const ContType : String; const Header : String; - LastModified : TDateTime = 0); virtual; { V7.43 } + LastModified : TDateTime = 0); overload; virtual; { V7.43 } procedure AnswerString(var Flags : THttpGetFlag; const Status : String; const ContType : String; @@ -2622,6 +2634,10 @@ OnDataSent := ConnectionDataSent; { V7.19 always need an event after header is sent } { The line we just received is HTTP command, parse it } ParseRequest; + if FMethod = 'HEAD' then { V7.44 } + FSendType := httpSendHead { V7.44 } + else { V7.44 } + FSendType := httpSendDoc; { V7.44 } { Next lines will be header lines } FState := hcHeader; FRequestHasContentLength := FALSE; @@ -2865,6 +2881,8 @@ if FServer.PersistentHeader <> '' then PutStringInSendBuffer (FServer.PersistentHeader); { V7.29 } PutStringInSendBuffer(#13#10); + if FSendType = httpSendHead then { V7.44 } + FDocStream.Size := 0; { V7.44 } SendStream; end; @@ -2915,6 +2933,17 @@ {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} { Only use this method for OK responses } +procedure THttpConnection.AnswerStreamAcceptRange( { V7.44 } + var Flags : THttpGetFlag; + const ContType : String; { if emtpy, defaults to text/html } + LastModified : TDateTime = 0); { zero => no Last-Modified header } +begin + AnswerStreamAcceptRange(Flags, ContType, '', LastModified); +end; + + +{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} +{ Only use this method for OK responses } procedure THttpConnection.AnswerStreamAcceptRange( { V7.43 } var Flags : THttpGetFlag; const ContType : String; { if emtpy, defaults to text/html } @@ -2992,6 +3021,8 @@ PutStringInSendBuffer (FServer.PersistentHeader); { V7.29 } PutStringInSendBuffer(GetKeepAliveHdrLines); PutStringInSendBuffer(#13#10); + if FSendType = httpSendHead then { V7.44 } + FDocStream.Size := 0; { V7.44 } SendStream; end; @@ -3203,8 +3234,11 @@ 'Content-Length: ' + _IntToStr(Length(Body)) + #13#10 + GetKeepAliveHdrLines + #13#10); - FAnswerStatus := 416; { V7.19 } - SendStr(Body); + FAnswerStatus := 416; { V7.19 } + if FSendType = httpSendHead then { V7.44 } + Send(nil, 0) { V7.44 } + else { V7.44 } + SendStr(Body); end; @@ -3223,7 +3257,10 @@ GetKeepAliveHdrLines + #13#10); FAnswerStatus := 404; { V7.19 } - SendStr(Body); + if FSendType = httpSendHead then { V7.44 } + Send(nil, 0) { V7.44 } + else { V7.44 } + SendStr(Body); end; @@ -3242,7 +3279,10 @@ GetKeepAliveHdrLines + #13#10); FAnswerStatus := 400; - SendStr(Body); + if FSendType = httpSendHead then { V7.44 } + Send(nil, 0) { V7.44 } + else { V7.44 } + SendStr(Body); end; @@ -3275,13 +3315,16 @@ '<BODY><H1>403 Forbidden</H1>The requested URL ' + TextToHtmlText(FPath) + ' is Forbidden on this server.<P></BODY></HTML>' + #13#10; - SendHeader(FVersion + ' 403 Forbidden' + #13#10 + - 'Content-Type: text/html' + #13#10 + - 'Content-Length: ' + _IntToStr(Length(Body)) + #13#10 + - GetKeepAliveHdrLines + - #13#10); + SendHeader(FVersion + ' 403 Forbidden' + #13#10 + + 'Content-Type: text/html' + #13#10 + + 'Content-Length: ' + _IntToStr(Length(Body)) + #13#10 + + GetKeepAliveHdrLines + + #13#10); FAnswerStatus := 403; { V7.19 } - SendStr(Body); + if FSendType = httpSendHead then { V7.44 } + Send(nil, 0) { V7.44 } + else { V7.44 } + SendStr(Body); end; @@ -3413,7 +3456,10 @@ *) Header := Header + #13#10; // Mark the end of header SendHeader(Header); - SendStr(Body); + if FSendType = httpSendHead then { V7.44 } + Send(nil, 0) { V7.44 } + else { V7.44 } + SendStr(Body); end; @@ -3429,7 +3475,10 @@ GetKeepAliveHdrLines + #13#10); FAnswerStatus := 501; { V7.19 } - SendStr(Body); + if FSendType = httpSendHead then { V7.44 } + Send(nil, 0) { V7.44 } + else { V7.44 } + SendStr(Body); end; @@ -3942,7 +3991,8 @@ { sending data (if required) } procedure THttpConnection.SendDocument(SendType : THttpSendType); begin - SendDocument(SendType, ''); + FSendType := SendType; // overwrites the default value for this request + SendDocument(''); end; @@ -3950,16 +4000,29 @@ procedure THttpConnection.SendDocument( SendType : THttpSendType; const CustomHeaders : String); +begin + FSendType := SendType; // overwrites the default value for this request + SendDocument(CustomHeaders); +end; + + +{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} +procedure THttpConnection.SendDocument; +begin + SendDocument(''); +end; + + +{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} +procedure THttpConnection.SendDocument(const CustomHeaders : String); var Header : String; NewDocStream : TStream; ProtoNumber : Integer; CompleteDocSize : THttpRangeInt; - ErrorSend : Boolean; SyntaxError : Boolean; ContEncoderHdr : String ; { V7.20 } begin - ErrorSend := FALSE; ProtoNumber := 200; FLastModified := FileDate(FDocument); FAnswerContentType := DocumentToContentType(FDocument); @@ -4001,17 +4064,12 @@ OnDataSent := ConnectionDataSent; ContEncoderHdr := ''; { V7.20 } - { Free and nil the stream because HEAD will not send current document } - if SendType = httpSendHead then begin - FDocStream.Free; { V7.38 } - FDocStream := nil; { V7.38 } - end - else begin - { V7.21 are we allowed to compress content } - if CheckContentEncoding(FAnswerContentType) then begin - ContEncoderHdr := DoContentEncoding; { V7.21 do it, returning new header } - FDocSize := FDocStream.Size; { stream is now smaller, we hope } - end; + { V7.44 Do not skip compression on HEAD requests, we need the correct size } + + { V7.21 are we allowed to compress content } + if CheckContentEncoding(FAnswerContentType) then begin + ContEncoderHdr := DoContentEncoding; { V7.21 do it, returning new header } + FDocSize := FDocStream.Size; { stream is now smaller, we hope } end; { Create Header } @@ -4026,22 +4084,10 @@ if CustomHeaders <> '' then Header := Header + CustomHeaders; { V7.29 } Header := Header + GetKeepAliveHdrLines + #13#10; - - { A HEAD response does not send content } - if SendType = httpSendHead then { V7.38 } - FDocSize := 0; { V7.38 } - - SendHeader(Header); - if not ErrorSend then begin - if FDocSize <= 0 then - Send(nil, 0); - if SendType = httpSendDoc then - SendStream - else - Send(nil, 0); { Added 15/04/02 } - end - else - Send(nil, 0); + SendHeader(Header); + if FSendType = httpSendHead then { V7.44 } + FDocStream.Size := 0; { V7.44 } + SendStream; end; @@ -4072,17 +4118,19 @@ FDataSent := 0; OnDataSent := ConnectionDataSent; -{ V7.40 speed up larger files by increasing buffer sizes } - if (FDocSize > FSndBlkSize) and (FServer.MaxBlkSize > FSndBlkSize) then begin - if (FDocSize >= FServer.MaxBlkSize) then - SetSndBlkSize (FServer.MaxBlkSize) - else - SetSndBlkSize (FDocSize); { don't need a max buffer } + if FDocSize > 0 then begin { 7.44 } + { V7.40 speed up larger files by increasing buffer sizes } + if (FDocSize > FSndBlkSize) and (FServer.MaxBlkSize > FSndBlkSize) then begin + if (FDocSize >= FServer.MaxBlkSize) then + SetSndBlkSize (FServer.MaxBlkSize) + else + SetSndBlkSize (FDocSize); { don't need a max buffer } + end; + if SocketSndBufSize < FSndBlkSize then + SocketSndBufSize := FSndBlkSize; { socket TCP buffer } + if not Assigned(FDocBuf) then + GetMem(FDocBuf, FSndBlkSize); end; - if SocketSndBufSize < FSndBlkSize then - SocketSndBufSize := FSndBlkSize; { socket TCP buffer } - if not Assigned(FDocBuf) then - GetMem(FDocBuf, FSndBlkSize); { event is called repeatedly until stream is all sent } ConnectionDataSent(Self, 0); {code} -- To unsubscribe or change your settings for TWSocket mailing list please goto http://lists.elists.org/cgi-bin/mailman/listinfo/twsocket Visit our website at http://www.overbyte.be