|
@@ -0,0 +1,770 @@
|
|
|
+{
|
|
|
+ This file is part of the Free Component Library (FCL)
|
|
|
+ Copyright (c) 2017-2018 by the Free Pascal development team
|
|
|
+
|
|
|
+ Windows HTTP Server API based TCustomWebApplication
|
|
|
+
|
|
|
+ See the file COPYING.FPC, included in this distribution,
|
|
|
+ for details about the copyright.
|
|
|
+
|
|
|
+ This program is distributed in the hope that it will be useful,
|
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
+
|
|
|
+ **********************************************************************}
|
|
|
+unit custHTTPSys;
|
|
|
+
|
|
|
+{$mode objfpc}{$H+}
|
|
|
+
|
|
|
+interface
|
|
|
+
|
|
|
+uses
|
|
|
+ SysUtils, Classes, HttpApi, custWeb, HTTPDefs;
|
|
|
+
|
|
|
+type
|
|
|
+
|
|
|
+ { THTTPSysRequest }
|
|
|
+
|
|
|
+ THTTPSysRequest = class(TRequest)
|
|
|
+ private
|
|
|
+ fHandle: THandle;
|
|
|
+ fRequestId: HTTP_REQUEST_ID;
|
|
|
+ function GetBaseUrl(const aUrl: AnsiString): AnsiString;
|
|
|
+ procedure FillHeader(aRequest: PHTTP_REQUEST);
|
|
|
+ procedure FillHTTPVariables(aRequest: PHTTP_REQUEST);
|
|
|
+ procedure InitFromRequest(aRequest: PHTTP_REQUEST);
|
|
|
+ protected
|
|
|
+ procedure ReadContent; override;
|
|
|
+ public
|
|
|
+ constructor CreateReq(aHandle: THandle; const aUrl: String; aRequest: PHTTP_REQUEST);
|
|
|
+ end;
|
|
|
+ THTTPSysRequestClass = class of THTTPSysRequest;
|
|
|
+
|
|
|
+ { THTTPSysResponse }
|
|
|
+
|
|
|
+ THTTPSysResponse = class(TResponse)
|
|
|
+ protected
|
|
|
+ fHandle: THandle;
|
|
|
+ fRequestId: HTTP_REQUEST_ID;
|
|
|
+ fRequestVersion: HTTP_VERSION;
|
|
|
+ procedure DoSendHeaders(aHeaders: TStrings); override;
|
|
|
+ procedure DoSendContent; override;
|
|
|
+ end;
|
|
|
+ THTTPSysResponseClass = class of THTTPSysResponse;
|
|
|
+
|
|
|
+ { THTTPSysHandler }
|
|
|
+
|
|
|
+ THTTPSysHandler = class(TWebHandler)
|
|
|
+ private
|
|
|
+ fUrls: TStrings;
|
|
|
+ fHandle: THandle;
|
|
|
+ fServerSession: HTTP_SERVER_SESSION_ID;
|
|
|
+ fUrlGroup: HTTP_URL_GROUP_ID;
|
|
|
+ fBuffer: PHTTP_REQUEST;
|
|
|
+ fBufferSize: LongWord;
|
|
|
+ procedure InitUrls;
|
|
|
+ protected
|
|
|
+ function CreateRequest(aRequest: PHTTP_REQUEST; const aUrl: String): THTTPSysRequest; virtual;
|
|
|
+ function CreateResponse(aRequest: THTTPSysRequest): THTTPSysResponse; virtual;
|
|
|
+ procedure ProcessRequest(aBuffer: PHTTP_REQUEST; aSize: LongWord; out aRequest: TRequest; out aResponse: TResponse);
|
|
|
+ function WaitForRequest(out aRequest: TRequest; out aResponse: TResponse): Boolean; override;
|
|
|
+ public
|
|
|
+ procedure Terminate; override;
|
|
|
+ constructor Create(AOwner: TComponent); override;
|
|
|
+ destructor Destroy; override;
|
|
|
+ end;
|
|
|
+ THTTPSysHandlerClass = class of THTTPSysHandler;
|
|
|
+
|
|
|
+ { TCustomHTTPSysApplication }
|
|
|
+
|
|
|
+ TCustomHTTPSysApplication = class(TCustomWebApplication)
|
|
|
+ private
|
|
|
+ fUrls: TStrings;
|
|
|
+ protected
|
|
|
+ function InitializeWebHandler: TWebHandler; override;
|
|
|
+ procedure DoRun; override;
|
|
|
+ public
|
|
|
+ constructor Create(AOwner: TComponent); override;
|
|
|
+ destructor Destroy; override;
|
|
|
+ property Urls: TStrings read fUrls;
|
|
|
+ end;
|
|
|
+
|
|
|
+ EHTTPSys = class(EFPWebError);
|
|
|
+
|
|
|
+var
|
|
|
+ HTTPSysRequestClass: THTTPSysRequestClass = THTTPSysRequest;
|
|
|
+ HTTPSysResponseClass: THTTPSysResponseClass = THTTPSysResponse;
|
|
|
+ HTTPSysHandlerClass: THTTPSysHandlerClass = THTTPSysHandler;
|
|
|
+
|
|
|
+implementation
|
|
|
+
|
|
|
+uses
|
|
|
+ Windows, httpprotocol, WinSock2;
|
|
|
+
|
|
|
+resourcestring
|
|
|
+ SErrReceiveRequest = 'Failed to receive HTTP request (Errorcode: 0x%x)';
|
|
|
+ SErrReceiveRequestBody = 'Failed to receive body of HTTP request (Errorcode: 0x%x)';
|
|
|
+ SErrSendResponse = 'Failed to send HTTP response (Errorcode: 0x%x)';
|
|
|
+ SErrSendResponseBody = 'Failed to send body of HTTP response (Errorcode: 0x%x)';
|
|
|
+ SErrInitializeHttpApi = 'Failed to initialize HTTP API (Errorcode: 0x%x)';
|
|
|
+ SErrCreateRequestQueue = 'Failed to create request queue (Errorcode: 0x%x)';
|
|
|
+ SErrCreateServerSession = 'Failed to create server session (Errorcode: 0x%x)';
|
|
|
+ SErrCreateUrlGroup = 'Failed to create URL group (Errorcode: 0x%x)';
|
|
|
+ SErrAddUrl = 'Failed to add URL ''%s'' to URL group (Errorcode: 0x%x)';
|
|
|
+ SErrBindGroupToQueue = 'Failed to bind URL group to queue (Errorcode: 0x%x)';
|
|
|
+
|
|
|
+function HeaderToHttpHeaderId(aHeader: THeader; out aId: HTTP_HEADER_ID): Boolean;
|
|
|
+begin
|
|
|
+ Result := True;
|
|
|
+ case aHeader of
|
|
|
+ hhAccept:
|
|
|
+ aId := HttpHeaderAccept;
|
|
|
+ hhAcceptCharset:
|
|
|
+ aId := HttpHeaderAcceptCharset;
|
|
|
+ hhAcceptEncoding:
|
|
|
+ aId := HttpHeaderAcceptEncoding;
|
|
|
+ hhAcceptLanguage:
|
|
|
+ aId := HttpHeaderAcceptLanguage;
|
|
|
+ hhAcceptRanges:
|
|
|
+ aId := HttpHeaderAcceptRanges;
|
|
|
+ hhAge:
|
|
|
+ aId := HttpHeaderAge;
|
|
|
+ hhAllow:
|
|
|
+ aId := HttpHeaderAllow;
|
|
|
+ hhAuthorization:
|
|
|
+ aId := HttpHeaderAuthorization;
|
|
|
+ hhCacheControl:
|
|
|
+ aId := HttpHeaderCacheControl;
|
|
|
+ hhConnection:
|
|
|
+ aId := HttpHeaderConnection;
|
|
|
+ hhContentEncoding:
|
|
|
+ aId := HttpHeaderContentEncoding;
|
|
|
+ hhContentLanguage:
|
|
|
+ aId := HttpHeaderContentLanguage;
|
|
|
+ hhContentLength:
|
|
|
+ aId := HttpHeaderContentLength;
|
|
|
+ hhContentLocation:
|
|
|
+ aId := HttpHeaderContentLocation;
|
|
|
+ hhContentMD5:
|
|
|
+ aId := HttpHeaderContentMd5;
|
|
|
+ hhContentRange:
|
|
|
+ aId := HttpHeaderContentRange;
|
|
|
+ hhContentType:
|
|
|
+ aId := HttpHeaderContentType;
|
|
|
+ hhDate:
|
|
|
+ aId := HttpHeaderDate;
|
|
|
+ hhETag:
|
|
|
+ aId := HttpHeaderEtag;
|
|
|
+ hhExpires:
|
|
|
+ aId := HttpHeaderExpires;
|
|
|
+ hhExpect:
|
|
|
+ aId := HttpHeaderExpect;
|
|
|
+ hhFrom:
|
|
|
+ aId := HttpHeaderFrom;
|
|
|
+ hhHost:
|
|
|
+ aId := HttpHeaderHost;
|
|
|
+ hhIfMatch:
|
|
|
+ aId := HttpHeaderIfMatch;
|
|
|
+ hhIfModifiedSince:
|
|
|
+ aId := HttpHeaderIfModifiedSince;
|
|
|
+ hhIfNoneMatch:
|
|
|
+ aId := HttpHeaderIfNoneMatch;
|
|
|
+ hhIfRange:
|
|
|
+ aId := HttpHeaderIfRange;
|
|
|
+ hhIfUnModifiedSince:
|
|
|
+ aId := HttpHeaderIfUnmodifiedSince;
|
|
|
+ hhLastModified:
|
|
|
+ aId := HttpHeaderLastModified;
|
|
|
+ hhLocation:
|
|
|
+ aId := HttpHeaderLocation;
|
|
|
+ hhMaxForwards:
|
|
|
+ aId := HttpHeaderMaxForwards;
|
|
|
+ hhPragma:
|
|
|
+ aId := HttpHeaderPragma;
|
|
|
+ //hhProxyAuthenticate: ;
|
|
|
+ //hhProxyAuthorization: ;
|
|
|
+ hhRange:
|
|
|
+ aId := HttpHeaderRange;
|
|
|
+ hhReferer:
|
|
|
+ aId := HttpHeaderReferer;
|
|
|
+ hhRetryAfter:
|
|
|
+ aId := HttpHeaderRetryAfter;
|
|
|
+ hhServer:
|
|
|
+ aId := HttpHeaderServer;
|
|
|
+ hhTE:
|
|
|
+ aId := HttpHeaderTe;
|
|
|
+ hhTrailer:
|
|
|
+ aId := HttpHeaderTrailer;
|
|
|
+ hhTransferEncoding:
|
|
|
+ aId := HttpHeaderTransferEncoding;
|
|
|
+ hhUpgrade:
|
|
|
+ aId := HttpHeaderUpgrade;
|
|
|
+ hhUserAgent:
|
|
|
+ aId := HttpHeaderUserAgent;
|
|
|
+ hhVary:
|
|
|
+ aId := HttpHeaderVary;
|
|
|
+ hhVia:
|
|
|
+ aId := HttpHeaderVia;
|
|
|
+ hhWarning:
|
|
|
+ aId := HttpHeaderWarning;
|
|
|
+ hhWWWAuthenticate:
|
|
|
+ aId := HttpHeaderWwwAuthenticate;
|
|
|
+ otherwise
|
|
|
+ Result := False;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+function IgnoreHttpHeaderForRequest(aHeader: THeader): Boolean;
|
|
|
+begin
|
|
|
+ case aHeader of
|
|
|
+ hhAcceptRanges,
|
|
|
+ hhAge,
|
|
|
+ hhETag,
|
|
|
+ hhLocation,
|
|
|
+ hhProxyAuthenticate,
|
|
|
+ hhRetryAfter,
|
|
|
+ hhServer,
|
|
|
+ hhVary,
|
|
|
+ hhWWWAuthenticate:
|
|
|
+ Result := True;
|
|
|
+ otherwise
|
|
|
+ Result := False;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+{ THTTPSysResponse }
|
|
|
+
|
|
|
+procedure THTTPSysResponse.DoSendHeaders(aHeaders: TStrings);
|
|
|
+var
|
|
|
+ resp: HTTP_RESPONSE;
|
|
|
+ flags, bytessend: LongWord;
|
|
|
+ i, colonidx: LongInt;
|
|
|
+ headerstr, headerval: String;
|
|
|
+ res: ULONG;
|
|
|
+ hh: THeader;
|
|
|
+ headerid: HTTP_HEADER_ID;
|
|
|
+ headerstrs, unknownheaders: TStrings;
|
|
|
+ unknownheadersarr: array of HTTP_UNKNOWN_HEADER;
|
|
|
+begin
|
|
|
+ resp := Default(HTTP_RESPONSE);
|
|
|
+ resp.Version := fRequestVersion;
|
|
|
+ resp.StatusCode := Code;
|
|
|
+ if CodeText <> '' then
|
|
|
+ resp.pReason := PChar(CodeText);
|
|
|
+
|
|
|
+ flags := 0;
|
|
|
+ if (Assigned(ContentStream) and (ContentStream.Size > 0)) or (Contents.Count > 0) then
|
|
|
+ flags := flags or HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
|
|
|
+
|
|
|
+ unknownheaders := Nil;
|
|
|
+ headerstrs := TStringList.Create;
|
|
|
+ try
|
|
|
+ unknownheaders := TStringList.Create;
|
|
|
+
|
|
|
+ for i := 0 to aHeaders.Count - 1 do begin
|
|
|
+ colonidx := Pos(':', aHeaders[i]);
|
|
|
+ if colonidx = 0 then
|
|
|
+ Continue;
|
|
|
+ headerstr := Copy(aHeaders[i], 1, colonidx - 1);
|
|
|
+ headerval := Trim(Copy(aHeaders[i], colonidx + 1, Length(aHeaders[i]) - colonidx));
|
|
|
+
|
|
|
+ hh := HeaderType(headerstr);
|
|
|
+ if hh = hhUnknown then begin
|
|
|
+ unknownheaders.Values[headerstr] := headerval;
|
|
|
+ Continue;
|
|
|
+ end;
|
|
|
+
|
|
|
+ if not (hdResponse in HTTPHeaderDirections[hh]) then begin
|
|
|
+ unknownheaders.Values[headerstr] := headerval;
|
|
|
+ Continue;
|
|
|
+ end;
|
|
|
+
|
|
|
+ if not HeaderToHttpHeaderId(hh, headerid) then begin
|
|
|
+ unknownheaders.Values[headerstr] := headerval;
|
|
|
+ Continue;
|
|
|
+ end;
|
|
|
+
|
|
|
+ if headerid >= HttpHeaderResponseMaximum then begin
|
|
|
+ unknownheaders.Values[headerstr] := headerval;
|
|
|
+ Continue;
|
|
|
+ end;
|
|
|
+
|
|
|
+ headerstrs.Add(headerval);
|
|
|
+
|
|
|
+ resp.Headers.KnownHeaders[Ord(headerid)].RawValueLength := Length(headerval) + 1;
|
|
|
+ resp.Headers.KnownHeaders[Ord(headerid)].pRawValue := PAnsiChar(headerstrs[headerstrs.Count - 1]);
|
|
|
+ end;
|
|
|
+
|
|
|
+ SetLength(unknownheadersarr, unknownheaders.Count);
|
|
|
+ for i := 0 to unknownheaders.Count - 1 do begin
|
|
|
+ headerstr := unknownheaders.Names[i];
|
|
|
+ headerval := unknownheaders.ValueFromIndex[i];
|
|
|
+
|
|
|
+ headerstrs.Add(headerstr);
|
|
|
+ unknownheadersarr[i].NameLength := Length(headerstr) + 1;
|
|
|
+ unknownheadersarr[i].pName := PAnsiChar(headerstrs[headerstrs.Count - 1]);
|
|
|
+
|
|
|
+ headerstrs.Add(headerval);
|
|
|
+ unknownheadersarr[i].RawValueLength := Length(headerval) + 1;
|
|
|
+ unknownheadersarr[i].pRawValue := PAnsiChar(headerstrs[headerstrs.Count - 1]);
|
|
|
+ end;
|
|
|
+
|
|
|
+ if unknownheaders.Count > 0 then begin
|
|
|
+ resp.Headers.UnknownHeaderCount := unknownheaders.Count;
|
|
|
+ resp.Headers.pUnknownHeaders := @unknownheadersarr[0];
|
|
|
+ end;
|
|
|
+
|
|
|
+ res := HttpSendHttpResponse(fHandle, fRequestId, flags, @resp, Nil, @bytessend, Nil, 0, Nil, Nil);
|
|
|
+ if res <> NO_ERROR then
|
|
|
+ raise EHTTPSys.CreateFmtHelp(SErrSendResponse, [res], 500);
|
|
|
+ finally
|
|
|
+ unknownheaders.Free;
|
|
|
+ headerstrs.Free;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure THTTPSysResponse.DoSendContent;
|
|
|
+var
|
|
|
+ chunk: HTTP_DATA_CHUNK;
|
|
|
+ bytessend: LongWord;
|
|
|
+ memstrm: TMemoryStream;
|
|
|
+ res: ULONG;
|
|
|
+begin
|
|
|
+ if not (Assigned(ContentStream) and (ContentStream.Size > 0)) and not (Contents.Count > 0) then
|
|
|
+ Exit;
|
|
|
+
|
|
|
+ memstrm := TMemoryStream.Create;
|
|
|
+ try
|
|
|
+ if Assigned(ContentStream) then
|
|
|
+ memstrm.CopyFrom(ContentStream, ContentStream.Size)
|
|
|
+ else
|
|
|
+ Contents.SaveToStream(memstrm);
|
|
|
+
|
|
|
+ chunk := Default(HTTP_DATA_CHUNK);
|
|
|
+ chunk.DataChunkType := HttpDataChunkFromMemory;
|
|
|
+ chunk.FromMemory.pBuffer := memstrm.Memory;
|
|
|
+ chunk.FromMemory.BufferLength := memstrm.Size;
|
|
|
+
|
|
|
+ res := HttpSendResponseEntityBody(fHandle, fRequestId, 0, 1, @chunk, @bytessend, Nil, Nil, Nil, Nil);
|
|
|
+ if res <> NO_ERROR then
|
|
|
+ raise EHTTPSys.CreateFmtHelp(SErrSendResponseBody, [res], 500);
|
|
|
+ finally
|
|
|
+ memstrm.Free;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+{ THTTPSysRequest }
|
|
|
+
|
|
|
+function THTTPSysRequest.GetBaseUrl(const aUrl: AnsiString): AnsiString;
|
|
|
+const
|
|
|
+ ProtocolHttp = 'http://';
|
|
|
+ ProtocolHttps = 'https://';
|
|
|
+var
|
|
|
+ prefix: AnsiString;
|
|
|
+ slashidx: LongInt;
|
|
|
+begin
|
|
|
+ prefix := aUrl;
|
|
|
+ if Copy(prefix, 1, Length(ProtocolHttp)) = ProtocolHttp then
|
|
|
+ Delete(prefix, 1, Length(ProtocolHttp))
|
|
|
+ else if Copy(prefix, 1, Length(ProtocolHttps)) = ProtocolHttps then
|
|
|
+ Delete(prefix, 1, Length(ProtocolHttps))
|
|
|
+ else
|
|
|
+ Exit('');
|
|
|
+
|
|
|
+ slashidx := Pos('/', prefix);
|
|
|
+ if slashidx = 0 then
|
|
|
+ Exit('');
|
|
|
+
|
|
|
+ Delete(prefix, 1, slashidx - 1);
|
|
|
+
|
|
|
+ Result := prefix;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure THTTPSysRequest.FillHeader(aRequest: PHTTP_REQUEST);
|
|
|
+var
|
|
|
+ hh: THeader;
|
|
|
+ hid: HTTP_HEADER_ID;
|
|
|
+ unkheader: PHTTP_UNKNOWN_HEADER;
|
|
|
+ i: LongInt;
|
|
|
+ name, value: AnsiString;
|
|
|
+ hv: THTTPVariableType;
|
|
|
+begin
|
|
|
+ for hh := Low(THeader) to High(THeader) do begin
|
|
|
+ if not (hdRequest in HTTPHeaderDirections[hh]) or IgnoreHttpHeaderForRequest(hh) then
|
|
|
+ Continue;
|
|
|
+ if not HeaderToHttpHeaderId(hh, hid) then
|
|
|
+ Continue;
|
|
|
+ if aRequest^.Headers.KnownHeaders[Ord(hid)].RawValueLength > 0 then
|
|
|
+ SetHeader(hh, StrPas(aRequest^.Headers.KnownHeaders[Ord(hid)].pRawValue));
|
|
|
+ end;
|
|
|
+
|
|
|
+ for i := 0 to aRequest^.Headers.UnknownHeaderCount - 1 do begin
|
|
|
+ unkheader := @aRequest^.Headers.pUnknownHeaders[i];
|
|
|
+ if (unkheader^.NameLength > 0) and Assigned(unkheader^.pName) then begin
|
|
|
+ name := StrPas(unkheader^.pName);
|
|
|
+ value := StrPas(unkheader^.pRawValue);
|
|
|
+ if name = HeaderProxyAuthenticate then
|
|
|
+ hh := hhProxyAuthenticate
|
|
|
+ else if name = HeaderProxyAuthorization then
|
|
|
+ hh := hhProxyAuthorization
|
|
|
+ else begin
|
|
|
+ hh := hhUnknown;
|
|
|
+ hv := hvUnknown;
|
|
|
+ if name = HeaderSetCookie then
|
|
|
+ hv := hvSetCookie
|
|
|
+ else if name = HeaderCookie then
|
|
|
+ hv := hvCookie
|
|
|
+ else if name = HeaderXRequestedWith then
|
|
|
+ hv := hvXRequestedWith;
|
|
|
+ if hv <> hvUnknown then
|
|
|
+ SetHTTPVariable(hvSetCookie, value)
|
|
|
+ else
|
|
|
+ SetCustomHeader(name, value);
|
|
|
+ end;
|
|
|
+ if hh <> hhUnknown then
|
|
|
+ SetHeader(hh, value);
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure THTTPSysRequest.FillHTTPVariables(aRequest: PHTTP_REQUEST);
|
|
|
+
|
|
|
+ function GetMethodStr(aRequest: PHTTP_REQUEST): String;
|
|
|
+ begin
|
|
|
+ case aRequest^.Verb of
|
|
|
+ HttpVerbOPTIONS:
|
|
|
+ Result := 'OPTIONS';
|
|
|
+ HttpVerbGET:
|
|
|
+ Result := 'GET';
|
|
|
+ HttpVerbHEAD:
|
|
|
+ Result := 'HEAD';
|
|
|
+ HttpVerbPOST:
|
|
|
+ Result := 'POST';
|
|
|
+ HttpVerbPUT:
|
|
|
+ Result := 'PUT';
|
|
|
+ HttpVerbDELETE:
|
|
|
+ Result := 'DELETE';
|
|
|
+ HttpVerbTRACE:
|
|
|
+ Result := 'TRACE';
|
|
|
+ HttpVerbCONNECT:
|
|
|
+ Result := 'CONNECT';
|
|
|
+ HttpVerbTRACK:
|
|
|
+ Result := 'TRACK';
|
|
|
+ HttpVerbMOVE:
|
|
|
+ Result := 'MOVE';
|
|
|
+ HttpVerbCOPY:
|
|
|
+ Result := 'COPY';
|
|
|
+ HttpVerbPROPFIND:
|
|
|
+ Result := 'PROPFIND';
|
|
|
+ HttpVerbPROPPATCH:
|
|
|
+ Result := 'PROPPATCH';
|
|
|
+ HttpVerbMKCOL:
|
|
|
+ Result := 'MKCOL';
|
|
|
+ HttpVerbLOCK:
|
|
|
+ Result := 'LOCK';
|
|
|
+ HttpVerbUNLOCK:
|
|
|
+ Result := 'UNLOCK';
|
|
|
+ HttpVerbSEARCH:
|
|
|
+ Result := 'SEARCH';
|
|
|
+ otherwise
|
|
|
+ if (aRequest^.UnknownVerbLength > 0) and Assigned(aRequest^.pUnknownVerb) then
|
|
|
+ Result := StrPas(aRequest^.pUnknownVerb)
|
|
|
+ else
|
|
|
+ Result := '';
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ function GetRemoteAddress: String;
|
|
|
+ var
|
|
|
+ len, size: DWord;
|
|
|
+ begin
|
|
|
+ if not Assigned(aRequest^.Address.pRemoteAddress) then
|
|
|
+ Exit('');
|
|
|
+
|
|
|
+ if aRequest^.Address.pRemoteAddress^.sa_family = AF_INET then
|
|
|
+ size := SizeOf(TSockAddrIn)
|
|
|
+ else if aRequest^.Address.pRemoteAddress^.sa_family = AF_INET6 then
|
|
|
+ size := SizeOf(TSockAddrIn6)
|
|
|
+ else
|
|
|
+ Exit('');
|
|
|
+
|
|
|
+ len := 32;
|
|
|
+ SetLength(Result, len - 1);
|
|
|
+
|
|
|
+ if WSAAddressToString(aRequest^.Address.pRemoteAddress^, size, Nil, PChar(Result), len) <> 0 then begin
|
|
|
+ //Writeln('Failed to retrieve address string; error: ', WSAGetLastError);
|
|
|
+ Exit('');
|
|
|
+ end;
|
|
|
+
|
|
|
+ SetLength(Result, len - 1);
|
|
|
+ end;
|
|
|
+
|
|
|
+var
|
|
|
+ s: AnsiString;
|
|
|
+ urlstr, urlprefix: UTF8String;
|
|
|
+ idx: LongInt;
|
|
|
+begin
|
|
|
+ SetHTTPVariable(hvHTTPVersion, IntToStr(aRequest^.Version.MajorVersion) + '.' + IntToStr(aRequest^.Version.MinorVersion));
|
|
|
+ SetHTTPVariable(hvMethod, GetMethodStr(aRequest));
|
|
|
+
|
|
|
+ urlstr := Utf8String(StrPas(aRequest^.CookedUrl.pAbsPath));
|
|
|
+ urlprefix := ReturnedPathInfo;
|
|
|
+
|
|
|
+ SetHTTPVariable(hvURL, urlstr);
|
|
|
+
|
|
|
+ if Copy(urlstr, 1, Length(urlprefix)) = urlprefix then
|
|
|
+ Delete(urlstr, 1, Length(urlprefix));
|
|
|
+
|
|
|
+ idx := Pos('?', urlstr);
|
|
|
+ if idx > 0 then begin
|
|
|
+ SetHTTPVariable(hvPathInfo, Copy(urlstr, 1, idx - 1));
|
|
|
+ SetHTTPVariable(hvQuery, Copy(urlstr, idx + 1, Length(urlstr) - idx));
|
|
|
+ end else
|
|
|
+ SetHTTPVariable(hvPathInfo, urlstr);
|
|
|
+
|
|
|
+ // ToDo
|
|
|
+ {s := GetRemoteAddress;
|
|
|
+ if s <> '' then
|
|
|
+ SetHTTPVariable(hvRemoteAddress, s)}
|
|
|
+end;
|
|
|
+
|
|
|
+procedure THTTPSysRequest.InitFromRequest(aRequest: PHTTP_REQUEST);
|
|
|
+begin
|
|
|
+ FillHeader(aRequest);
|
|
|
+ FillHTTPVariables(aRequest);
|
|
|
+ ParseCookies;
|
|
|
+ ReadContent;
|
|
|
+ InitRequestVars;
|
|
|
+end;
|
|
|
+
|
|
|
+procedure THTTPSysRequest.ReadContent;
|
|
|
+const
|
|
|
+ BufLen = 4096;
|
|
|
+var
|
|
|
+ ss: TStringStream;
|
|
|
+ res, bytesreturned: ULONG;
|
|
|
+ buf: PByte;
|
|
|
+ e: EHTTPSys;
|
|
|
+ s: AnsiString;
|
|
|
+begin
|
|
|
+ buf := Nil;
|
|
|
+ ss := TStringStream.Create('');
|
|
|
+ try
|
|
|
+ buf := GetMem(BufLen);
|
|
|
+
|
|
|
+ repeat
|
|
|
+ res := HttpReceiveRequestEntityBody(fHandle, fRequestId, 0, buf, BufLen, @bytesreturned, Nil);
|
|
|
+ if res = NO_ERROR then
|
|
|
+ ss.Write(buf^, bytesreturned)
|
|
|
+ else if res <> ERROR_HANDLE_EOF then begin
|
|
|
+ e := EHTTPSys.CreateFmt(SErrReceiveRequestBody, [res]);
|
|
|
+ e.StatusCode := 500;
|
|
|
+ raise e;
|
|
|
+ end;
|
|
|
+ until res = ERROR_HANDLE_EOF;
|
|
|
+
|
|
|
+ s := ss.DataString;
|
|
|
+ InitContent(s);
|
|
|
+ finally
|
|
|
+ Freemem(buf);
|
|
|
+ ss.Free;
|
|
|
+ end;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor THTTPSysRequest.CreateReq(aHandle: THandle; const aUrl: String;
|
|
|
+ aRequest: PHTTP_REQUEST);
|
|
|
+begin
|
|
|
+ fHandle := aHandle;
|
|
|
+ fRequestId := aRequest^.RequestId;
|
|
|
+ ReturnedPathInfo := GetBaseUrl(aUrl);
|
|
|
+ inherited Create;
|
|
|
+ InitFromRequest(aRequest);
|
|
|
+end;
|
|
|
+
|
|
|
+{ THTTPSysHandler }
|
|
|
+
|
|
|
+function THTTPSysHandler.CreateRequest(aRequest: PHTTP_REQUEST;
|
|
|
+ const aUrl: String): THTTPSysRequest;
|
|
|
+var
|
|
|
+ c: THTTPSysRequestClass;
|
|
|
+begin
|
|
|
+ c := HTTPSysRequestClass;
|
|
|
+ if not Assigned(c) then
|
|
|
+ c := THTTPSysRequest;
|
|
|
+ Result := c.CreateReq(fHandle, aUrl, aRequest);
|
|
|
+end;
|
|
|
+
|
|
|
+function THTTPSysHandler.CreateResponse(aRequest: THTTPSysRequest
|
|
|
+ ): THTTPSysResponse;
|
|
|
+var
|
|
|
+ c: THTTPSysResponseClass;
|
|
|
+begin
|
|
|
+ c := HTTPSysResponseClass;
|
|
|
+ if not Assigned(c) then
|
|
|
+ c := THTTPSysResponse;
|
|
|
+ Result := c.Create(aRequest);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure THTTPSysHandler.ProcessRequest(aBuffer: PHTTP_REQUEST;
|
|
|
+ aSize: LongWord; out aRequest: TRequest; out aResponse: TResponse);
|
|
|
+var
|
|
|
+ locrequest: THTTPSysRequest;
|
|
|
+ locresponse: THTTPSysResponse;
|
|
|
+ url: String;
|
|
|
+begin
|
|
|
+ if aBuffer^.UrlContext < fUrls.Count then
|
|
|
+ url := fUrls[aBuffer^.UrlContext];
|
|
|
+ locrequest := CreateRequest(aBuffer, url);
|
|
|
+ InitRequest(locrequest);
|
|
|
+
|
|
|
+ locresponse := CreateResponse(locrequest);
|
|
|
+ InitResponse(locresponse);
|
|
|
+ locresponse.fRequestId := aBuffer^.RequestId;
|
|
|
+ locresponse.fRequestVersion := aBuffer^.Version;
|
|
|
+ locresponse.fHandle := fHandle;
|
|
|
+
|
|
|
+ aRequest := locrequest;
|
|
|
+ aResponse := locresponse;
|
|
|
+end;
|
|
|
+
|
|
|
+function THTTPSysHandler.WaitForRequest(out aRequest: TRequest; out
|
|
|
+ aResponse: TResponse): Boolean;
|
|
|
+var
|
|
|
+ readsize: ULONG;
|
|
|
+ res: ULONG;
|
|
|
+begin
|
|
|
+ if not Assigned(fBuffer) then begin
|
|
|
+ InitUrls;
|
|
|
+
|
|
|
+ fBufferSize := 4096;
|
|
|
+ fBuffer := GetMem(fBufferSize);
|
|
|
+ end;
|
|
|
+
|
|
|
+ repeat
|
|
|
+ repeat
|
|
|
+ res := HttpReceiveHttpRequest(fHandle, HTTP_NULL_ID, 0, fBuffer, fBufferSize, @readsize, Nil);
|
|
|
+ if res = ERROR_MORE_DATA then begin
|
|
|
+ FreeMem(fBuffer);
|
|
|
+ fBufferSize := fBufferSize + 4096;
|
|
|
+ fBuffer := GetMem(fBufferSize);
|
|
|
+ end;
|
|
|
+ until res <> ERROR_MORE_DATA;
|
|
|
+ if res <> NO_ERROR then
|
|
|
+ DoError(SErrReceiveRequest, [res])
|
|
|
+ else begin
|
|
|
+ ProcessRequest(fBuffer, readsize, aRequest, aResponse);
|
|
|
+ Result := True;
|
|
|
+ end;
|
|
|
+ until Result or (fHandle = INVALID_HANDLE_VALUE);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure THTTPSysHandler.InitUrls;
|
|
|
+var
|
|
|
+ i: LongInt;
|
|
|
+ res: ULONG;
|
|
|
+ binding: HTTP_BINDING_INFO;
|
|
|
+ s: String;
|
|
|
+begin
|
|
|
+ for i := 0 to fUrls.Count - 1 do begin
|
|
|
+ s := fUrls[i];
|
|
|
+ Log(etInfo, 'Adding URL ' + s);
|
|
|
+ res := HttpAddUrlToUrlGroup(fUrlGroup, PWideChar(WideString(s)), i, 0);
|
|
|
+ if res <> NO_ERROR then
|
|
|
+ DoError(SErrAddUrl, [s, res]);
|
|
|
+ end;
|
|
|
+
|
|
|
+ binding := Default(HTTP_BINDING_INFO);
|
|
|
+ set_Present(binding.Flags, 1);
|
|
|
+ binding.RequestQueueHandle := fHandle;
|
|
|
+
|
|
|
+ res := HttpSetUrlGroupProperty(fUrlGroup, HttpServerBindingProperty, @binding, SizeOf(binding));
|
|
|
+ if res <> NO_ERROR then
|
|
|
+ DoError(SErrBindGroupToQueue, [res]);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure THTTPSysHandler.Terminate;
|
|
|
+begin
|
|
|
+ inherited Terminate;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor THTTPSysHandler.Create(AOwner: TComponent);
|
|
|
+var
|
|
|
+ res: ULONG;
|
|
|
+begin
|
|
|
+ fUrls := TStringList.Create;
|
|
|
+
|
|
|
+ inherited Create(AOwner);
|
|
|
+
|
|
|
+ fHandle := INVALID_HANDLE_VALUE;
|
|
|
+
|
|
|
+ res := HttpCreateRequestQueue(HTTPAPI_VERSION_2, Nil, Nil, 0, @fHandle);
|
|
|
+ if res <> NO_ERROR then
|
|
|
+ DoError(SErrCreateRequestQueue, [res]);
|
|
|
+
|
|
|
+ res := HttpCreateServerSession(HTTPAPI_VERSION_2, @fServerSession, 0);
|
|
|
+ if res <> NO_ERROR then
|
|
|
+ DoError(SErrCreateServerSession, [res]);
|
|
|
+
|
|
|
+ res := HttpCreateUrlGroup(fServerSession, @fUrlGroup, 0);
|
|
|
+ if res <> NO_ERROR then
|
|
|
+ DoError(SErrCreateUrlGroup, [res]);
|
|
|
+end;
|
|
|
+
|
|
|
+destructor THTTPSysHandler.Destroy;
|
|
|
+begin
|
|
|
+ if fUrlGroup <> HTTP_NULL_ID then
|
|
|
+ HttpCloseUrlGroup(fUrlGroup);
|
|
|
+
|
|
|
+ if fServerSession <> HTTP_NULL_ID then
|
|
|
+ HttpCloseServerSession(fServerSession);
|
|
|
+
|
|
|
+ if fHandle <> INVALID_HANDLE_VALUE then
|
|
|
+ HttpCloseRequestQueue(fHandle);
|
|
|
+
|
|
|
+ FreeMem(fBuffer);
|
|
|
+ fUrls.Free;
|
|
|
+
|
|
|
+ inherited Destroy;
|
|
|
+end;
|
|
|
+
|
|
|
+{ TCustomHTTPSysApplication }
|
|
|
+
|
|
|
+function TCustomHTTPSysApplication.InitializeWebHandler: TWebHandler;
|
|
|
+var
|
|
|
+ c: THTTPSysHandlerClass;
|
|
|
+begin
|
|
|
+ c := HTTPSysHandlerClass;
|
|
|
+ if not Assigned(c) then
|
|
|
+ c := THTTPSysHandler;
|
|
|
+ Result := c.Create(Self);
|
|
|
+end;
|
|
|
+
|
|
|
+procedure TCustomHTTPSysApplication.DoRun;
|
|
|
+begin
|
|
|
+ if WebHandler is THTTPSysHandler then
|
|
|
+ THTTPSysHandler(WebHandler).fUrls.Assign(fUrls);
|
|
|
+ inherited DoRun;
|
|
|
+end;
|
|
|
+
|
|
|
+constructor TCustomHTTPSysApplication.Create(AOwner: TComponent);
|
|
|
+var
|
|
|
+ res: ULONG;
|
|
|
+begin
|
|
|
+ fUrls := TStringList.Create;
|
|
|
+
|
|
|
+ res := HttpInitialize(HTTPAPI_VERSION_2, HTTP_INITIALIZE_SERVER, Nil);
|
|
|
+ if res <> NO_ERROR then
|
|
|
+ raise Exception.CreateFmt(SErrInitializeHttpApi, [res]);
|
|
|
+
|
|
|
+ inherited Create(AOwner);
|
|
|
+end;
|
|
|
+
|
|
|
+destructor TCustomHTTPSysApplication.Destroy;
|
|
|
+begin
|
|
|
+ fUrls.Free;
|
|
|
+ HttpTerminate(HTTP_INITIALIZE_SERVER, Nil);
|
|
|
+ inherited Destroy;
|
|
|
+end;
|
|
|
+
|
|
|
+end.
|
|
|
+
|