Sign in Join
Wiki copytemporary

fpwebfile

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:

pascal
Procedure RegisterFileLocation(Const ALocation,ADirectory : String);

like so:

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

after this call, an url

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

will attempt to serve the file

pascal
/home/myuser/public_html/index.html

You can stop serving files with UnRegisterFileLocation

pascal
Procedure UnRegisterFileLocation(Const ALocation: String);

like this:

pascal
  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:

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

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

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

when invoked like this:

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

The URL

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

will be correctly served.

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

pascal
http://localhost:8080/

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

pascal
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:

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

For example, the following call:

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

Will enable the following REST URL:

pascal
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:

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

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

pascal
Authorization: Bearer mysecret

The 4 CRUD operations are supported in the REST API.

GET

use the GET command to get a list of locations:

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

This will result in something like

pascal
{
  "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:

pascal
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:

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

Using fphttpclient:

pascal
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:

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

pascal
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:

pascal
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:

pascal
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:

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


From the Free Pascal wiki, page "fpwebfile", as it stood on 2021-09-15 after a change by Mattias2, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 1, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.