forked from zamronypj/fano
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix bug which allow hidden dot files to be served by TStaticFilesMidd…
…leware
- Loading branch information
Showing
2 changed files
with
152 additions
and
70 deletions.
There are no files selected for viewing
130 changes: 130 additions & 0 deletions
130
src/Middleware/BuiltIns/BaseStaticFilesMiddlewareImpl.pas
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
{*! | ||
* Fano Web Framework (https://fanoframework.github.io) | ||
* | ||
* @link https://github.com/fanoframework/fano | ||
* @copyright Copyright (c) 2018 - 2022 Zamrony P. Juhara | ||
* @license https://github.com/fanoframework/fano/blob/master/LICENSE (MIT) | ||
*} | ||
|
||
unit BaseStaticFilesMiddlewareImpl; | ||
|
||
interface | ||
|
||
{$MODE OBJFPC} | ||
{$H+} | ||
|
||
uses | ||
|
||
RequestIntf, | ||
ResponseIntf, | ||
MiddlewareIntf, | ||
RouteArgsReaderIntf, | ||
RequestHandlerIntf, | ||
ReadOnlyKeyValuePairIntf, | ||
InjectableObjectImpl; | ||
|
||
type | ||
|
||
(*!------------------------------------------------ | ||
* base middleware class that serves static files from | ||
* a base directory. | ||
*------------------------------------------------- | ||
* Content type of response will be determined | ||
* using file extension that is stored in fMimeTypes | ||
* if not set then 'application/octet-stream' is assumed | ||
*------------------------------------------------- | ||
* @author Zamrony P. Juhara <[email protected]> | ||
*-------------------------------------------------*) | ||
TBaseStaticFilesMiddleware = class(TInjectableObject, IMiddleware) | ||
protected | ||
fBaseDirectory : string; | ||
fMimeTypes : IReadOnlyKeyValuePair; | ||
function getContentTypeFromFilename(const filename : string) : string; | ||
|
||
(*!------------------------------------------- | ||
* clean filepath (if required) | ||
*-------------------------------------------- | ||
* This method is provided so that descendant | ||
* have opportunity to clean file path | ||
*-------------------------------------------- | ||
* @param filePath original file path | ||
* @return new cleaned file path | ||
*--------------------------------------------*) | ||
function clean(const filePath: string) : string; virtual; | ||
public | ||
constructor create( | ||
const baseDir : string; | ||
const mimeTypes : IReadOnlyKeyValuePair | ||
); | ||
|
||
function handleRequest( | ||
const request : IRequest; | ||
const response : IResponse; | ||
const args : IRouteArgsReader; | ||
const nextMdlwr : IRequestHandler | ||
) : IResponse; virtual; | ||
end; | ||
|
||
implementation | ||
|
||
uses | ||
|
||
SysUtils, | ||
FileResponseImpl; | ||
|
||
constructor TBaseStaticFilesMiddleware.create( | ||
const baseDir : string; | ||
const mimeTypes : IReadOnlyKeyValuePair | ||
); | ||
begin | ||
fBaseDirectory := baseDir; | ||
fMimeTypes := mimeTypes; | ||
end; | ||
|
||
function TBaseStaticFilesMiddleware.getContentTypeFromFilename( | ||
const filename : string | ||
) : string; | ||
var ext : string; | ||
begin | ||
ext := ExtractFileExt(filename); | ||
//remove dot from ext | ||
ext := copy(ext, 2, length(ext)-1); | ||
if (fMimeTypes.has(ext)) then | ||
begin | ||
result := fMimeTypes.getValue(ext); | ||
end else | ||
begin | ||
//set default | ||
result := 'application/octet-stream'; | ||
end; | ||
end; | ||
|
||
function TBaseStaticFilesMiddleware.clean(const filePath: string) : string; | ||
begin | ||
result := filePath; | ||
end; | ||
|
||
function TBaseStaticFilesMiddleware.handleRequest( | ||
const request : IRequest; | ||
const response : IResponse; | ||
const args : IRouteArgsReader; | ||
const nextMdlwr : IRequestHandler | ||
) : IResponse; | ||
var filename : string; | ||
begin | ||
filename := fBaseDirectory + clean(request.uri().getPath()); | ||
if fileExists(filename) then | ||
begin | ||
//serve file | ||
result := TFileResponse.create( | ||
response.headers(), | ||
getContentTypeFromFilename(filename), | ||
filename | ||
); | ||
end else | ||
begin | ||
//file not found, just pass to next middleware | ||
result := nextMdlwr.handleRequest(request, response, args); | ||
end; | ||
end; | ||
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,13 +15,7 @@ interface | |
|
||
uses | ||
|
||
RequestIntf, | ||
ResponseIntf, | ||
MiddlewareIntf, | ||
RouteArgsReaderIntf, | ||
RequestHandlerIntf, | ||
ReadOnlyKeyValuePairIntf, | ||
InjectableObjectImpl; | ||
BaseStaticFilesMiddlewareImpl; | ||
|
||
type | ||
|
||
|
@@ -35,80 +29,38 @@ interface | |
*------------------------------------------------- | ||
* @author Zamrony P. Juhara <[email protected]> | ||
*-------------------------------------------------*) | ||
TStaticFilesMiddleware = class(TInjectableObject, IMiddleware) | ||
TStaticFilesMiddleware = class(TBaseStaticFilesMiddleware) | ||
protected | ||
fBaseDirectory : string; | ||
fMimeTypes : IReadOnlyKeyValuePair; | ||
function getContentTypeFromFilename(const filename : string) : string; | ||
(*!------------------------------------------- | ||
* clean filepath avoid serve hidden dot files in unix | ||
*-------------------------------------------- | ||
* @param filePath original file path | ||
* @return new cleaned file path | ||
*--------------------------------------------*) | ||
function clean(const filePath: string) : string; override; | ||
public | ||
constructor create( | ||
const baseDir : string; | ||
const mimeTypes : IReadOnlyKeyValuePair | ||
); | ||
|
||
function handleRequest( | ||
const request : IRequest; | ||
const response : IResponse; | ||
const args : IRouteArgsReader; | ||
const nextMdlwr : IRequestHandler | ||
) : IResponse; virtual; | ||
end; | ||
|
||
implementation | ||
|
||
uses | ||
|
||
SysUtils, | ||
FileResponseImpl; | ||
SysUtils; | ||
|
||
constructor TStaticFilesMiddleware.create( | ||
const baseDir : string; | ||
const mimeTypes : IReadOnlyKeyValuePair | ||
); | ||
begin | ||
fBaseDirectory := baseDir; | ||
fMimeTypes := mimeTypes; | ||
end; | ||
|
||
function TStaticFilesMiddleware.getContentTypeFromFilename( | ||
const filename : string | ||
) : string; | ||
var ext : string; | ||
(*!------------------------------------------- | ||
* clean filepath avoid serve hidden dot files in unix | ||
*-------------------------------------------- | ||
* @param filePath original file path | ||
* @return new cleaned file path | ||
*--------------------------------------------*) | ||
function TStaticFilesMiddleware.clean(const filePath: string) : string; | ||
begin | ||
ext := ExtractFileExt(filename); | ||
//remove dot from ext | ||
ext := copy(ext, 2, length(ext)-1); | ||
if (fMimeTypes.has(ext)) then | ||
begin | ||
result := fMimeTypes.getValue(ext); | ||
end else | ||
begin | ||
//set default | ||
result := 'application/octet-stream'; | ||
end; | ||
// for example if filePath contain '/.htaccess' we replace it so | ||
// filePath become '/htaccess' | ||
result := stringReplace(filePath, '/.', '/', [rfReplaceAll]); | ||
// just paranoia handle .. too | ||
result := stringReplace(result, '..', '', [rfReplaceAll]); | ||
end; | ||
|
||
function TStaticFilesMiddleware.handleRequest( | ||
const request : IRequest; | ||
const response : IResponse; | ||
const args : IRouteArgsReader; | ||
const nextMdlwr : IRequestHandler | ||
) : IResponse; | ||
var filename : string; | ||
begin | ||
filename := fBaseDirectory + request.uri().getPath(); | ||
if fileExists(filename) then | ||
begin | ||
//serve file | ||
result := TFileResponse.create( | ||
response.headers(), | ||
getContentTypeFromFilename(filename), | ||
filename | ||
); | ||
end else | ||
begin | ||
//file not found, just pass to next middleware | ||
result := nextMdlwr.handleRequest(request, response, args); | ||
end; | ||
end; | ||
end. |