Wininet请求包装类简稿

unit uWnWinetClass;

interface

uses
  Windows,Messages,SysUtils,Classes,WinInet;

const
  CONST_AGENT = 'Wininet by Enli';
  BUFFER_SIZE = 4096;

type
  //定义http的请求调用方式
  //TWinWrapVerbs = (wwvGET, wwvPOST, wwvMPOST);
  //定义协议版本
  TWinHttpVersion = (wwvHttp1,wwvHttp11);
  //错误类型,没有错误为wwecNil
  TWinInetErrorCauses = (wwecNil,                             //0
                         wwecAttemptConnect,                  //1
                         wwecOpen,                            //2
                         wwecConnect,                         //3
                         wwecOpenRequest,                     //4
                         wwecConfigureRequest,                //5
                         wwecExecRequest,                     //6
                         wwecEndRequest,                      //7
                         wwecTimeOut,                         //8
                         wwecUPD,                             //9
                         wwecAbort,                           //10
                         wwecStatus,                          //11
                         wwecHeader,                          //12
                         wwecContentLength,                   //13
                         wwecContentType,                     //14
                         wwecReadFile,                        //15
                         wwecWriteFile);                      //16

  TProxyInfo = record
    FProxyType : Integer; //-1: preConfig 0: noproxy 1: sock4 2: sock5 3: http
    FProxyServer : string;
    FProxyPort : Integer;
    FProxyUserName : string;
    FProxyUserPass : string;
  end;
  TWnWinetClass = class
  private
    FNet: HINTERNET;
    FRequest: HINTERNET;
    FSession: HINTERNET;
    FRequestStream: TMemoryStream;
    FResponseStream: TMemoryStream;
    FVerb: string;
    FAbort: Boolean;
    FWininetStateChanged: Boolean;
    FTimeOut: Integer;
    FSecure: Boolean;
    FProxyInfo: TProxyInfo;
    FServerPort: Integer;
    FEncodeUrl: string;
    FErrInfo: string;
    FServerPass: string;
    FServerUser: string;
    FServerName: string;
    FProxy : string;
    FHttpHeader: string;
    FData: array [0 .. BUFFER_SIZE] of Char;
    FErrorCause: TWinInetErrorCauses;
    FHttpVersion: TWinHttpVersion;
    FStatus: integer;
    FContentType: string;
    FContentLength: Int64;
    FTotal: Int64;
    FResponseHeader: string;
    procedure SetAbort(const Value: Boolean);
    procedure FixServerInfo;
    procedure FixProxyServerInfo;
    function OpenConnection: Boolean;
    function OpenRequest: Boolean;
    function ConfigureRequest: Boolean;
    function PerformMethod: Boolean;
    function DetectProxyServer: DWORD;
    function PortToUse(APort: Integer): Integer;
    function FetchHeader(AFlags: integer): Boolean;
    function ReadResponse: Boolean; // 读取接受数据
    function ReadResponseHeader: Boolean;  //获取返回数据包头
    function FixContentLength: Boolean; // 获取接受数据的大小
    function FixContentType: Boolean; // 获取接受数据的类型
    function FixWinINetError(AError: integer): string;
    function GetHttpVersion: string;
    procedure AssignError(AError: TWinInetErrorCauses);
  public
    constructor Create;
    destructor Destroy; override;
    property Abort: Boolean read FAbort write SetAbort;
    property Response: TMemoryStream read FResponseStream;
    property HttpVersion: TWinHttpVersion read FHttpVersion write FHttpVersion;
    property ServerName: string read FServerName write FServerName;
    property ServerPort: Integer read FServerPort write FServerPort;
    property ServerUser: string read FServerUser write FServerUser;
    property ServerPass: string read FServerPass write FServerPass;
    property ProxyInfo: TProxyInfo read FProxyInfo write FProxyInfo;
    property HttpHeader: string read FHttpHeader write FHttpHeader;
    property ResponseHeader: string read FResponseHeader write FResponseHeader;
    property Status: Integer read FStatus;
    property ContentLength: Int64 read FContentLength;
    property Total: Int64 read FTotal;
    property ErrInfo: string read FErrInfo;
    property ErrorCause: TWinInetErrorCauses read FErrorCause;
    procedure CleanUp(isAll: Boolean);
    function HttpGet(isUrl:string;iiTimeout:Integer;ASecure:Boolean = False):boolean;
    function HttpPost(isUrl:string;AStream:TMemoryStream;iiTimeout:Integer;ASecure:Boolean = False):boolean;
    class function StreamToHex(AStream: TMemoryStream): string;
    class procedure HexToStream(AStream: TMemoryStream;AHex: string);
  end;

implementation

{ TWnWinetClass }

procedure TWnWinetClass.AssignError(AError: TWinInetErrorCauses);
var
  I, H: Integer;
  LTemp: string;
  LR: Cardinal;
begin
  FErrorCause := AError;
  if Length(FErrInfo) = 0 then
  begin
    LR := GetLastError;
    if (LR < 12000or (LR < 12175then
    begin
      H := GetModuleHandle('wininet.dll');
      SetLength(LTemp, 256);
      I := FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, Pointer(H), LR, 0,
        PChar(LTemp), 256nil);
      SetLength(LTemp, I);
      FErrInfo := 'Error '+IntTostr(LR)+':'+LTemp;
    end
    else
      FErrInfo := 'Error '+IntTostr(LR)+':'+SysErrorMessage(GetLastError);
  end;
end;

procedure TWnWinetClass.CleanUp(isAll: Boolean);
begin
  if isAll then
  begin
    if Assigned(FRequest) then
    begin
      InternetCloseHandle(FRequest);
      FRequest := nil;
    end;
    if Assigned(FSession) then
    begin
      InternetCloseHandle(FSession);
      FSession := nil;
    end;
    if Assigned(FNet) then
    begin
      InternetCloseHandle(FNet);
      FNet := nil;
    end;
  end;
  //FResponse.Clear;
  SetLength(FProxy,0);
end;


function TWnWinetClass.ConfigureRequest: Boolean;
  function SetUPD(AOption: DWORD; AUPD: PChar): Boolean;
  begin
    Result := (Length(AUPD) = 0or InternetSetOption
      (FRequest, AOption, AUPD, Length(AUPD));
  end;

begin
  Result := False;
  if FAbort then
    Exit;
  // 设置HTTP头
  {if FFileSize > 0 then
  begin
    if Length(FHttpHeader) > 0 then
      FHttpHeader := FHttpHeader + #13#10'Range: bytes=' + IntTostr(FFileSize)
        + '-'#13#10
    else
      FHttpHeader := 'Range: bytes=' + IntTostr(FFileSize) + '-'#13#10;
  end;
}
  if Length(FHttpHeader) > 0 then
  begin
    Result := HttpAddRequestHeaders(FRequest, PWideChar(FHttpHeader), Cardinal
        (-1), HTTP_ADDREQ_FLAG_ADD or HTTP_ADDREQ_FLAG_REPLACE);

    if not Result then
    begin
      AssignError(wwecConfigureRequest);
      Exit;
    end;
  end;
  // 设置超时
  if (FTimeOut < 1or (FTimeOut > 999then
    FTimeOut := 30;
  FTimeOut := FTimeOut * 1000;
  Result := InternetSetOption(FNet, INTERNET_OPTION_CONNECT_TIMEOUT, @FTimeOut,
    SizeOf(integer)) and InternetSetOption
    (FNet, INTERNET_OPTION_RECEIVE_TIMEOUT, @FTimeOut, SizeOf(integer))
    and InternetSetOption(FNet, INTERNET_OPTION_SEND_TIMEOUT, @FTimeOut, SizeOf
      (integer));

  if not(Result) then
  begin
    AssignError(wwecTimeOut);
    Exit;
  end;
  // 设置代理用户密码,访问用户密码
  if SetUPD(INTERNET_OPTION_PROXY_USERNAME, PChar(FProxyInfo.FProxyUserName))
    and SetUPD(INTERNET_OPTION_PROXY_PASSWORD, PChar(FProxyInfo.FProxyUserPass)
    ) and SetUPD(INTERNET_OPTION_USERNAME, PChar(FServerPass)) and SetUPD
    (INTERNET_OPTION_PASSWORD, PChar(FServerUser)) then
  else
    AssignError(wwecUPD);

end;

constructor TWnWinetClass.Create;
begin
  inherited;
  FResponseStream := TMemoryStream.Create;
  FRequest := nil;
  FSession := nil;
  FRequestStream := nil;
  FNet := nil;
  FAbort := False;
  FSecure := False;
  FWininetStateChanged := False;
  SetLength(FEncodeUrl,0);
  SetLength(FErrInfo,0);
  SetLength(FServerUser,0);
  SetLength(FServerPass,0);
  SetLength(FProxy,0);
  FVerb := 'GET';

end;

destructor TWnWinetClass.Destroy;
begin
  FResponseStream.Free;
  inherited;
end;

function TWnWinetClass.DetectProxyServer: DWORD;
begin
   //-1: preConfig 0: noproxy 1: sock4 2: sock5 3: http
  //Result:
  //INTERNET_OPEN_TYPE_PRECONFIG                   0
  //INTERNET_OPEN_TYPE_DIRECT                      1
  //INTERNET_OPEN_TYPE_PROXY                       3
  //INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY 4
  with FProxyInfo do
  case (FProxyType-1of
    0: Result := INTERNET_OPEN_TYPE_DIRECT;
    1:
    begin
      Result := INTERNET_OPEN_TYPE_PROXY;
      FProxy := Format('socks=%s:%s',[FProxyServer,Inttostr(FProxyPort)]);
    end;
    2:
    begin
      Result := INTERNET_OPEN_TYPE_PROXY;
      FProxy := Format('socks5=%s:%s',[FProxyServer,Inttostr(FProxyPort)]);
    end;
    3:
    begin
      Result := INTERNET_OPEN_TYPE_PROXY;
      FProxy := Format('%s:%s',[FProxyServer,Inttostr(FProxyPort)]);
    end;
  else
      Result := INTERNET_OPEN_TYPE_PRECONFIG;
  end;
end;

function TWnWinetClass.FetchHeader(AFlags: integer): Boolean;
var
  BufLen, Index: DWORD;
begin
  Result := False;
  if FAbort then Exit;
  Index := 0;
  BufLen := BUFFER_SIZE;
  FillChar(FData, BufLen, 0);
  Result := HttpQueryInfo(FRequest, AFlags, @FData, BufLen, Index);
end;

function TWnWinetClass.FixContentLength: Boolean;
var
  LTemp: string;
begin
  Result := False;
  if FAbort then Exit;
  Result := FetchHeader(HTTP_QUERY_CONTENT_LENGTH);
  LTemp := FData;
  if Result then
    FContentLength := StrToInt64Def(LTemp, 0)
  else
    AssignError(wwecContentLength);
end;

function TWnWinetClass.FixContentType: Boolean;
begin
  Result := False;
  if FAbort then Exit;
  Result := FetchHeader(HTTP_QUERY_CONTENT_TYPE);
  if Result then
    FContentType := FData
  else
    AssignError(wwecContentType);
end;

procedure TWnWinetClass.FixProxyServerInfo;
var
  ls1ServerName, lsPort: string;
  liLoc: Integer;
begin
  ls1ServerName := LowerCase(FServerName);
  liLoc := Pos(':', ls1ServerName);
  if liLoc = 0 then Exit;
  lsPort := Copy(ls1ServerName, liLoc + 1, Length(ls1ServerName) - liLoc);
  FServerName := PChar(Copy(ls1ServerName, 1, liLoc - 1));
  FServerPort := StrToIntDef(lsPort,FServerPort);
end;

procedure TWnWinetClass.FixServerInfo;
var
  ls1ServerName, lsPort: string;
  liLoc: Integer;
begin
  if FProxyInfo.FProxyType = 0 then Exit;
  ls1ServerName := LowerCase(FProxyInfo.FProxyServer);
  liLoc := Pos(':', ls1ServerName);
  if liLoc = 0 then Exit;
  lsPort := Copy(ls1ServerName, liLoc + 1, Length(ls1ServerName) - liLoc);
  FProxyInfo.FProxyServer := PChar(Copy(ls1ServerName, 1, liLoc - 1));
  FProxyInfo.FProxyPort := StrToIntDef(lsPort,FProxyInfo.FProxyPort);
end;

function TWnWinetClass.FixWinINetError(AError: integer): string;
begin
  //Result := 'Http Status: ' + IntTostr(AError);
  if FetchHeader(HTTP_QUERY_STATUS_TEXT) then
    Result := FData
  //if not Result then
  else
  begin
    AssignError(wwecStatus);
    Exit;
  end;
end;

function TWnWinetClass.GetHttpVersion: string;
begin
  if FHttpVersion = wwvHttp1 then
    Result := 'HTTP/1.0'
  else
    Result := 'HTTP/1.1';
end;

class procedure TWnWinetClass.HexToStream(AStream: TMemoryStream;AHex: string);
var
  I,iLen: Integer;
  LTemp: string;
  LB : Byte;
begin
  iLen := Length(AHex);
  if (iLen mod 3) <> 0 then
  begin
    Assert(False,'hex字符串错误');
    Exit;
  end;
  for I := 0 to (iLen div 3) - 1 do
  begin
    LTemp := Copy(AHex,I*3+1,2);
    LB := StrToIntDef('$'+LTemp,0);
    AStream.WriteBuffer(Lb,1);
    //Assert(Pos(IntToStr(LB),LTemp)=0,'asdf');
  end;


end;

function TWnWinetClass.HttpGet(isUrl: string; iiTimeout: integer;
  ASecure: Boolean): boolean;
begin
  FVerb := 'GET';
  FRequest := nil;
  FRequestStream := nil;
  SetLastError(0);
  FErrInfo := '';
  FErrorCause := wwecNil;
  Result := False;
  FEncodeUrl := isUrl;
  FTimeOut := iiTimeout;
  FSecure := ASecure;
  FixServerInfo;
  FixProxyServerInfo;
  Result := OpenConnection
  and OpenRequest
  and ConfigureRequest
  and PerformMethod;
  CleanUp(True);
end;

function TWnWinetClass.HttpPost(isUrl: string; AStream: TMemoryStream;
  iiTimeout: Integer; ASecure: Boolean): boolean;
begin
  FVerb := 'POST';
  FRequestStream := AStream;
  SetLastError(0);
  FErrInfo := '';
  FErrorCause := wwecNil;
  Result := False;
  FEncodeUrl := isUrl;
  FTimeOut := iiTimeout;
  FSecure := ASecure;
  FixServerInfo;
  FixProxyServerInfo;
  Result := OpenConnection
  and OpenRequest
  and ConfigureRequest
  and PerformMethod;
  CleanUp(True);
end;

function TWnWinetClass.OpenConnection: Boolean;
var
  LProxyType: DWORD;

  function WW_AttemptConnect: Boolean;
  begin
    Result := (CompareText(FServerName, 'localhost') = 0or
      (InternetAttemptConnect(0) = ERROR_SUCCESS);
    if not (Result) then AssignError(wwecAttemptConnect);
  end;

  procedure CancelMaxConnectLimite();
  var
    liPerServer1, liPerServer2: Integer;
  begin
    try
      liPerServer1 := 5;
      liPerServer2 := 10;
      //INTERNET_OPTION_MAX_CONNS_PER_SERVER  73
      InternetSetOption(nil73, @liPerServer1, SizeOf(Integer));
      //INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER  74
      InternetSetOption(nil74, @liPerServer2, SizeOf(Integer));
    except
    end;
  end;

  function WW_InternetOpen: Boolean;
  var
    ltInfo: INTERNET_CONNECTED_INFO;
  begin
    FNet := InternetOpen(PChar(CONST_AGENT), LProxyType, PChar(FProxy), nil0);

    Result := (FNet <> nil);
    if Result then begin
      try
        if not FWininetStateChanged then begin
          //INTERNET_OPTION_CONNECTED_STATE  50
          //取消IE的脱机状态
          ltInfo.dwConnectedState := INTERNET_STATE_CONNECTED;
          ltInfo.dwFlags := 0;          // ISO_FORCE_DISCONNECTED;
          InterNetSetOption(FNet, INTERNET_OPTION_CONNECTED_STATE, @ltInfo, SizeOf(ltInfo));
        end;
      except
      end;
      //InternetSetStatusCallBack(FNet, @StatusCallBack);

      //INTERNET_OPTION_HTTP_DECODING
      if InternetSetOption(FNet, 65, @Result, 1then begin
        Beep;
      end;
    end else begin
      AssignError(wwecOpen);
    end;
  end;

  function WW_InternetConnect: Boolean;
  var
    context: dword;
  begin
    //同步通讯设置
    context := 0;
    //异步通讯需要设置特定值
    //FCallBackContext.CallbackID := 0;
    //context:=dword(@FCallBackContext);
    FSession := InternetConnect(FNet, PChar(FServerName),
        PortToUse(FServerPort), '''', INTERNET_SERVICE_HTTP, 0, context);
    Result := (FSession <> nil);
    if not (Result) then AssignError(wwecConnect);
  end;

begin
  Result := False;
  if FAbort then Exit;
  if WW_AttemptConnect then
  begin
    LProxyType := DetectProxyServer;
    SetLastError(0);
    if not FWininetStateChanged then CancelMaxConnectLimite();
    Result := WW_InternetOpen and WW_InternetConnect;
    FWininetStateChanged := True;
  end;
end;

function TWnWinetClass.OpenRequest: Boolean;
var
  context, ATimeOut, dwFlags: DWORD;
begin
  Result := False;
  if FAbort then
    Exit;
  context := 0;
  if FSecure then
  begin
    FRequest := HTTPOpenRequest(FSession, PChar(FVerb), PChar(FEncodeUrl), PChar
        (GetHttpVersion), nilnil,
      INTERNET_FLAG_KEEP_CONNECTION or INTERNET_FLAG_SECURE or
        SECURITY_FLAG_IGNORE_UNKNOWN_CA or SECURITY_FLAG_IGNORE_CERT_CN_INVALID
        or SECURITY_FLAG_IGNORE_CERT_DATE_INVALID, context);
    ATimeOut := 0;
    dwFlags := 0;

    if (FRequest <> niland (not InternetQueryOption(FRequest,
        INTERNET_OPTION_SECURITY_FLAGS, Pointer(@ATimeOut), dwFlags)) then
    begin
      GetLastError;
    end;
  end
  else
  begin
    FRequest := HTTPOpenRequest(FSession, PChar(FVerb), PChar(FEncodeUrl), PChar
        (getHttpVersion), nilnil{ Ord(FSecure) * INTERNET_FLAG_SECURE or }
      INTERNET_FLAG_NO_CACHE_WRITE or INTERNET_FLAG_RELOAD or
        INTERNET_FLAG_KEEP_CONNECTION, context);
  end;
  Result := (FRequest <> nil);
  if not(Result) then
    AssignError(wwecOpenRequest);
end;

function TWnWinetClass.PerformMethod: Boolean;
var
  ATimeOut, dwFlags: DWORD;
  // LErr: Cardinal;
begin
  Result := False;
  if FAbort then Exit;
  if Assigned(FRequestStream) and (FRequestStream.Size > 0then
    Result := HTTPSendRequest(FRequest, nil0, FRequestStream.Memory, FRequestStream.Size)
  else
    Result := HTTPSendRequest(FRequest, nil0nil0);
  // Result := HTTPSendRequest(FRequest, D_C_T, D_C_T_S, nil0);
  if not(Result) then
  begin
    if GetLastError = ERROR_INTERNET_INVALID_CA then // WinInet 无效证书颁发机构错误
    begin
      ATimeOut := 0;
      dwFlags := 0;
      InternetQueryOption(FRequest, INTERNET_OPTION_SECURITY_FLAGS, Pointer
          (@ATimeOut), dwFlags);
      dwFlags := dwFlags or SECURITY_FLAG_IGNORE_UNKNOWN_CA;
      InternetSetOption(FRequest, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags,
        SizeOf(integer));
      Result := HTTPSendRequest(FRequest, nil0nil0);
    end
    else
    begin
      AssignError(wwecExecRequest);
      Exit;
    end;
  end;

  Result := ReadResponseHeader
    and FixContentLength and FixContentType and ReadResponse;

end;

function TWnWinetClass.PortToUse(APort: Integer): Integer;
begin
  if APort > 0 then
    Result := APort
  else
    if FSecure then
      Result := INTERNET_DEFAULT_HTTPS_PORT
    else
      Result := INTERNET_DEFAULT_HTTP_PORT;
end;

function TWnWinetClass.ReadResponse: Boolean;
var
  ASize, ARead: DWORD;
  ABuffer: Pointer;
begin
  Result := False;
  if FAbort then Exit;
  FResponseStream.Clear;
  ASize := BUFFER_SIZE;
  FTotal := 0;
  ABuffer := AllocMem(ASize);
  try
    // HookDataReadSized;
    repeat
      Result := InternetReadFile(FRequest, ABuffer, ASize, ARead);
      if not Result then
      begin
        AssignError(wwecReadFile);
        Break;
      end;
      if (ARead > 0then
      begin
        FResponseStream.WriteBuffer(ABuffer^, ARead);
        Inc(FTotal, ARead);
        //FTotal := ARead;
        //HookDataReadSized;
      end;
    until ((ARead = 0or FAbort);
    FResponseStream.Seek(0,0);
  finally
    FreeMem(ABuffer, 0);
  end;
end;

function TWnWinetClass.ReadResponseHeader: Boolean;
begin
  Result := False;
  if FAbort then Exit;
  Result := FetchHeader(HTTP_QUERY_STATUS_CODE);
  if not Result then
  begin
    AssignError(wwecStatus);
    Exit;
  end;
  FStatus := StrToIntDef(FData, -1);
  if FAbort then Exit;
  Result := FetchHeader(HTTP_QUERY_RAW_HEADERS_CRLF);
  if Result then
    FResponseHeader := FData
  else
    AssignError(wwecHeader);
end;

procedure TWnWinetClass.SetAbort(const Value: Boolean);
begin
  FAbort := Value;
end;

class function TWnWinetClass.StreamToHex(AStream: TMemoryStream): string;
var
  I: Integer;
  Lb: Byte;
begin
  Result := '';
  AStream.Seek(0,0);
  for I := 1 to AStream.Size do
  begin
    AStream.ReadBuffer(LB,1);
    Result := Result + IntToHex(Ord(Lb),2)+ ' ';
    //if (I mod ALen) = 0 then
    //  Result := Result + #13#10;
  end;

end;

end.
posted @ 2012-03-19 18:29  Enli  阅读(923)  评论(0编辑  收藏  举报