使用Indy的IDTCPClient从响应流中获取HTML

时间:2013-11-18 15:17:14

标签: delphi sockets indy delphi-xe5

我有一个以前员工创建的组件。它使用Indy(IDTCPClient)和以下方法来执行请求(其中“aReadHeader”是一个 在传入之前构建的预定义Uri。)

function TMyConnector.GET(aRawHeader: String): String;
begin
if Not Connected then Connected := True;
if Connected then
  begin
  FRawRequest :=  'GET /'+ aRawHeader + ' HTTP/'+HTTPVerText+#13#10+
                  'Host: '+FHost+#13#10+

                  'Cookie: UserHPos=IOGLO00003000090000C000BS; '+
                  'LOSID=qsBiy/wEDCq6tOXFzGbOlTD1lmo5AXdFnCkbzzPn6+qCeheYVyTcumRrjsqh+Hds4Fr2gZDazfDzGN1RA+nnHuQQeBy78ZUgctrZyyy9MnGl2qI/ulkV6EPxAfmmLg/lopRq99f5gAcG/dgtytAJjS+aD5DqtHGrAqjiqgtkwuA=; '+
                  'LoginHPos=IOGLO00003000090000C000BS; '+
                  'UIHPos=IOGLO00003000020000500003; '+
                  'LOSG=61939308-7C83-47ED-B909-2D2D10AD7026; '+
                  'fControllingBusiness=IOGLO000030000900001000050000200001'+#13#10+

                  'Connection: Close'+#13#10+
                  #13#10;

  FSock.Socket.Write(FRawRequest);
  FRawResponse := FSock.Socket.ReadLn(#13#10#13#10,nil);
  Result := FRawResponse;
  if ResponseStream = nil then ResponseStream := TMemoryStream.Create
    else ResponseStream.SetSize(0);
  FSock.Socket.ReadStream(ResponseStream,-1,True);
  if Connected and (Not KeepAlive) then Connected := False;
  end;
end;

质疑FRawResponse返回

  

HTTP / 1.0 200 OK内容长度:5560日期:星期一,2013年11月18日15:05:07   GMT内容类型:text / html; charset = UTF-8 ...,public

如何将这个html内容从ResponseStream实际获取到HTML

目前存在的一种方法是“GenerateJSON”(参见下面的代码)。我想创建一个名为“GenerateHTML”的

Function StreamToArray(Strm:TStream):TArray<Byte>;
Begin
 Strm.Position := 0;
 SetLength(Result,Strm.Size);
 Strm.Read(Result[0],Strm.Size);
End;


Procedure TMyConnector.GenerateJSON;
begin
 if ResponseStream <> nil then
  Begin
   ResponseJSON_V := TJSONObject.ParseJSONValue(StreamToArray(ResponseStream),0) as TJSONValue; // Note ResponseJSON_V is declared as TJSONValue in TMyConnector);
  End;
end;

所以,我需要

Procedure TMyConnector.GenerateHTML;
begin
 if ResponseStream <> nil then
  Begin
   // result:= html from stream here
  End;
end;

编辑:

Procedure TMyConnector.GenerateXML;
var
 S: String;
begin
if ResponseStream <> nil then
  Begin
   try
    while FSock.IOHandler.CheckForDataOnSource(30) do
    begin
       S := FSock.IOHandler.InputBufferAsString;
    end;
  finally
    ResponseStr_v:= S;
  end;
  End;
end;

1 个答案:

答案 0 :(得分:4)

以前的员工显然不知道Indy的TIdHTTP组件的存在,或者Indy的ReadStream()方法实际上是如何工作的,或者HTTP实际上是如何工作的。您应该重新编写函数以使用TIdHTTP并让它为您处理所有细节,例如:

function TMyConnector.GET(aRawHeader: String): String;
begin
  FHTTP.ProtocolVersion := ...; // pv1_0 or pv1_1

  // Indy has a TIdCookieManager component that can be attached to
  // the TIdHTTP.CookieManager property, which handles parsing,
  // tracking, and sending back cookies for you for each HTTP
  // request, so this should be re-written to utilize that
  // functionality...
  //
  FHTTP.Request.CustomHeaders.Values['Cookie'] := 'UserHPos=IOGLO00003000090000C000BS; '+
  'LOSID=qsBiy/wEDCq6tOXFzGbOlTD1lmo5AXdFnCkbzzPn6+qCeheYVyTcumRrjsqh+Hds4Fr2gZDazfDzGN1RA+nnHuQQeBy78ZUgctrZyyy9MnGl2qI/ulkV6EPxAfmmLg/lopRq99f5gAcG/dgtytAJjS+aD5DqtHGrAqjiqgtkwuA=; '+
  'LoginHPos=IOGLO00003000090000C000BS; '+
  'UIHPos=IOGLO00003000020000500003; '+
  'LOSG=61939308-7C83-47ED-B909-2D2D10AD7026; '+
  'fControllingBusiness=IOGLO000030000900001000050000200001';

  FHTTP.Request.Connection := 'Close';

  if ResponseStream = nil then
    ResponseStream := TMemoryStream.Create
  else
    ResponseStream.Size := 0;

  try
    try
      FHTTP.Get('http://' + FHost + '/' + aRawHeader, ResponseStream);
    finally
      FRawResponse := FHTTP.Response.RawHeaders.Text;
      Result := FRawResponse;
    end;
  except
    on E: EIdHTTPProtocolException do
    begin
      // HTTP error response. You can grab the error content if you need it...
      WriteStringToStream(ResponseStream, E.ErrorMessage);
    end;
    on E: Exception do
    begin
      // some other error, handle as needed...
    end;
  end;
end;

如果无法重写以使用TIdHTTP,则必须更新原始代码以实际实现基本的HTTP解析。仅仅调用ReadStream(),你必须知道 WHEN 来调用它,并且 WHAT 参数传递给它是不够的。尝试更像这样的东西:

function TMyConnector.GET(aRawHeader: String): String;
var
  Headers: TIdHeaderList;
  Size: integer;

  function InternalReadLn: String;
  begin
    Result := FSock.IOHandler.ReadLn;
    if FSock.IOHandler.ReadLnTimedout then begin
      raise EIdReadTimeout.Create('');
    end;
  end;

  function ChunkSize: integer;
  var
    j: Integer;
    s: string;
  begin
    s := InternalReadLn;
    j := Pos(';', s); {do not localize}
    if j > 0 then begin
      s := Copy(s, 1, j - 1);
    end;
    Result := StrToInt('$' + Trim(s), 0);
  end;

begin
  if Not Connected then Connected := True;
  if Connected then
  begin
    FRawRequest :=  'GET /'+ aRawHeader + ' HTTP/'+HTTPVerText+#13#10+
                    'Host: '+FHost+#13#10+

                    'Cookie: UserHPos=IOGLO00003000090000C000BS; '+
                    'LOSID=qsBiy/wEDCq6tOXFzGbOlTD1lmo5AXdFnCkbzzPn6+qCeheYVyTcumRrjsqh+Hds4Fr2gZDazfDzGN1RA+nnHuQQeBy78ZUgctrZyyy9MnGl2qI/ulkV6EPxAfmmLg/lopRq99f5gAcG/dgtytAJjS+aD5DqtHGrAqjiqgtkwuA=; '+
                    'LoginHPos=IOGLO00003000090000C000BS; '+
                    'UIHPos=IOGLO00003000020000500003; '+
                    'LOSG=61939308-7C83-47ED-B909-2D2D10AD7026; '+
                    'fControllingBusiness=IOGLO000030000900001000050000200001'+#13#10+

                    'Connection: Close'+#13#10+
                    #13#10;

    FSock.IOHandler.Write(FRawRequest);
    FRawResponse := FSock.IOHandler.ReadLn(#13#10#13#10,nil);
    Result := FRawResponse;

    if ResponseStream = nil then ResponseStream := TMemoryStream.Create
    else ResponseStream.Size := 0;

    Headers := TIdHeaderList.Create(QuoteHTTP);
    try
      Headers.Text := FRawResponse;

      if Pos('chunked', LowerCase(Headers.Values['Transfer-Encoding']) > 0 then
      begin
        Size := ChunkSize;
        while Size <> 0 do begin
          FSock.IOHandler.ReadStream(ResponseStream, Size);
          InternalReadLn;
          Size := ChunkSize;
        end;
        repeat until InternalReadLn = '';
      end
      else if Headers.IndexOfName('Content-Length') <> -1 then
      begin
        FSock.IOHandler.ReadStream(ResponseStream, StrToInt64(Headers.Values['Content-Length']), False);
      end
      else
        FSock.IOHandler.ReadStream(ResponseStream, -1, True);
    finally
      Headers.Free;
    end;

    if Connected and (Not KeepAlive) then Connected := False;
  end;
end;