fpwebfile

From Lazarus wiki
Jump to navigationJump to search

Intro

fpwebfile is a unit with a simple HTTP module (TFPCustomFileModule and TSimpleFileModule) to serve files in fcl-web.

You don't need to do anything except register a location from which to serve files. The module does the rest.

The unit also has a TFPWebFileLocationAPIModule module which allows to remotely manage the file locations which are served by the TSimpleFileModule class in a JSON & REST fashion.

Usage

Various locations

To serve files from a location, all you need to do is call RegisterFileLocation:

Procedure RegisterFileLocation(Const ALocation,ADirectory : String);

like so:

  RegisterFileLocation('home','/home/myuser/public_html');

after this call, an url

http://localhost:8080/home/index.html

will attempt to serve the file

/home/myuser/public_html/index.html

You can stop serving files with UnRegisterFileLocation

Procedure UnRegisterFileLocation(Const ALocation: String);

like this:

  UnRegisterFileLocation('home');

Default location

Note that all paths registered with RegisterFileLocation must have a prefix: the name of the location. This means that the following URL cannot be served:

http://localhost:8080/index.html

However, to serve this kind of file anyway, the TSimpleFileModule can be registered as the default route:

Class Procedure TSimpleFileModule.RegisterDefaultRoute(OverAllDefault : Boolean = True);

when invoked like this:

TSimpleFileModule.BaseDir:='/home/myuser/public_html/';
TSimpleFileModule.RegisterDefaultRoute;

The URL

http://localhost:8080/index.html

will be correctly served.

If the URL does not contain a document, but only a directory:

http://localhost:8080/

You can tell the TSimpleFileModule to serve file index.html by setting

TSimpleFileModule.IndexPageName:='index.html';

You can mix RegisterFileLocation and TSimpleFileModule.RegisterDefaultRoute. When an URL is checked, the various locations registered with RegisterFileLocation will be tried first. If no location matches, the default location will be tried.

Registration order

Note that the order in which you register locations is important: the first match on the initial element of the URL path will be used.

When you use both RegisterFileLocation and TSimpleFileModule.RegisterDefaultRoute, then the locations created with RegisterFileLocation will always be checked before the default route.

API

The TFPWebFileLocationAPIModule module is a ready-to-run module which allows to remotely manage the file locations. It is basically a REST interface to the RegisterFileLocation and UnRegisterFileLocation routines.

The class can be registered with the followin class method:

Class procedure RegisterFileLocationAPI(Const aPath,aPassword : String);

For example, the following call:

TFPWebFileLocationAPIModule.RegisterFileLocationAPI('_locations','mysecret');

Will enable the following REST URL:

http://localhost:8080/_locations/

If a password was supplied in the RegisterFileLocationAPI call, you must authenticate requests to the locations endpoint.

This can be done in one of 2 ways:

  1. Provide the key with the APIKey query variable.
  2. Or provide the key as an Authorization bearer token.

The first way results in an URL like this:

http://localhost:3003/_locations/?APIKey=mysecret

For the second way, the following header must be added to the request

Authorization: Bearer mysecret

The 4 CRUD operations are supported in the REST API.

GET

use the GET command to get a list of locations:

wget -q 'http://localhost:3003/_locations/?APIKey=mysecret&fmt=1' -O -

This will result in something like

{
  "data" : [
    {
      "location" : "tmp",
      "path" : "/tmp/"
    },
    {
      "location" : "*",
      "path" : "/home/michael/public_html/"
    }
  ]
}

if you omit the fmt=1 query parameter then the returned JSON will not be formatted.

POST

a POST command will register a new location. The content-type must be application/json, and the payload a JSON object with 2 keys:

location
the name of the location
path
the directory for the location

Several checks are done:

  1. The path must exist and be a directory.
  2. The location name may not be empty.
  3. The location name may not contain / characters.
  4. The location name may not yet be registered.

So, for example, the above 'tmp' location may be registered as:

wget -q --post-data='{ "location" : "tmp", "path": "/tmp" }' \
  --header="Content-Type: application/json" \
  'http://localhost:3003/_locations/?APIKey=mysecret&fmt=1' -O -

if all went well, the output is the new location:

{ "location" : "tmp", "path" : "/tmp" }

Using fphttpclient:

Uses classes, fphttpclient, fpjson;
...
  JSON:=TStringStream.Create('{'
    +'"location" : "'+StringToJSONString(Location)+'",'
    +'"path": "'+StringToJSONString(Path)+'"'
    +'}');
  Client:=TFPHTTPClient.Create(Nil);
  Response:=TMemoryStream.Create;
  try
    Client.RequestBody:=JSON;
    Client.AddHeader('Content-Type','application/json');
    Client.HTTPMethod('POST',URL,Response,[201,200]);
    Response.Position:=0;
    // maybe: check response
  finally
    Response.Free;
    JSON.Free;
    Client.Free;
  end;

PUT

a PUT command will update an existing location. The content-type must be application/json, and the payload a JSON object with 1 or 2 keys as for the POST call. The name of the location can be part of the url:

wget -q --post-data='{ "path": "/tmp2" }' \
  --header="Content-Type: application/json" \
  --method=PUT 'http://localhost:3003/_locations/tmp?APIKey=mysecret&fmt=1' -O -

If the location element is present in the payload, it will be used to rename the location.

DELETE

a DELETE command will delete an existing location. No payload must be present.

wget -q --method=DELETE 'http://localhost:3003/_locations/tmp?APIKey=mysecret&fmt=1' -O -

If the location element is present in the payload, it will be used to rename the location.

Using fphttpclient:

uses classes, fphttpclient;
... 
  URL:='http://127.0.0.1:'+IntToStr(Port)+'/'+APIPath+'/'+Location+'?APIKey='+APIKey+'&fmt=1';
  Response:=TMemoryStream.Create;
  Client:=TFPHTTPClient.Create(Nil);
  try
    Client.HTTPMethod('DELETE',URL,Response,[204,200]);
    Response.Position:=0;
    // maybe: check response
  finally
    Response.Free;
    Client.Free;
  end;

Customization

File serving

If you want to customize the request treatment, you can always create a descendant of the TSimpleFileModule class and register that:

TSimpleFileModule.DefaultSimpleFileModuleClass:=TMySimpleFileModule;

requests will be handled by creating an instance of TMySimpleFileModule for each file served.

You can use this for example to implement authentication or file filtering.

API

Similarly if you want to customize the API request treatment, you can always create a descendant of the TFPWebFileLocationAPIModule class and register that:

TFPWebFileLocationAPIModule.LocationAPIModuleClass:=TMyFileAPIModule;

requests will be handled by creating an instance of TMyFileAPIModule for each API request.

CORS Considerations

The API allows CORS requests by default. You can disable this by creating a descendent and setting Cors.Enabled to False in the constructor. The file serving mechanism is not CORS enabled.