0

I have an HTTP Web Broker Server which needs to handle requests such as...

http://myserver.com/versions/2013.6.0.0/ML/Files/SomeFile.exe

The server its self just for reference is hosting information and specific individual files for automatic software updates (where many individual updatable files are involved).

So I create a request handler in the Web Module for this, but the path info of this request (Request.PathInfo) is...

/versions/2013.6.0.0/ML/Files/SomeFile.exe

...but it needs to detect just the first part...

/versions

Once it detects this, then it needs to handle the following version number...

/2013.6.0.0

...in which case the web server knows the client's requesting data specific to that version. The next part is the application Edition...

/ML

Then I specify that I'm looking for specific files related to this version/edition combo...

/Files

...and finally the actual file...

/SomeFile.exe

This URL does not represent any actual directory on the web server - the actual location of SomeFile.exe could be entirely different (another story of how it detects where to find those files).

The question is, How should I detect this hierarchy of Path Info and handle it accordingly using the request handler actions (TCollection) available in the Web Module? Because the built-in request handlers assume that there'll only be one level, but this server could have virtually endless levels.

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
  • 2
    Detect? Look for more than one slash in the path. Handle? Split the query by slashes and do whatever you want with the result. Where's the difficulty? I'm sure I don't understand the issue. – Rob Kennedy Jun 29 '13 at 20:03

3 Answers3

1

Based off of Rob's comment on the question, I quickly realized how simple this was to accomplish. Using a String List, break down the Path Info into different list items dividing each slash. Then, use whatever custom mechanism to evaluate the first one, then second, and so on. Each variation will trigger a different handler procedure, like so...

unit uUpdateServerWebModule;

interface

uses System.SysUtils, System.Classes, Web.HTTPApp, Update.Common;

type
  TWebModule1 = class(TWebModule)
    procedure WebModule1DefaultHandlerAction(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  private
    procedure HandleVersionCheck(Request: TWebRequest; Response: TWebResponse;
      PathInfo: TStringList);
    procedure HandleInfoCheck(Request: TWebRequest; Response: TWebResponse;
      PathInfo: TStringList);
    procedure HandleException(Request: TWebRequest; Response: TWebResponse;
      PathInfo: TStringList; const Code: Integer);
  public
    { Public declarations }
  end;

var
  WebModuleClass: TComponentClass = TWebModule1;

implementation

{$R *.dfm}

procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
  L: TStringList;
  S, T: String;
  P: Integer;
  procedure A(const Text: String);
  begin
    S:= S + Text + sLineBreak;
  end;
begin
  //Break down URL by slashes into list
  L:= TStringList.Create;
  try
    S:= Request.PathInfo;
    if Copy(S, Length(S)-1, 1) <> '/' then
      S:= S + '/';
    Delete(S, 1, 1);
    while Length(S) > 0 do begin
      P:= Pos('/', S);
      T:= Copy(S, 1, P-1);
      Delete(S, 1, P);
      L.Append(T);
    end;
    if L.Count > 0 then begin
      //Handle path info list
      if LowerCase(L[0]) = 'ver' then begin
        HandleVersionCheck(Request, Response, L);
      end else
      if LowerCase(L[0]) = 'info' then begin
        HandleInfoCheck(Request, Response, L);
      end else begin
        HandleException(Request, Response, L, 0);
      end;
    end else begin
      //No page specified, return default page
      Response.Content:= 'Application Update Server';
    end;
  finally
    L.Free;
  end;
end;

procedure TWebModule1.HandleVersionCheck(Request: TWebRequest;
  Response: TWebResponse; PathInfo: TStringList);
var
  EC: String;
  FN: String;
  Ver: TVersion;
begin
  if LowerCase(PathInfo[1]) = 'info' then begin
    //Next parameter: Edition Code
    EC:= LowerCase(PathInfo[2]);
    if (EC = 'ml') or (EC = 'sl') or (EC = 'lt') then begin
      //Return current version info for specified software edition...
    end else begin
      HandleException(Request, Response, PathInfo, 4);
    end;
  end else begin
    //Next parameter: Version Number(s)
    Ver:= TVersion.Create;
    try
      Ver.Version:= PathInfo[1];
      if (Ver.Ver1 > 0) and (Ver.Ver2 > 0) then begin
        //Next parameter: Edition Code
        EC:= LowerCase(PathInfo[2]);
        if (EC = 'ml') or (EC = 'sl') or (EC = 'lt') then begin
          //Next parameter: Version/Edition Commands
          if LowerCase(PathInfo[3]) = 'update' then begin
            //Return stream of full verion update installer app...
            Response.ContentType:= 'application/octet-stream';

          end else
          if LowerCase(PathInfo[3]) = 'files' then begin
            //Next parameter: Specific filename
            FN:= PathInfo[4];
            //Return stream of specific file...
            Response.ContentType:= 'application/octet-stream';

          end else begin
            HandleException(Request, Response, PathInfo, 1);
          end;
        end else begin
          HandleException(Request, Response, PathInfo, 2);
        end;
      end else begin
        HandleException(Request, Response, PathInfo, 3);
      end;
    finally
      Ver.Free;
    end;
  end;
end;

procedure TWebModule1.HandleInfoCheck(Request: TWebRequest;
  Response: TWebResponse; PathInfo: TStringList);
begin
  //Return information about all software and their current versions...
end;

procedure TWebModule1.HandleException(Request: TWebRequest;
  Response: TWebResponse; PathInfo: TStringList; const Code: Integer);
begin
  //Return error information...
  Response.Content:= 'EXCEPTION: '+IntToStr(Code);
end;

end.

That is the full code of the Web Module (main logic)

The primary handling is done from the Default Handler. Essentially all incoming requests are processed through this procedure WebModule1DefaultHandlerAction which handles each incoming request. From here, it first identifies if there is any path specified at all. If not, it just returns the main landing page. Otherwise, it will first read the first path listed, and call another handler procedure depending on the requested name. In this case, it handles the versions request and further downloads a particular file (SomeFile.exe) to be saved in a particular destination.

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
0

Your Split code could have been done by:

function Split( StringToSplit: string; SplitChar: Char = '/' ): TStringlist;
begin
  Result                 := TStringlist.Create;
  Result.Delimiter       := SplitChar;
  Result.StrictDelimiter := True;
  Result.DelimitedText   := StringToSplit;
end;
Toby
  • 355
  • 6
  • 14
0

You need to add a trailing character "/" to avoid a memory overflow error. If one character per string is passed.

.... S:= Request.PathInfo**+'/'**; ...

Eugene
  • 1