home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Runimage / Delphi50 / Source / Internet / ISAPIAPP.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-08-11  |  14.6 KB  |  490 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Borland Delphi Visual Component Library         }
  5. {       ISAPI/NSAPI Web server application components   }
  6. {                                                       }
  7. {       Copyright (c) 1997,99 Inprise Corporation       }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11. {$DENYPACKAGEUNIT}
  12.  
  13. unit ISAPIApp;
  14.  
  15. interface
  16.  
  17. uses Windows, Classes, HTTPApp, WebBroker, ISAPI2;
  18.  
  19. type
  20.   TISAPIRequest = class(TWebRequest)
  21.   private
  22.     FECB: PEXTENSION_CONTROL_BLOCK;
  23.   protected
  24.     function GetStringVariable(Index: Integer): string; override;
  25.     function GetDateVariable(Index: Integer): TDateTime; override;
  26.     function GetIntegerVariable(Index: Integer): Integer; override;
  27.   public
  28.     constructor Create(AECB: PEXTENSION_CONTROL_BLOCK);
  29.     function GetFieldByName(const Name: string): string; override;
  30.     function ReadClient(var Buffer; Count: Integer): Integer; override;
  31.     function ReadString(Count: Integer): string; override;
  32.     function TranslateURI(const URI: string): string; override;
  33.     function WriteClient(var Buffer; Count: Integer): Integer; override;
  34.     function WriteString(const AString: string): Boolean; override;
  35.     property ECB: PEXTENSION_CONTROL_BLOCK read FECB;
  36.   end;
  37.  
  38.   TISAPIResponse = class(TWebResponse)
  39.   private
  40.     FStatusCode: Integer;
  41.     FStringVariables: array[0..MAX_STRINGS - 1] of string;
  42.     FIntegerVariables: array[0..MAX_INTEGERS - 1] of Integer;
  43.     FDateVariables: array[0..MAX_DATETIMES - 1] of TDateTime;
  44.     FContent: string;
  45.     FSent: Boolean;
  46.   protected
  47.     function GetContent: string; override;
  48.     function GetDateVariable(Index: Integer): TDateTime; override;
  49.     function GetIntegerVariable(Index: Integer): Integer; override;
  50.     function GetLogMessage: string; override;
  51.     function GetStatusCode: Integer; override;
  52.     function GetStringVariable(Index: Integer): string; override;
  53.     procedure SetContent(const Value: string); override;
  54.     procedure SetDateVariable(Index: Integer; const Value: TDateTime); override;
  55.     procedure SetIntegerVariable(Index: Integer; Value: Integer); override;
  56.     procedure SetLogMessage(const Value: string); override;
  57.     procedure SetStatusCode(Value: Integer); override;
  58.     procedure SetStringVariable(Index: Integer; const Value: string); override;
  59.   public
  60.     constructor Create(HTTPRequest: TWebRequest);
  61.     procedure SendResponse; override;
  62.     procedure SendRedirect(const URI: string); override;
  63.     procedure SendStream(AStream: TStream); override;
  64.     function Sent: Boolean; override;
  65.   end;
  66.  
  67.   TISAPIApplication = class(TWebApplication)
  68.   private
  69.     function NewRequest(var AECB: TEXTENSION_CONTROL_BLOCK): TISAPIRequest;
  70.     function NewResponse(ISAPIRequest: TISAPIRequest): TISAPIResponse;
  71.   public
  72.     // These are the entry points relayed from the ISAPI DLL imports
  73.     function GetExtensionVersion(var Ver: THSE_VERSION_INFO): BOOL;
  74.     function HttpExtensionProc(var ECB: TEXTENSION_CONTROL_BLOCK): DWORD;
  75.     function TerminateExtension(dwFlags: DWORD): BOOL;
  76.   end;
  77.  
  78. function GetExtensionVersion(var Ver: THSE_VERSION_INFO): BOOL; stdcall;
  79. function HttpExtensionProc(var ECB: TEXTENSION_CONTROL_BLOCK): DWORD; stdcall;
  80. function TerminateExtension(dwFlags: DWORD): BOOL; stdcall;
  81.  
  82. implementation
  83.  
  84. uses SysUtils, BrkrConst;
  85.  
  86. const
  87.   ServerVariables: array[0..28] of string = (
  88.     '',
  89.     'SERVER_PROTOCOL',
  90.     'URL',
  91.     '',
  92.     '',
  93.     '',
  94.     'HTTP_CACHE_CONTROL',
  95.     'HTTP_DATE',
  96.     'HTTP_ACCEPT',
  97.     'HTTP_FROM',
  98.     'HTTP_HOST',
  99.     'HTTP_IF_MODIFIED_SINCE',
  100.     'HTTP_REFERER',
  101.     'HTTP_USER_AGENT',
  102.     'HTTP_CONTENT_ENCODING',
  103.     'CONTENT_TYPE',
  104.     'CONTENT_LENGTH',
  105.     'HTTP_CONTENT_VERSION',
  106.     'HTTP_DERIVED_FROM',
  107.     'HTTP_EXPIRES',
  108.     'HTTP_TITLE',
  109.     'REMOTE_ADDR',
  110.     'REMOTE_HOST',
  111.     'SCRIPT_NAME',
  112.     'SERVER_PORT',
  113.     '',
  114.     'HTTP_CONNECTION',
  115.     'HTTP_COOKIE',
  116.     'HTTP_AUTHORIZATION');
  117.  
  118. { TISAPIRequest }
  119.  
  120. constructor TISAPIRequest.Create(AECB: PEXTENSION_CONTROL_BLOCK);
  121. begin
  122.   FECB := AECB;
  123.   inherited Create;
  124. end;
  125.  
  126. function TISAPIRequest.GetFieldByName(const Name: string): string;
  127. var
  128.   Buffer: array[0..4095] of Char;
  129.   Size: DWORD;
  130. begin
  131.   Size := SizeOf(Buffer);
  132.   if ECB.GetServerVariable(ECB.ConnID, PChar(Name), @Buffer, Size) then
  133.   begin
  134.     if Size > 0 then Dec(Size);
  135.     SetString(Result, Buffer, Size);
  136.   end else Result := '';
  137. end;
  138.  
  139. function TISAPIRequest.GetStringVariable(Index: Integer): string;
  140. begin
  141.   case Index of
  142.     0: Result := ECB.lpszMethod;
  143.     3: Result := ECB.lpszQueryString;
  144.     4: Result := ECB.lpszPathInfo;
  145.     5: Result := ECB.lpszPathTranslated;
  146.     1..2, 6..24, 26..28: Result := GetFieldByName(ServerVariables[Index]);
  147.     25: if ECB.cbAvailable > 0 then
  148.       SetString(Result, PChar(ECB.lpbData), ECB.cbAvailable);
  149.    else
  150.       Result := '';
  151.   end;
  152. end;
  153.  
  154. function TISAPIRequest.GetDateVariable(Index: Integer): TDateTime;
  155. var
  156.   Value: string;
  157. begin
  158.   Value := GetStringVariable(Index);
  159.   if Value <> '' then
  160.     Result := ParseDate(Value)
  161.   else Result := -1;
  162. end;
  163.  
  164. function TISAPIRequest.GetIntegerVariable(Index: Integer): Integer;
  165. var
  166.   Value: string;
  167. begin
  168.   Value := GetStringVariable(Index);
  169.   if Value <> '' then
  170.     Result := StrToInt(Value)
  171.   else Result := -1;
  172. end;
  173.  
  174. function TISAPIRequest.ReadClient(var Buffer; Count: Integer): Integer;
  175. begin
  176.   Result := Count;
  177.   if not ECB.ReadClient(ECB.ConnID, @Buffer, DWORD(Result)) then
  178.     Result := -1;
  179. end;
  180.  
  181. function TISAPIRequest.ReadString(Count: Integer): string;
  182. var
  183.   Len: Integer;
  184. begin
  185.   SetLength(Result, Count);
  186.   Len := ReadClient(Pointer(Result)^, Count);
  187.   if Len > 0 then
  188.     SetLength(Result, Len)
  189.   else Result := '';
  190. end;
  191.  
  192. function TISAPIRequest.TranslateURI(const URI: string): string;
  193. var
  194.   PathBuffer: array[0..1023] of Char;
  195.   Size: Integer;
  196. begin
  197.   StrCopy(PathBuffer, PChar(URI));
  198.   Size := SizeOf(PathBuffer);
  199.   if ECB.ServerSupportFunction(ECB.ConnID, HSE_REQ_MAP_URL_TO_PATH,
  200.     @PathBuffer, @Size, nil) then
  201.     Result := PathBuffer
  202.   else Result := '';
  203. end;
  204.  
  205. function TISAPIRequest.WriteClient(var Buffer; Count: Integer): Integer;
  206. begin
  207.   Result := Count;
  208.   if not ECB.WriteClient(ECB.ConnID, @Buffer, DWORD(Result), 0) then
  209.     Result := -1;
  210. end;
  211.  
  212. function TISAPIRequest.WriteString(const AString: string): Boolean;
  213. begin
  214.   Result := WriteClient(Pointer(AString)^, Length(AString)) = Length(AString);
  215. end;
  216.  
  217. { TISAPIResponse }
  218.  
  219. constructor TISAPIResponse.Create(HTTPRequest: TWebRequest);
  220. begin
  221.   inherited Create(HTTPRequest);
  222.   if FHTTPRequest.ProtocolVersion = '' then
  223.     Version := '1.0';
  224.   StatusCode := 200;
  225.   LastModified := -1;
  226.   Expires := -1;
  227.   Date := -1;
  228.   ContentType := 'text/html';
  229. end;
  230.  
  231. function TISAPIResponse.GetContent: string;
  232. begin
  233.   Result := FContent;
  234. end;
  235.  
  236. function TISAPIResponse.GetDateVariable(Index: Integer): TDateTime;
  237. begin
  238.   if (Index >= Low(FDateVariables)) and (Index <= High(FDateVariables)) then
  239.     Result := FDateVariables[Index]
  240.   else Result := 0.0;
  241. end;
  242.  
  243. function TISAPIResponse.GetIntegerVariable(Index: Integer): Integer;
  244. begin
  245.   if (Index >= Low(FIntegerVariables)) and (Index <= High(FIntegerVariables)) then
  246.     Result := FIntegerVariables[Index]
  247.   else Result := -1;
  248. end;
  249.  
  250. function TISAPIResponse.GetLogMessage: string;
  251. begin
  252.   Result := TISAPIRequest(HTTPRequest).ECB.lpszLogData;
  253. end;
  254.  
  255. function TISAPIResponse.GetStatusCode: Integer;
  256. begin
  257.   Result := FStatusCode;
  258. end;
  259.  
  260. function TISAPIResponse.GetStringVariable(Index: Integer): string;
  261. begin
  262.   if (Index >= Low(FStringVariables)) and (Index <= High(FStringVariables)) then
  263.     Result := FStringVariables[Index];
  264. end;
  265.  
  266. function TISAPIResponse.Sent: Boolean;
  267. begin
  268.   Result := FSent;
  269. end;
  270.  
  271. procedure TISAPIResponse.SetContent(const Value: string);
  272. begin
  273.   FContent := Value;
  274.   if ContentStream = nil then
  275.     ContentLength := Length(FContent);
  276. end;
  277.  
  278. procedure TISAPIResponse.SetDateVariable(Index: Integer; const Value: TDateTime);
  279. begin
  280.   if (Index >= Low(FDateVariables)) and (Index <= High(FDateVariables)) then
  281.     if Value <> FDateVariables[Index] then
  282.       FDateVariables[Index] := Value;
  283. end;
  284.  
  285. procedure TISAPIResponse.SetIntegerVariable(Index: Integer; Value: Integer);
  286. begin
  287.   if (Index >= Low(FIntegerVariables)) and (Index <= High(FIntegerVariables)) then
  288.     if Value <> FIntegerVariables[Index] then
  289.       FIntegerVariables[Index] := Value;
  290. end;
  291.  
  292. procedure TISAPIResponse.SetLogMessage(const Value: string);
  293. begin
  294.   StrPLCopy(TISAPIRequest(HTTPRequest).ECB.lpszLogData, Value, HSE_LOG_BUFFER_LEN);
  295. end;
  296.  
  297. {!! Strings not to be resourced !!}
  298. procedure TISAPIResponse.SetStatusCode(Value: Integer);
  299. begin
  300.   if FStatusCode <> Value then
  301.   begin
  302.     FStatusCode := Value;
  303.     ReasonString := StatusString(Value);
  304.   end;
  305. end;
  306.  
  307. procedure TISAPIResponse.SetStringVariable(Index: Integer; const Value: string);
  308. begin
  309.   if (Index >= Low(FStringVariables)) and (Index <= High(FStringVariables)) then
  310.     FStringVariables[Index] := Value;
  311. end;
  312.  
  313. procedure TISAPIResponse.SendResponse;
  314. var
  315.   StatusString: string;
  316.   Headers: string;
  317.   I: Integer;
  318.  
  319.   procedure AddHeaderItem(const Item, FormatStr: string);
  320.   begin
  321.     if Item <> '' then
  322.       Headers := Headers + Format(FormatStr, [Item]);
  323.   end;
  324.  
  325. begin
  326.   if HTTPRequest.ProtocolVersion <> '' then
  327.   begin
  328.     TISAPIRequest(HTTPRequest).ECB.dwHttpStatusCode := StatusCode;
  329.     if (ReasonString <> '') and (StatusCode > 0) then
  330.       StatusString := Format('%d %s', [StatusCode, ReasonString])
  331.     else StatusString := '200 OK';
  332.     AddHeaderItem(Allow, 'Allow: %s'#13#10);
  333.     for I := 0 to Cookies.Count - 1 do
  334.       AddHeaderItem(Cookies[I].HeaderValue, 'Set-Cookie: %s'#13#10);
  335.     AddHeaderItem(DerivedFrom, 'Derived-From: %s'#13#10);
  336.     if Expires > 0 then
  337.       Headers := Headers +
  338.         Format(FormatDateTime('"Expires: "' + sDateFormat + ' "GMT"'#13#10, Expires),
  339.           [DayOfWeekStr(Expires), MonthStr(Expires)]);
  340.     if LastModified > 0 then
  341.       Headers := Headers +
  342.         Format(FormatDateTime('"Last-Modified: "' + sDateFormat + ' "GMT"'#13#10,
  343.           LastModified), [DayOfWeekStr(LastModified), MonthStr(LastModified)]);
  344.     AddHeaderItem(Title, 'Title: %s'#13#10);
  345.     AddHeaderItem(WWWAuthenticate, 'WWW-Authenticate: %s'#13#10);
  346.     AddCustomHeaders(Headers);
  347.     AddHeaderItem(ContentVersion, 'Content-Version: %s'#13#10);
  348.     AddHeaderItem(ContentEncoding, 'Content-Encoding: %s'#13#10);
  349.     AddHeaderItem(ContentType, 'Content-Type: %s'#13#10);
  350.     if (Content <> '') or (ContentStream <> nil) then
  351.       AddHeaderItem(IntToStr(ContentLength), 'Content-Length: %s'#13#10);
  352.     Headers := Headers + 'Content:'#13#10#13#10;
  353.     with TISAPIRequest(FHTTPRequest) do
  354.       ECB.ServerSupportFunction(ECB.ConnID, HSE_REQ_SEND_RESPONSE_HEADER,
  355.         PChar(StatusString), nil, LPDWORD(Headers));
  356.   end;
  357.   if ContentStream = nil then
  358.     HTTPRequest.WriteString(Content)
  359.   else if ContentStream <> nil then
  360.   begin
  361.     SendStream(ContentStream);
  362.     ContentStream := nil; // Drop the stream
  363.   end;
  364.   FSent := True;
  365. end;
  366.  
  367. procedure TISAPIResponse.SendRedirect(const URI: string);
  368. begin
  369.   with TISAPIRequest(FHTTPRequest) do
  370.     ECB.ServerSupportFunction(ECB.ConnID, HSE_REQ_SEND_URL_REDIRECT_RESP,
  371.       PChar(URI), nil, nil);
  372.   FSent := True;
  373. end;
  374.  
  375. procedure TISAPIResponse.SendStream(AStream: TStream);
  376. var
  377.   Buffer: array[0..8191] of Byte;
  378.   BytesToSend: Integer;
  379. begin
  380.   while AStream.Position < AStream.Size do
  381.   begin
  382.     BytesToSend := AStream.Read(Buffer, SizeOf(Buffer));
  383.     FHTTPRequest.WriteClient(Buffer, BytesToSend);
  384.   end;
  385. end;
  386.  
  387. { TISAPIApplication }
  388.  
  389. procedure HandleServerException(E: Exception; var ECB: TEXTENSION_CONTROL_BLOCK);
  390. var
  391.   ResultText, ResultHeaders: string;
  392.   Size: DWORD;
  393. begin
  394.   ECB.dwHTTPStatusCode := 500;
  395.   ResultText := Format(sInternalServerError, [E.ClassName, E.Message]);
  396.   ResultHeaders := Format(
  397.     'Content-Type: text/html'#13#10 +     //Not resourced
  398.     'Content-Length: %d'#13#10 +          //Not resourced
  399.     'Content:'#13#10#13#10, [Length(ResultText)]); //Not resourced
  400.   ECB.ServerSupportFunction(ECB.ConnID, HSE_REQ_SEND_RESPONSE_HEADER,
  401.     PChar('500 ' + E.Message), @Size, LPDWORD(ResultHeaders));
  402.   Size := Length(ResultText);
  403.   ECB.WriteClient(ECB.ConnID, Pointer(ResultText), Size, 0);
  404. end;
  405.  
  406. function TISAPIApplication.GetExtensionVersion(var Ver: THSE_VERSION_INFO): BOOL;
  407. begin
  408.   try
  409.     Ver.dwExtensionVersion := MakeLong(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
  410.     StrLCopy(Ver.lpszExtensionDesc, PChar(Title), HSE_MAX_EXT_DLL_NAME_LEN);
  411.     Result := BOOL(1); // This is so that the Apache web server will know what "True" really is
  412.   except
  413.     Result := False;
  414.   end;
  415. end;
  416.  
  417. function TISAPIApplication.HttpExtensionProc(var ECB: TEXTENSION_CONTROL_BLOCK): DWORD;
  418. var
  419.   HTTPRequest: TISAPIRequest;
  420.   HTTPResponse: TISAPIResponse;
  421. begin
  422.   try
  423.     HTTPRequest := NewRequest(ECB);
  424.     try
  425.       HTTPResponse := NewResponse(HTTPRequest);
  426.       try
  427.         if HandleRequest(HTTPRequest, HTTPResponse) then
  428.           Result := HSE_STATUS_SUCCESS
  429.         else Result := HSE_STATUS_ERROR;
  430.       finally
  431.         HTTPResponse.Free;
  432.       end;
  433.     finally
  434.       HTTPRequest.Free;
  435.     end;
  436.   except
  437.     HandleServerException(Exception(ExceptObject), ECB);
  438.     Result := HSE_STATUS_ERROR;
  439.   end;
  440. end;
  441.  
  442. function TISAPIApplication.NewRequest(var AECB: TEXTENSION_CONTROL_BLOCK): TISAPIRequest;
  443. begin
  444.   Result := TISAPIRequest.Create(@AECB);
  445. end;
  446.  
  447. function TISAPIApplication.NewResponse(ISAPIRequest: TISAPIRequest): TISAPIResponse;
  448. begin
  449.   Result := TISAPIResponse.Create(ISAPIRequest);
  450. end;
  451.  
  452. function TISAPIApplication.TerminateExtension(dwFlags: DWORD): BOOL;
  453. begin
  454.   Result := BOOL(1); // This is so that the Apache web server will know what "True" really is
  455. end;
  456.  
  457. // ISAPI interface
  458.  
  459. function GetExtensionVersion(var Ver: THSE_VERSION_INFO): BOOL;
  460. begin
  461.   Result := (Application as TISAPIApplication).GetExtensionVersion(Ver);
  462. end;
  463.  
  464. function HttpExtensionProc(var ECB: TEXTENSION_CONTROL_BLOCK): DWORD;
  465. begin
  466.   Result := (Application as TISAPIApplication).HttpExtensionProc(ECB);
  467. end;
  468.  
  469. function TerminateExtension(dwFlags: DWORD): BOOL;
  470. begin
  471.   Result := (Application as TISAPIApplication).TerminateExtension(dwFlags);
  472. end;
  473.  
  474. procedure InitApplication;
  475. begin
  476.   Application := TISAPIApplication.Create(nil);
  477. end;
  478.  
  479. procedure DoneApplication;
  480. begin
  481.   Application.Free;
  482.   Application := nil;
  483. end;
  484.  
  485. initialization
  486.   InitApplication;
  487. finalization
  488.   DoneApplication;
  489. end.
  490.