Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | some code refactoring |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
59de047ba40294f6717b84748d517f56 |
User & Date: | ab 2011-04-18 14:00:00 |
2011-04-18
| ||
15:35 | new TSQLite3HttpClientWinHTTP class, using WinHTTP API (fast and stable), is therefore now the default TSQLite3HttpClient class check-in: d8e6e2e17e user: ab tags: trunk | |
14:00 | some code refactoring check-in: 59de047ba4 user: ab tags: trunk | |
13:12 |
| |
Changes to SQLite3/SQLite3HttpClient.pas.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 ... 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 ... 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 ... 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 ... 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
and Server over HTTP/1.1 - there will be no speed penalty on the server side, whereas deflate would use much more CPU - by default, will handle SynLZ compression for TSQLite3HttpServer - can make TCP/IP stream not HTTP compliant (against antivirus slowdown) - new TSQLite3HttpClientWinINet class, using WinINet API (very slow) - new TSQLite3HttpClientWinHTTP class, using WinHTTP API (fast and stable): this class should be considered to be used instead of TSQLite3HttpClient for any HTTP/1.1 client connection } interface {$define COMPRESSSYNLZ} { if defined, will use SynLZ for content compression - SynLZ is much faster than deflate/zip, so is preferred ................................................................................ - not defined by default - should be set globally to the project conditionals } {$define USEWININET} // define this to define TSQLite3HttpClientWinHTTP or TSQLite3HttpClientWinINet // - TSQLite3HttpClientWinHTTP was found out to be the faster implementation uses Windows, SysUtils, {$ifdef COMPRESSDEFLATE} SynZip, {$endif} {$ifdef COMPRESSSYNLZ} SynLZ, {$endif} ................................................................................ destructor Destroy; override; end; {$ifdef USEWININET} /// HTTP/1.1 RESTFUL JSON SQLite3 Client abstract class using either WinINet // or TWinHTTP API // - not to be called directly, but via TSQLite3HttpClientWinINet or (even // prefered) TSQLite3HttpClientWinHTTP TSQLite3HttpClientWinGeneric = class(TSQLite3HttpClientGeneric) protected fWinAPI: TWinHttpAPI; /// call fWinAPI.Request() procedure InternalCreate(const aServer, aPort: AnsiString; aHttps: boolean); virtual; abstract; function InternalRequest(const url, method: RawUTF8; var Header, Data, DataType: RawUTF8): Int64Rec; override; ................................................................................ TSQLite3HttpClientWinINet = class(TSQLite3HttpClientWinGeneric) protected procedure InternalCreate(const aServer, aPort: AnsiString; aHttps: boolean); override; end; /// HTTP/1.1 RESTFUL JSON SQLite3 Client class using WinHTTP API // - has a common behavior as TSQLite3HttpClient and seems to be faster // (especially over a network) and will retrieve the current proxy settings // (if available) - so it seems to be the class to use in your programs // - this class is able to connect via the secure HTTPS protocol // - by design, the WinHTTP API can be used from a service or a server TSQLite3HttpClientWinHTTP = class(TSQLite3HttpClientWinGeneric) protected procedure InternalCreate(const aServer, aPort: AnsiString; aHttps: boolean); override; end; {$endif} implementation { TSQLite3HttpClientGeneric } function TSQLite3HttpClientGeneric.URI(const url, method: RawUTF8; Resp, ................................................................................ end; destructor TSQLite3HttpClient.Destroy; begin fSocket.Free; inherited Destroy; end; function TSQLite3HttpClient.InternalRequest(const url, method: RawUTF8; var Header, Data, DataType: RawUTF8): Int64Rec; begin result.Lo := fSocket.Request(url,method,KeepAliveMS,Header,Data,DataType,false); result.Hi := GetCardinal(pointer(fSocket.HeaderValue('Server-InternalState'))); Header := fSocket.HeaderGetText; |
| > > > > | | > < |
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 ... 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 ... 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 ... 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 ... 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
and Server over HTTP/1.1 - there will be no speed penalty on the server side, whereas deflate would use much more CPU - by default, will handle SynLZ compression for TSQLite3HttpServer - can make TCP/IP stream not HTTP compliant (against antivirus slowdown) - new TSQLite3HttpClientWinINet class, using WinINet API (very slow) - new TSQLite3HttpClientWinHTTP class, using WinHTTP API (fast and stable): this class should be considered to be used instead of TSQLite3HttpClient for any HTTP/1.1 client connection over a network } interface {$define COMPRESSSYNLZ} { if defined, will use SynLZ for content compression - SynLZ is much faster than deflate/zip, so is preferred ................................................................................ - not defined by default - should be set globally to the project conditionals } {$define USEWININET} // define this to define TSQLite3HttpClientWinHTTP or TSQLite3HttpClientWinINet // - TSQLite3HttpClientWinHTTP was found out to be the faster implementation uses {$ifdef MSWINDOWS} Windows, {$else} {$undef USEWININET} {$endif} SysUtils, {$ifdef COMPRESSDEFLATE} SynZip, {$endif} {$ifdef COMPRESSSYNLZ} SynLZ, {$endif} ................................................................................ destructor Destroy; override; end; {$ifdef USEWININET} /// HTTP/1.1 RESTFUL JSON SQLite3 Client abstract class using either WinINet // or TWinHTTP API // - not to be called directly, but via TSQLite3HttpClientWinINet or (even // better) TSQLite3HttpClientWinHTTP TSQLite3HttpClientWinGeneric = class(TSQLite3HttpClientGeneric) protected fWinAPI: TWinHttpAPI; /// call fWinAPI.Request() procedure InternalCreate(const aServer, aPort: AnsiString; aHttps: boolean); virtual; abstract; function InternalRequest(const url, method: RawUTF8; var Header, Data, DataType: RawUTF8): Int64Rec; override; ................................................................................ TSQLite3HttpClientWinINet = class(TSQLite3HttpClientWinGeneric) protected procedure InternalCreate(const aServer, aPort: AnsiString; aHttps: boolean); override; end; /// HTTP/1.1 RESTFUL JSON SQLite3 Client class using WinHTTP API // - has a common behavior as TSQLite3HttpClient and seems to be faster // over a network, and will retrieve the current proxy settings // (if available) - so it seems to be the class to use in your programs // - this class is able to connect via the secure HTTPS protocol // - by design, the WinHTTP API can be used from a service or a server TSQLite3HttpClientWinHTTP = class(TSQLite3HttpClientWinGeneric) protected procedure InternalCreate(const aServer, aPort: AnsiString; aHttps: boolean); override; end; {$endif} implementation { TSQLite3HttpClientGeneric } function TSQLite3HttpClientGeneric.URI(const url, method: RawUTF8; Resp, ................................................................................ end; destructor TSQLite3HttpClient.Destroy; begin fSocket.Free; inherited Destroy; end; function TSQLite3HttpClient.InternalRequest(const url, method: RawUTF8; var Header, Data, DataType: RawUTF8): Int64Rec; begin result.Lo := fSocket.Request(url,method,KeepAliveMS,Header,Data,DataType,false); result.Hi := GetCardinal(pointer(fSocket.HeaderValue('Server-InternalState'))); Header := fSocket.HeaderGetText; |
Changes to SynCrtSock.pas.
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 ... 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 ... 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 ... 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 ... 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 .... 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 .... 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 .... 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 .... 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 .... 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 .... 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 .... 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 .... 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 |
/// the event handled called by the default implementation of the // virtual Request method // - warning: this process must be thread-safe (can be called by several // threads simultaneously) property OnRequest: TOnHttpServerRequest read fOnRequest write fOnRequest; end; /// the fastest unicode string available {$ifndef UNICODE} UnicodeString = WideString; {$endif} {{ HTTP server using fast http.sys kernel-mode server - The HTTP Server API enables applications to communicate over HTTP without using Microsoft Internet Information Server (IIS). Applications can register to receive HTTP requests for particular URLs, receive HTTP requests, and send HTTP responses. The HTTP Server API includes SSL support so that applications can exchange data over secure HTTP connections without IIS. It is also designed to work with I/O completion ports. ................................................................................ applications running simultaneously. } THttpApiServer = class(THttpServerGeneric) protected /// the internal request queue fReqQueue: THandle; /// contain clones list fClones: TObjectList; /// list of all registered URL fRegisteredUrl: array of UnicodeString; /// server main loop - don't change directly // - will call the Request public virtual method with the appropriate // parameters to retrive the content procedure Execute; override; /// create a clone constructor CreateClone(From: THttpApiServer); public ................................................................................ fCompressHeader: THttpSocketCompressSet; /// used for internal connection fSession, fConnection, fRequest: HINTERNET; procedure InternalConnect; virtual; abstract; procedure InternalRequest(const method, aURL: TSockData); virtual; abstract; procedure InternalCloseRequest; virtual; abstract; procedure InternalAddHeader(const hdr: TSockData); virtual; abstract; function InternalSendRequest(const aData: TSockData; var OutHeader, OutData, OutDataEncoding, InAcceptEncoding: TSockData): integer; virtual; abstract; public /// connect to http://aServer:aPort or https://aServer:aPort constructor Create(const aServer, aPort: AnsiString; aHttps: boolean); /// low-level HTTP/1.1 request // - after an Create(server,port), return 200,202,204 if OK, // http status error otherwize function Request(const url, method: TSockData; KeepAlive: cardinal; ................................................................................ TWinINet = class(TWinHttpAPI) protected // those internal methods will raise an EWinINet exception on error procedure InternalConnect; override; procedure InternalRequest(const method, aURL: TSockData); override; procedure InternalCloseRequest; override; procedure InternalAddHeader(const hdr: TSockData); override; function InternalSendRequest(const aData: TSockData; var OutHeader, OutData, OutDataEncoding, InAcceptEncoding: TSockData): integer; override; public /// relase the connection destructor Destroy; override; end; /// WinINet exception type EWinINet = class(Exception) ................................................................................ TWinHTTP = class(TWinHttpAPI) protected // those internal methods will raise an EWinINet exception on error procedure InternalConnect; override; procedure InternalRequest(const method, aURL: TSockData); override; procedure InternalCloseRequest; override; procedure InternalAddHeader(const hdr: TSockData); override; function InternalSendRequest(const aData: TSockData; var OutHeader, OutData, OutDataEncoding, InAcceptEncoding: TSockData): integer; override; public /// relase the connection destructor Destroy; override; end; /// type of a TWinHttpAPI class TWinHttpAPIClass = class of TWinHttpAPI; ................................................................................ const HttpNames: array[0..9] of PChar = ( 'HttpInitialize','HttpTerminate','HttpCreateHttpHandle', 'HttpAddUrl', 'HttpRemoveUrl', 'HttpReceiveHttpRequest', 'HttpSendHttpResponse', 'HttpReceiveRequestEntityBody', 'HttpSetServiceConfiguration', 'HttpDeleteServiceConfiguration'); function RegURL(aRoot, aPort: TSockData; Https: boolean; aDomainName: TSockData): UnicodeString; const Prefix: array[boolean] of TSockData = ('http://','https://'); begin if aPort='' then aPort := '80'; aRoot := trim(aRoot); aDomainName := trim(aDomainName); if aDomainName='' then begin ................................................................................ if aRoot[1]<>'/' then insert('/',aRoot,1); if aRoot[length(aRoot)]<>'/' then aRoot := aRoot+'/'; end else aRoot := '/'; // allow for instance 'http://*:2869/' aRoot := Prefix[Https]+aDomainName+':'+aPort+aRoot; result := UnicodeString(aRoot); end; function THttpApiServer.AddUrl(const aRoot, aPort: TSockData; Https: boolean; const aDomainName: TSockData): integer; var s: UnicodeString; n: integer; begin result := -1; if (Self=nil) or (fReqQueue=0) or (Http.Module=0) then exit; s := RegURL(aRoot, aPort, Https, aDomainName); if s='' then ................................................................................ inherited CreateFmt('%s failed: %s (%d)', [HttpNames[NameIndex],SysErrorMessage(Error),Error]) end; class function THttpApiServer.AddUrlAuthorize(const aRoot, aPort: TSockData; Https: boolean; const aDomainName: TSockData; OnlyDelete: boolean): string; var prefix: UnicodeString; Error: HRESULT; Config: HTTP_SERVICE_CONFIG_URLACL_SET; begin try HttpApiInitialize; prefix := RegURL(aRoot, aPort, Https, aDomainName); if prefix='' then ................................................................................ begin result := RegisterCompressFunc(fCompress,aFunction,fCompressAcceptEncoding)<>''; end; function TWinHttpAPI.Request(const url, method: TSockData; KeepAlive: cardinal; const InHeader, InData, InDataType: TSockData; out OutHeader, OutData: TSockData): integer; var aData, aDataEncoding, aAccceptEncoding, aURL: TSockData; i: integer; begin if (url='') or (url[1]<>'/') then aURL := '/'+url else // need valid url according to the HTTP/1.1 RFC aURL := url; fKeepAlive := KeepAlive; InternalRequest(method,aURL); // should raise an exception on error ................................................................................ InDataType,aData); if aDataEncoding<>'' then InternalAddHeader(TSockData('Content-Encoding: ')+aDataEncoding); end; if fCompressAcceptEncoding<>'' then InternalAddHeader(fCompressAcceptEncoding); // send request to remote server result := InternalSendRequest(aData,OutHeader,OutData,aDataEncoding,aAccceptEncoding); // handle incoming answer compression if OutData<>'' then begin if aDataEncoding<>'' then for i := 0 to high(fCompress) do with fCompress[i] do if Name=aDataEncoding then if Func(AnsiString(OutData),false)='' then raise ECrtSocket.CreateFmt('%s uncompress',[Name]) else break; // successfully uncompressed content if aAccceptEncoding<>'' then fCompressHeader := SetCompressHeader(fCompress,pointer(aAccceptEncoding)); end; finally InternalCloseRequest; end; end; ................................................................................ InternetCloseHandle(FSession); inherited; end; procedure TWinINet.InternalAddHeader(const hdr: TSockData); begin if (hdr<>'') and not HttpAddRequestHeadersA(FRequest, Pointer(hdr),length(hdr),HTTP_ADDREQ_FLAG_COALESCE) then EWinINet.Create; end; procedure TWinINet.InternalCloseRequest; begin if FRequest<>nil then begin InternetCloseHandle(FRequest); FRequest := nil; end; end; procedure TWinINet.InternalConnect; begin FSession := InternetOpenA(DEFAULT_AGENT,INTERNET_OPEN_TYPE_PRECONFIG,nil,nil,0); if FSession=nil then raise EWinINet.Create; FConnection := InternetConnectA(FSession,pointer(FServer),FPort,nil,nil, INTERNET_SERVICE_HTTP,0,0); if FConnection=nil then raise EWinINet.Create; end; const ALL_ACCEPT: array[0..1] of PAnsiChar = ('*/*',nil); procedure TWinINet.InternalRequest(const method, aURL: TSockData); var Flags: DWORD; begin Flags := INTERNET_FLAG_HYPERLINK or INTERNET_FLAG_PRAGMA_NOCACHE or INTERNET_FLAG_RESYNCHRONIZE; // options for a true RESTful request if FKeepAlive<>0 then ................................................................................ Flags := Flags or INTERNET_FLAG_KEEP_CONNECTION; FRequest := HttpOpenRequestA(FConnection, Pointer(method), Pointer(aURL), nil, nil, @ALL_ACCEPT, Flags,0); if FRequest=nil then EWinINet.Create; end; function TWinINet.InternalSendRequest(const aData: TSockData; var OutHeader, OutData, OutDataEncoding, InAcceptEncoding: TSockData): integer; function GetInfo(Info: DWORD): TSockData; var dwSize, dwIndex: DWORD; begin result := ''; dwSize := 0; dwIndex := 0; if not HttpQueryInfoA(fRequest,Info,nil,dwSize,dwIndex) and (GetLastError=ERROR_INSUFFICIENT_BUFFER) then begin SetLength(result,dwSize-1); if not HttpQueryInfoA(fRequest,Info,pointer(result),dwSize,dwIndex) then result := ''; end; end; function GetInfo32(Info: DWORD): DWORD; var dwSize, dwIndex: DWORD; begin dwSize := sizeof(result); dwIndex := 0; Info := Info or HTTP_QUERY_FLAG_NUMBER; if not HttpQueryInfoA(fRequest,Info,@result,dwSize,dwIndex) then result := 0; end; var Bytes, DataLen, Read: DWORD; begin // send request to remote server if not HttpSendRequestA(fRequest,nil,0,pointer(aData),length(aData)) then EWinINet.Create; // retrieve status and headers result := GetInfo32(HTTP_QUERY_STATUS_CODE); OutHeader := GetInfo(HTTP_QUERY_RAW_HEADERS_CRLF); // retrieve body content OutData := ''; DataLen := GetInfo32(HTTP_QUERY_CONTENT_LENGTH); if DataLen<>0 then begin SetLength(OutData,DataLen); Read := 0; repeat if InternetReadFile(fRequest,@PByteArray(OutData)[Read],DataLen-Read,Bytes) then if Bytes=0 then begin SetLength(OutData,Read); break; end else inc(Read,Bytes) else raise EWinINet.Create; until Read=DataLen; OutDataEncoding := GetInfo(HTTP_QUERY_CONTENT_ENCODING); InAcceptEncoding := GetInfo(HTTP_QUERY_ACCEPT_ENCODING); end; end; { TWinHTTP } const winhttpdll = 'winhttp.dll'; WINHTTP_ACCESS_TYPE_DEFAULT_PROXY = 0; WINHTTP_FLAG_REFRESH = $00000100; WINHTTP_FLAG_SECURE = $00800000; WINHTTP_ADDREQ_FLAG_COALESCE = $40000000; WINHTTP_QUERY_FLAG_NUMBER = $20000000; WINHTTP_QUERY_CONTENT_LENGTH = 5; WINHTTP_QUERY_STATUS_CODE = 19; WINHTTP_QUERY_RAW_HEADERS_CRLF = 22; WINHTTP_QUERY_CONTENT_ENCODING = 29; WINHTTP_QUERY_ACCEPT_ENCODING = 26; function WinHttpOpen(pwszUserAgent: PWideChar; dwAccessType: DWORD; pwszProxyName, pwszProxyBypass: PWideChar; dwFlags: DWORD): HINTERNET; stdcall; external winhttpdll; function WinHttpConnect(hSession: HINTERNET; pswzServerName: PWideChar; nServerPort: INTERNET_PORT; dwReserved: DWORD): HINTERNET; stdcall; external winhttpdll; function WinHttpOpenRequest(hConnect: HINTERNET; pwszVerb: PWideChar; pwszObjectName: PWideChar; pwszVersion: PWideChar; pwszReferer: PWideChar; ................................................................................ if fConnection<>nil then WinHttpCloseHandle(fConnection); if fSession<>nil then WinHttpCloseHandle(fSession); inherited; end; function TSockDataToUnicode(const Ansi: TSockData): TSockData; var n, i: integer; begin n := length(Ansi); SetLength(result,n*2+1); for i := 0 to n do // to n = including last #0 PWordArray(result)[i] := PByteArray(Ansi)[i]; // fast ANSI 7 bit conversion end; procedure TWinHTTP.InternalAddHeader(const hdr: TSockData); begin if hdr='' then exit; if not WinHttpAddRequestHeaders(FRequest,Pointer(TSockDataToUnicode(hdr)), length(hdr),WINHTTP_ADDREQ_FLAG_COALESCE) then RaiseLastOSError; end; procedure TWinHTTP.InternalCloseRequest; begin if fRequest<>nil then begin WinHttpCloseHandle(fRequest); FRequest := nil; end; end; procedure TWinHTTP.InternalConnect; begin fSession := WinHttpOpen(DEFAULT_AGENT,WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,nil,nil,0); if fSession=nil then RaiseLastOSError; fConnection := WinHttpConnect(fSession,pointer(TSockDataToUnicode(FServer)),fPort,0); if fConnection=nil then RaiseLastOSError; end; procedure TWinHTTP.InternalRequest(const method, aURL: TSockData); var Flags: DWORD; begin Flags := WINHTTP_FLAG_REFRESH; // options for a true RESTful request if fHttps then Flags := Flags or WINHTTP_FLAG_SECURE; fRequest := WinHttpOpenRequest(fConnection, pointer(TSockDataToUnicode(method)), pointer(TSockDataToUnicode(aURL)), nil, nil, @ALL_ACCEPT, Flags); if fRequest=nil then RaiseLastOSError; end; function TWinHTTP.InternalSendRequest(const aData: TSockData; var OutHeader, OutData, OutDataEncoding, InAcceptEncoding: TSockData): integer; function GetInfo(Info: DWORD): TSockData; var dwSize, dwIndex: DWORD; tmp: TSockData; i: integer; begin result := ''; dwSize := 0; dwIndex := 0; if not WinHttpQueryHeaders(fRequest,Info,nil,nil,dwSize,dwIndex) and (GetLastError=ERROR_INSUFFICIENT_BUFFER) then begin SetLength(tmp,dwSize); if WinHttpQueryHeaders(fRequest,Info,nil,pointer(tmp),dwSize,dwIndex) then begin dwSize := dwSize shr 1; SetLength(result,dwSize); for i := 0 to dwSize-1 do // fast ANSI 7 bit conversion PByteArray(result)[i] := PWordArray(tmp)[i]; end; end; end; function GetInfo32(Info: DWORD): DWORD; var dwSize, dwIndex: DWORD; begin dwSize := sizeof(result); dwIndex := 0; Info := Info or WINHTTP_QUERY_FLAG_NUMBER; if not WinHttpQueryHeaders(fRequest,Info,nil,@result,dwSize,dwIndex) then result := 0; end; var Bytes, DataLen, Read: DWORD; begin if not WinHttpSendRequest(fRequest,nil,0,pointer(aData),length(aData),length(aData),0) or not WinHttpReceiveResponse(fRequest,nil) then RaiseLastOSError; // retrieve status and headers result := GetInfo32(WINHTTP_QUERY_STATUS_CODE); OutHeader := GetInfo(WINHTTP_QUERY_RAW_HEADERS_CRLF); // retrieve body content OutData := ''; DataLen := GetInfo32(WINHTTP_QUERY_CONTENT_LENGTH); if DataLen<>0 then begin SetLength(OutData,DataLen); Read := 0; repeat if WinHttpReadData(fRequest,@PByteArray(OutData)[Read],DataLen-Read,Bytes) then if Bytes=0 then begin SetLength(OutData,Read); break; end else inc(Read,Bytes) else RaiseLastOSError; until Read=DataLen; OutDataEncoding := GetInfo(WINHTTP_QUERY_CONTENT_ENCODING); InAcceptEncoding := GetInfo(WINHTTP_QUERY_ACCEPT_ENCODING); end; end; {$endif} initialization {$ifdef DEBUGAPI}AllocConsole;{$endif} |
< < < < < | | | | > > | | > > | | > > > > > > > > > > > | | | | | > | > > > > > > > > > > > > > > > > > > > | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < | < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < > | < < < < < < < < < < < < < < < < < < < < < |
553 554 555 556 557 558 559 560 561 562 563 564 565 566 ... 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 ... 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 ... 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 ... 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 .... 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 .... 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 .... 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 .... 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 .... 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 .... 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 .... 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 .... 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 |
/// the event handled called by the default implementation of the // virtual Request method // - warning: this process must be thread-safe (can be called by several // threads simultaneously) property OnRequest: TOnHttpServerRequest read fOnRequest write fOnRequest; end; {{ HTTP server using fast http.sys kernel-mode server - The HTTP Server API enables applications to communicate over HTTP without using Microsoft Internet Information Server (IIS). Applications can register to receive HTTP requests for particular URLs, receive HTTP requests, and send HTTP responses. The HTTP Server API includes SSL support so that applications can exchange data over secure HTTP connections without IIS. It is also designed to work with I/O completion ports. ................................................................................ applications running simultaneously. } THttpApiServer = class(THttpServerGeneric) protected /// the internal request queue fReqQueue: THandle; /// contain clones list fClones: TObjectList; /// list of all registered URL (Unicode-encoded) fRegisteredUrl: array of TSockData; /// server main loop - don't change directly // - will call the Request public virtual method with the appropriate // parameters to retrive the content procedure Execute; override; /// create a clone constructor CreateClone(From: THttpApiServer); public ................................................................................ fCompressHeader: THttpSocketCompressSet; /// used for internal connection fSession, fConnection, fRequest: HINTERNET; procedure InternalConnect; virtual; abstract; procedure InternalRequest(const method, aURL: TSockData); virtual; abstract; procedure InternalCloseRequest; virtual; abstract; procedure InternalAddHeader(const hdr: TSockData); virtual; abstract; procedure InternalSendRequest(const aData: TSockData); virtual; abstract; function InternalGetInfo(Info: DWORD): TSockData; virtual; abstract; function InternalGetInfo32(Info: DWORD): DWORD; virtual; abstract; function InternalReadData(var Data: TSockData; Read: integer): cardinal; virtual; abstract; public /// connect to http://aServer:aPort or https://aServer:aPort constructor Create(const aServer, aPort: AnsiString; aHttps: boolean); /// low-level HTTP/1.1 request // - after an Create(server,port), return 200,202,204 if OK, // http status error otherwize function Request(const url, method: TSockData; KeepAlive: cardinal; ................................................................................ TWinINet = class(TWinHttpAPI) protected // those internal methods will raise an EWinINet exception on error procedure InternalConnect; override; procedure InternalRequest(const method, aURL: TSockData); override; procedure InternalCloseRequest; override; procedure InternalAddHeader(const hdr: TSockData); override; procedure InternalSendRequest(const aData: TSockData); override; function InternalGetInfo(Info: DWORD): TSockData; override; function InternalGetInfo32(Info: DWORD): DWORD; override; function InternalReadData(var Data: TSockData; Read: integer): cardinal; override; public /// relase the connection destructor Destroy; override; end; /// WinINet exception type EWinINet = class(Exception) ................................................................................ TWinHTTP = class(TWinHttpAPI) protected // those internal methods will raise an EWinINet exception on error procedure InternalConnect; override; procedure InternalRequest(const method, aURL: TSockData); override; procedure InternalCloseRequest; override; procedure InternalAddHeader(const hdr: TSockData); override; procedure InternalSendRequest(const aData: TSockData); override; function InternalGetInfo(Info: DWORD): TSockData; override; function InternalGetInfo32(Info: DWORD): DWORD; override; function InternalReadData(var Data: TSockData; Read: integer): cardinal; override; public /// relase the connection destructor Destroy; override; end; /// type of a TWinHttpAPI class TWinHttpAPIClass = class of TWinHttpAPI; ................................................................................ const HttpNames: array[0..9] of PChar = ( 'HttpInitialize','HttpTerminate','HttpCreateHttpHandle', 'HttpAddUrl', 'HttpRemoveUrl', 'HttpReceiveHttpRequest', 'HttpSendHttpResponse', 'HttpReceiveRequestEntityBody', 'HttpSetServiceConfiguration', 'HttpDeleteServiceConfiguration'); function ToUnicode(const Ansi: TSockData): TSockData; var n, i: integer; begin n := length(Ansi); SetLength(result,n*2+1); for i := 0 to n do // to n = including last #0 PWordArray(result)[i] := PByteArray(Ansi)[i]; // fast ANSI 7 bit conversion end; function RegURL(aRoot, aPort: TSockData; Https: boolean; aDomainName: TSockData): TSockData; const Prefix: array[boolean] of TSockData = ('http://','https://'); begin if aPort='' then aPort := '80'; aRoot := trim(aRoot); aDomainName := trim(aDomainName); if aDomainName='' then begin ................................................................................ if aRoot[1]<>'/' then insert('/',aRoot,1); if aRoot[length(aRoot)]<>'/' then aRoot := aRoot+'/'; end else aRoot := '/'; // allow for instance 'http://*:2869/' aRoot := Prefix[Https]+aDomainName+':'+aPort+aRoot; result := ToUnicode(aRoot); end; function THttpApiServer.AddUrl(const aRoot, aPort: TSockData; Https: boolean; const aDomainName: TSockData): integer; var s: TSockData; n: integer; begin result := -1; if (Self=nil) or (fReqQueue=0) or (Http.Module=0) then exit; s := RegURL(aRoot, aPort, Https, aDomainName); if s='' then ................................................................................ inherited CreateFmt('%s failed: %s (%d)', [HttpNames[NameIndex],SysErrorMessage(Error),Error]) end; class function THttpApiServer.AddUrlAuthorize(const aRoot, aPort: TSockData; Https: boolean; const aDomainName: TSockData; OnlyDelete: boolean): string; var prefix: TSockData; Error: HRESULT; Config: HTTP_SERVICE_CONFIG_URLACL_SET; begin try HttpApiInitialize; prefix := RegURL(aRoot, aPort, Https, aDomainName); if prefix='' then ................................................................................ begin result := RegisterCompressFunc(fCompress,aFunction,fCompressAcceptEncoding)<>''; end; function TWinHttpAPI.Request(const url, method: TSockData; KeepAlive: cardinal; const InHeader, InData, InDataType: TSockData; out OutHeader, OutData: TSockData): integer; var aData, aDataEncoding, aAcceptEncoding, aURL: TSockData; Bytes, DataLen, Read: DWORD; i: integer; begin if (url='') or (url[1]<>'/') then aURL := '/'+url else // need valid url according to the HTTP/1.1 RFC aURL := url; fKeepAlive := KeepAlive; InternalRequest(method,aURL); // should raise an exception on error ................................................................................ InDataType,aData); if aDataEncoding<>'' then InternalAddHeader(TSockData('Content-Encoding: ')+aDataEncoding); end; if fCompressAcceptEncoding<>'' then InternalAddHeader(fCompressAcceptEncoding); // send request to remote server InternalSendRequest(aData); // retrieve status and headers (HTTP_QUERY* and WINHTTP_QUERY* do match) result := InternalGetInfo32(HTTP_QUERY_STATUS_CODE); OutHeader := InternalGetInfo(HTTP_QUERY_RAW_HEADERS_CRLF); aDataEncoding := InternalGetInfo(HTTP_QUERY_CONTENT_ENCODING); aAcceptEncoding := InternalGetInfo(HTTP_QUERY_ACCEPT_ENCODING); // retrieve received content (if any) DataLen := InternalGetInfo32(HTTP_QUERY_CONTENT_LENGTH); if DataLen<>0 then begin SetLength(OutData,DataLen); Read := 0; repeat Bytes := InternalReadData(OutData,Read); if Bytes=0 then begin SetLength(OutData,Read); break; end else inc(Read,Bytes); until Read=DataLen; end; // handle incoming answer compression if OutData<>'' then begin if aDataEncoding<>'' then for i := 0 to high(fCompress) do with fCompress[i] do if Name=aDataEncoding then if Func(AnsiString(OutData),false)='' then raise ECrtSocket.CreateFmt('%s uncompress',[Name]) else break; // successfully uncompressed content if aAcceptEncoding<>'' then fCompressHeader := SetCompressHeader(fCompress,pointer(aAcceptEncoding)); end; finally InternalCloseRequest; end; end; ................................................................................ InternetCloseHandle(FSession); inherited; end; procedure TWinINet.InternalAddHeader(const hdr: TSockData); begin if (hdr<>'') and not HttpAddRequestHeadersA(FRequest, Pointer(hdr), length(hdr), HTTP_ADDREQ_FLAG_COALESCE) then EWinINet.Create; end; procedure TWinINet.InternalCloseRequest; begin if FRequest<>nil then begin InternetCloseHandle(FRequest); FRequest := nil; end; end; procedure TWinINet.InternalConnect; begin FSession := InternetOpenA(DEFAULT_AGENT, INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0); if FSession=nil then raise EWinINet.Create; FConnection := InternetConnectA(FSession, pointer(FServer), FPort, nil, nil, INTERNET_SERVICE_HTTP, 0, 0); if FConnection=nil then raise EWinINet.Create; end; const ALL_ACCEPT: array[0..1] of PAnsiChar = ('*/*',nil); function TWinINet.InternalGetInfo(Info: DWORD): TSockData; var dwSize, dwIndex: DWORD; begin result := ''; dwSize := 0; dwIndex := 0; if not HttpQueryInfoA(fRequest, Info, nil, dwSize, dwIndex) and (GetLastError=ERROR_INSUFFICIENT_BUFFER) then begin SetLength(result,dwSize-1); if not HttpQueryInfoA(fRequest, Info, pointer(result), dwSize, dwIndex) then result := ''; end; end; function TWinINet.InternalGetInfo32(Info: DWORD): DWORD; var dwSize, dwIndex: DWORD; begin dwSize := sizeof(result); dwIndex := 0; Info := Info or HTTP_QUERY_FLAG_NUMBER; if not HttpQueryInfoA(fRequest, Info, @result, dwSize, dwIndex) then result := 0; end; function TWinINet.InternalReadData(var Data: TSockData; Read: integer): cardinal; begin if not InternetReadFile(fRequest, @PByteArray(Data)[Read], length(Data)-Read, result) then raise EWinINet.Create; end; procedure TWinINet.InternalRequest(const method, aURL: TSockData); var Flags: DWORD; begin Flags := INTERNET_FLAG_HYPERLINK or INTERNET_FLAG_PRAGMA_NOCACHE or INTERNET_FLAG_RESYNCHRONIZE; // options for a true RESTful request if FKeepAlive<>0 then ................................................................................ Flags := Flags or INTERNET_FLAG_KEEP_CONNECTION; FRequest := HttpOpenRequestA(FConnection, Pointer(method), Pointer(aURL), nil, nil, @ALL_ACCEPT, Flags,0); if FRequest=nil then EWinINet.Create; end; procedure TWinINet.InternalSendRequest(const aData: TSockData); begin if not HttpSendRequestA(fRequest, nil, 0, pointer(aData), length(aData)) then EWinINet.Create; end; { TWinHTTP } const winhttpdll = 'winhttp.dll'; WINHTTP_ACCESS_TYPE_DEFAULT_PROXY = 0; WINHTTP_FLAG_REFRESH = $00000100; WINHTTP_FLAG_SECURE = $00800000; WINHTTP_ADDREQ_FLAG_COALESCE = $40000000; WINHTTP_QUERY_FLAG_NUMBER = $20000000; function WinHttpOpen(pwszUserAgent: PWideChar; dwAccessType: DWORD; pwszProxyName, pwszProxyBypass: PWideChar; dwFlags: DWORD): HINTERNET; stdcall; external winhttpdll; function WinHttpConnect(hSession: HINTERNET; pswzServerName: PWideChar; nServerPort: INTERNET_PORT; dwReserved: DWORD): HINTERNET; stdcall; external winhttpdll; function WinHttpOpenRequest(hConnect: HINTERNET; pwszVerb: PWideChar; pwszObjectName: PWideChar; pwszVersion: PWideChar; pwszReferer: PWideChar; ................................................................................ if fConnection<>nil then WinHttpCloseHandle(fConnection); if fSession<>nil then WinHttpCloseHandle(fSession); inherited; end; procedure TWinHTTP.InternalAddHeader(const hdr: TSockData); begin if hdr='' then exit; if not WinHttpAddRequestHeaders(FRequest, Pointer(ToUnicode(hdr)), length(hdr), WINHTTP_ADDREQ_FLAG_COALESCE) then RaiseLastOSError; end; procedure TWinHTTP.InternalCloseRequest; begin if fRequest<>nil then begin WinHttpCloseHandle(fRequest); FRequest := nil; end; end; procedure TWinHTTP.InternalConnect; begin fSession := WinHttpOpen(DEFAULT_AGENT, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, nil, nil, 0); if fSession=nil then RaiseLastOSError; fConnection := WinHttpConnect(fSession, pointer(ToUnicode(FServer)), fPort, 0); if fConnection=nil then RaiseLastOSError; end; function TWinHTTP.InternalGetInfo(Info: DWORD): TSockData; var dwSize, dwIndex: DWORD; tmp: TSockData; i: integer; begin result := ''; dwSize := 0; dwIndex := 0; if not WinHttpQueryHeaders(fRequest, Info, nil, nil, dwSize, dwIndex) and (GetLastError=ERROR_INSUFFICIENT_BUFFER) then begin SetLength(tmp,dwSize); if WinHttpQueryHeaders(fRequest, Info, nil, pointer(tmp), dwSize, dwIndex) then begin dwSize := dwSize shr 1; SetLength(result,dwSize); for i := 0 to dwSize-1 do // fast ANSI 7 bit conversion PByteArray(result)[i] := PWordArray(tmp)[i]; end; end; end; function TWinHTTP.InternalGetInfo32(Info: DWORD): DWORD; var dwSize, dwIndex: DWORD; begin dwSize := sizeof(result); dwIndex := 0; Info := Info or WINHTTP_QUERY_FLAG_NUMBER; if not WinHttpQueryHeaders(fRequest, Info, nil, @result, dwSize, dwIndex) then result := 0; end; function TWinHTTP.InternalReadData(var Data: TSockData; Read: integer): cardinal; begin if not WinHttpReadData(fRequest, @PByteArray(Data)[Read], length(Data)-Read, result) then RaiseLastOSError; end; procedure TWinHTTP.InternalRequest(const method, aURL: TSockData); var Flags: DWORD; begin Flags := WINHTTP_FLAG_REFRESH; // options for a true RESTful request if fHttps then Flags := Flags or WINHTTP_FLAG_SECURE; fRequest := WinHttpOpenRequest(fConnection, pointer(ToUnicode(method)), pointer(ToUnicode(aURL)), nil, nil, @ALL_ACCEPT, Flags); if fRequest=nil then RaiseLastOSError; end; procedure TWinHTTP.InternalSendRequest(const aData: TSockData); var L: integer; begin L := length(aData); if not WinHttpSendRequest(fRequest, nil, 0, pointer(aData), L, L, 0) or not WinHttpReceiveResponse(fRequest,nil) then RaiseLastOSError; end; {$endif} initialization {$ifdef DEBUGAPI}AllocConsole;{$endif} |