Search code examples
delphiurlwebrequesthttpserver

Web Server URL - Detecting PathInfo Hierarchy


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.


Solution

  • 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.