├── boss.json ├── README.md ├── .gitignore ├── boss-lock.json └── src └── Horse.StaticFiles.pas /boss.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "horse-staticfiles", 3 | "description": "", 4 | "version": "2.0.0", 5 | "homepage": "", 6 | "mainsrc": "src/", 7 | "projects": [], 8 | "dependencies": { 9 | "github.com/HashLoad/horse": "^2.0.0", 10 | "github.com/paolo-rossi/delphi-jose-jwt": "^v2.5.2" 11 | } 12 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # horse-staticfiles 2 | 3 | Middleware for StaticFiles in HORSE 4 | 5 | Sample Horse Server to serve static files 6 | ```delphi 7 | uses 8 | Horse, 9 | Horse.StaticFiles; 10 | 11 | begin 12 | THorse.Use('/static', HorseStaticFile('.\static', ['index.html'])) 13 | THorse.Listen; 14 | end; 15 | ``` 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | modules/ 2 | dist/ 3 | static/ 4 | **/Win32/ 5 | **/Win64/ 6 | **/Linux64/ 7 | **/__history/ 8 | **/__recovery/ 9 | src/*.~* 10 | *.res 11 | *.exe 12 | *.dll 13 | *.bpl 14 | *.bpi 15 | *.dcp 16 | *.so 17 | *.apk 18 | *.drc 19 | *.map 20 | *.dres 21 | *.rsm 22 | *.tds 23 | *.dcu 24 | *.lib 25 | *.a 26 | *.o 27 | *.ocx 28 | *.local 29 | *.identcache 30 | *.projdata 31 | *.tvsconfig 32 | *.dsk 33 | *.dcu 34 | *.exe 35 | *.so 36 | *.~* 37 | *.a 38 | *.stat -------------------------------------------------------------------------------- /boss-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash": "445774d05b3e1b873cfcdb89032b3e1f", 3 | "updated": "2019-10-29T12:23:27.0587203-03:00", 4 | "installedModules": { 5 | "github.com/hashload/horse": { 6 | "name": "horse", 7 | "version": "v2.0.1", 8 | "hash": "a9e80dbc40536b989ad1089a85bb1c5b", 9 | "artifacts": {}, 10 | "failed": false, 11 | "changed": false 12 | }, 13 | "github.com/paolo-rossi/delphi-jose-jwt": { 14 | "name": "delphi-jose-jwt", 15 | "version": "v2.5.2", 16 | "hash": "589e56e202f3acd41b4021314c54518c", 17 | "artifacts": {}, 18 | "failed": false, 19 | "changed": false 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/Horse.StaticFiles.pas: -------------------------------------------------------------------------------- 1 | unit Horse.StaticFiles; 2 | 3 | interface 4 | 5 | uses 6 | 7 | System.Generics.Collections, 8 | System.Classes, 9 | System.SysUtils, 10 | Horse; 11 | 12 | type 13 | 14 | THorseStaticFileCallback = class 15 | private 16 | FPathRoot: string; 17 | FDefaultFiles: TArray; 18 | public 19 | class function New: THorseStaticFileCallback; 20 | function SetPathRoot(APathRoot: string): THorseStaticFileCallback; 21 | function SetDefaultFiles(ADefaultFiles: TArray): THorseStaticFileCallback; 22 | procedure Callback(AHorseRequest: THorseRequest; AHorseResponse: THorseResponse; ANext: TProc); 23 | end; 24 | 25 | THorseStaticFileManager = class 26 | private 27 | FCallbackList: TObjectList; 28 | class var FDefaultManager: THorseStaticFileManager; 29 | procedure SetCallbackList(const Value: TObjectList); 30 | protected 31 | class function GetDefaultManager: THorseStaticFileManager; static; 32 | public 33 | constructor Create; 34 | destructor Destroy; override; 35 | property CallbackList: TObjectList read FCallbackList write SetCallbackList; 36 | class destructor UnInitialize; 37 | class property DefaultManager: THorseStaticFileManager read GetDefaultManager; 38 | end; 39 | 40 | function HorseStaticFile(APathRoot: string; const ADefaultFiles: TArray = []): THorseCallback; overload; 41 | 42 | implementation 43 | 44 | uses 45 | System.IOUtils, 46 | System.Net.Mime; 47 | 48 | function HorseStaticFile(APathRoot: string; const ADefaultFiles: TArray = []): THorseCallback; overload; 49 | var 50 | LHorseStaticFileCallback: THorseStaticFileCallback; 51 | begin 52 | LHorseStaticFileCallback := THorseStaticFileCallback.Create; 53 | 54 | THorseStaticFileManager 55 | .DefaultManager 56 | .CallbackList 57 | .Add(LHorseStaticFileCallback); 58 | 59 | Result := 60 | LHorseStaticFileCallback 61 | .SetPathRoot(APathRoot) 62 | .SetDefaultFiles(ADefaultFiles) 63 | .Callback; 64 | end; 65 | 66 | { THorseStaticFileCallback } 67 | 68 | procedure THorseStaticFileCallback.Callback(AHorseRequest: THorseRequest; AHorseResponse: THorseResponse; ANext: TProc); 69 | var 70 | LFileStream: TFileStream; 71 | LNormalizeFileName: string; 72 | LType: string; 73 | LKind: TMimeTypes.TKind; 74 | I: Integer; 75 | begin 76 | 77 | LNormalizeFileName := AHorseRequest.RawWebRequest.RawPathInfo.TrimLeft(['/']); 78 | 79 | LNormalizeFileName := LNormalizeFileName.Replace('/', TPath.DirectorySeparatorChar); 80 | 81 | LNormalizeFileName := TPath.Combine(FPathRoot, LNormalizeFileName); 82 | 83 | if (TDirectory.Exists(LNormalizeFileName)) or (ExtractFileName(LNormalizeFileName).IsEmpty) then 84 | begin 85 | for I := Low(FDefaultFiles) to High(FDefaultFiles) do 86 | begin 87 | if TFile.Exists(TPath.Combine(LNormalizeFileName, FDefaultFiles[I])) then 88 | begin 89 | LNormalizeFileName := TPath.Combine(LNormalizeFileName, FDefaultFiles[I]); 90 | Break; 91 | end; 92 | end; 93 | end; 94 | 95 | if TFile.Exists(LNormalizeFileName) then 96 | begin 97 | LFileStream := TFileStream.Create(LNormalizeFileName, fmShareDenyNone or fmOpenRead); 98 | AHorseResponse.RawWebResponse.ContentStream := LFileStream; 99 | TMimeTypes.Default.GetFileInfo(LNormalizeFileName, LType, LKind); 100 | AHorseResponse.RawWebResponse.ContentType := LType; 101 | AHorseResponse.RawWebResponse.StatusCode := 200; 102 | AHorseResponse.RawWebResponse.SendResponse; 103 | raise EHorseCallbackInterrupted.Create; 104 | end; 105 | 106 | ANext(); 107 | end; 108 | 109 | class function THorseStaticFileCallback.New: THorseStaticFileCallback; 110 | begin 111 | Result := THorseStaticFileCallback.Create; 112 | end; 113 | 114 | function THorseStaticFileCallback.SetPathRoot(APathRoot: string): THorseStaticFileCallback; 115 | begin 116 | Result := Self; 117 | FPathRoot := APathRoot; 118 | end; 119 | 120 | function THorseStaticFileCallback.SetDefaultFiles(ADefaultFiles: TArray): THorseStaticFileCallback; 121 | begin 122 | Result := Self; 123 | FDefaultFiles := ADefaultFiles; 124 | end; 125 | 126 | { THorseStaticFileManager } 127 | 128 | constructor THorseStaticFileManager.Create; 129 | begin 130 | FCallbackList := TObjectList.Create(True); 131 | end; 132 | 133 | destructor THorseStaticFileManager.Destroy; 134 | begin 135 | FCallbackList.Free; 136 | inherited; 137 | end; 138 | 139 | class function THorseStaticFileManager.GetDefaultManager: THorseStaticFileManager; 140 | begin 141 | if FDefaultManager = nil then 142 | FDefaultManager := THorseStaticFileManager.Create; 143 | Result := FDefaultManager; 144 | end; 145 | 146 | procedure THorseStaticFileManager.SetCallbackList(const Value: TObjectList); 147 | begin 148 | FCallbackList := Value; 149 | end; 150 | 151 | class destructor THorseStaticFileManager.UnInitialize; 152 | begin 153 | FreeAndNil(FDefaultManager); 154 | end; 155 | 156 | end. 157 | --------------------------------------------------------------------------------