Skip to content

Commit

Permalink
#11
Browse files Browse the repository at this point in the history
  • Loading branch information
viniciussanchez committed Dec 28, 2023
1 parent 2645fc2 commit 7dc2020
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 48 deletions.
6 changes: 3 additions & 3 deletions boss-lock.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"hash": "98c630592268bb1fca6ec7f8abd99c5a",
"updated": "2023-03-09T23:26:07.798818-03:00",
"updated": "2023-12-28T11:00:24.8682495-03:00",
"installedModules": {
"github.com/hashload/horse": {
"name": "horse",
"version": "3.1.0",
"hash": "fc3b8eefb46c1a3b387e86ca46a9faa1",
"version": "3.1.5",
"hash": "3824f65f99e511ba73d3fdd2f6d16413",
"artifacts": {},
"failed": false,
"changed": false
Expand Down
2 changes: 1 addition & 1 deletion boss.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
"mainsrc": "src/",
"projects": [],
"dependencies": {
"github.com/hashload/horse": "^3.1.0"
"github.com/hashload/horse": "^3.1.4"
}
}
34 changes: 30 additions & 4 deletions samples/delphi/samples.dpr
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,49 @@ program samples;

uses
Horse,
Horse.OctetStream, // It's necessary to use the unit
Horse.OctetStream,
System.Classes,
System.SysUtils;
System.SysUtils,
System.StrUtils;

begin
{$IFDEF MSWINDOWS}
IsConsole := False;
ReportMemoryLeaksOnShutdown := True;
{$ENDIF}

// It's necessary to add the middleware in the Horse:
THorse.Use(OctetStream);

// Add new ContentTypes to work with, the default is always application/octet-stream
// Please, be careful of adding ContentTypes that exist
THorseOctetStreamConfig.GetInstance.AcceptContentType.Add('application/pdf');

THorse.Get('/stream',
procedure(Req: THorseRequest; Res: THorseResponse)
var
LStream: TFileStream;
begin
// Now you can send your stream:
LStream := TFileStream.Create(ExtractFilePath(ParamStr(0)) + 'horse.pdf', fmOpenRead);
Res.Send<TStream>(LStream);
Res.Send<TStream>(LStream).ContentType('application/pdf');
end);

THorse.Listen(9000);
THorse.Post('/stream',
procedure(Req: THorseRequest; Res: THorseResponse)
var
LType: string;
begin
// here you could get the Req.ContentType and save the file based on that
LType := Copy(Req.RawWebRequest.ContentType, Pos('/', Req.RawWebRequest.ContentType) + 1, Req.RawWebRequest.ContentType.Length);
Req.Body<TBytesStream>.SaveToFile(ExtractFilePath(ParamStr(0)) + 'horse-post.' + LType);
Res.Status(THTTPStatus.NoContent);
end);

THorse.Listen(9000,
procedure
begin
Writeln(Format('Server is runing on %s:%d', [THorse.Host, THorse.Port]));
Readln;
end);
end.
6 changes: 6 additions & 0 deletions samples/delphi/samples.dproj
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@
</Platform>
</DeployFile>
<DeployFile LocalName="Win32\Debug\samples.exe" Configuration="Debug" Class="ProjectOutput"/>
<DeployFile LocalName="samples.exe" Configuration="Debug" Class="ProjectOutput">
<Platform Name="Win32">
<RemoteName>samples.exe</RemoteName>
<Overwrite>true</Overwrite>
</Platform>
</DeployFile>
<DeployClass Name="AdditionalDebugSymbols">
<Platform Name="OSX32">
<Operation>1</Operation>
Expand Down
10 changes: 7 additions & 3 deletions samples/lazarus/Samples.lpi
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="11"/>
<Version Value="12"/>
<PathDelim Value="\"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
<CompatibilityMode Value="True"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<Title Value="My Application"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
Expand All @@ -23,7 +23,6 @@
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
<Modes Count="0"/>
</RunParams>
<Units Count="1">
<Unit0>
Expand All @@ -43,6 +42,11 @@
<OtherUnitFiles Value="..\..\src;..\..\modules\horse\src"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
<Linking>
<Debugging>
<DebugInfoType Value="dsDwarf2Set"/>
</Debugging>
</Linking>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
Expand Down
20 changes: 18 additions & 2 deletions samples/lazarus/Samples.lpr
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,38 @@
{$ENDIF}{$ENDIF}
Horse,
Horse.OctetStream, // It's necessary to use the unit
SysUtils, Classes;
SysUtils,
Classes;

procedure GetStream(Req: THorseRequest; Res: THorseResponse);
var
LStream: TFileStream;
begin
// Now you can send your stream:
LStream := TFileStream.Create(ExtractFilePath(ParamStr(0)) + 'horse.pdf', fmOpenRead);
Res.Send<TStream>(LStream);
Res.Send<TStream>(LStream).ContentType('application/pdf');
end;

procedure PostStream(Req: THorseRequest; Res: THorseResponse);
var
LType: string;
begin
// here you could get the Req.ContentType and save the file based on that
LType := Copy(Req.RawWebRequest.ContentType, Pos('/', Req.RawWebRequest.ContentType) + 1, Req.RawWebRequest.ContentType.Length);
Req.Body<TBytesStream>.SaveToFile(ExtractFilePath(ParamStr(0)) + 'horse-post.' + LType);
Res.Status(THTTPStatus.NoContent);
end;

begin
// It's necessary to add the middleware in the Horse:
THorse.Use(OctetStream);

// Add new ContentTypes to work with, the default is always application/octet-stream
// Please, be careful of adding ContentTypes that exist
THorseOctetStream Config.GetInstance.AcceptContentType.Add('application/pdf');

THorse.Get('/stream', GetStream);
THorse.Post('/stream', PostStream);

THorse.Listen(9000);
end.
Expand Down
76 changes: 76 additions & 0 deletions src/Horse.OctetStream.Config.pas
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
unit Horse.OctetStream.Config;

{$IF DEFINED(FPC)}
{$MODE DELPHI}{$H+}
{$ENDIF}

interface

uses
{$IF DEFINED(FPC)}
SysUtils,
Classes,
{$ELSE}
System.SysUtils,
System.Classes,
{$ENDIF}
Generics.Collections;

type
TAcceptContentType = class(TList<string>)
end;

THorseOctetStreamConfig = class
strict private
class var FInstance: THorseOctetStreamConfig;
function GetAcceptContentType: TAcceptContentType;
var FAcceptContentType: TAcceptContentType;
protected
class function GetDefaultInstance: THorseOctetStreamConfig;
public
constructor Create;
destructor Destroy; override;
class function GetInstance: THorseOctetStreamConfig;
class destructor OnDestroy;
property AcceptContentType: TAcceptContentType read GetAcceptContentType;
end;

implementation

constructor THorseOctetStreamConfig.Create;
begin
if not Assigned(FAcceptContentType) then
FAcceptContentType := TAcceptContentType.Create;
end;

destructor THorseOctetStreamConfig.Destroy;
begin
if Assigned(FAcceptContentType) then
FreeAndNil(FAcceptContentType);
inherited;
end;

function THorseOctetStreamConfig.GetAcceptContentType: TAcceptContentType;
begin
Result := FAcceptContentType;
end;

class function THorseOctetStreamConfig.GetDefaultInstance: THorseOctetStreamConfig;
begin
if not Assigned(FInstance) then
FInstance := THorseOctetStreamConfig.Create;
Result := FInstance;
end;

class function THorseOctetStreamConfig.GetInstance: THorseOctetStreamConfig;
begin
Result := THorseOctetStreamConfig.GetDefaultInstance;
end;

class destructor THorseOctetStreamConfig.OnDestroy;
begin
if Assigned(FInstance) then
FreeAndNil(FInstance);
end;

end.
90 changes: 55 additions & 35 deletions src/Horse.OctetStream.pas
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
unit Horse.OctetStream;

{$IF DEFINED(FPC)}
{$MODE DELPHI}{$H+}
{$ENDIF}

interface

uses
{$IF DEFINED(FPC)}
SysUtils, Classes,
{$ELSE}
System.SysUtils, System.Classes,
{$ENDIF}
Horse, Horse.Commons;
{$IF DEFINED(FPC)}
SysUtils,
StrUtils,
Classes,
httpdefs,
Math,
{$ELSE}
Web.HTTPApp,
System.Math,
System.SysUtils,
System.Classes,
System.StrUtils,
{$ENDIF}
Horse,
Horse.Commons,
Horse.OctetStream.Config;

type
THorseOctetStreamConfig = Horse.OctetStream.Config.THorseOctetStreamConfig;

TFileReturn = class
private
FName: string;
Expand All @@ -27,13 +43,6 @@ procedure OctetStream(Req: THorseRequest; Res: THorseResponse; Next: {$IF DEFINE

implementation

uses
{$IF DEFINED(FPC)}
httpdefs, Math;
{$ELSE}
Web.HTTPApp, System.Math;
{$ENDIF}

procedure GetAllDataAsStream(ARequest: THorseRequest; AStream: TMemoryStream);
var
{$IF DEFINED(FPC)}
Expand All @@ -52,21 +61,20 @@ procedure GetAllDataAsStream(ARequest: THorseRequest; AStream: TMemoryStream);
LStringStream.Free;
end;
{$ELSE}
{$IF CompilerVersion <= 28}
Assert(Length(ARequest.RawWebRequest.RawContent) = ARequest.RawWebRequest.ContentLength);
{$ELSE}
ARequest.RawWebRequest.ReadTotalContent;
{$ENDIF}

ContentLength := ARequest.RawWebRequest.ContentLength;
while ContentLength > 0 do
begin
BytesRead := ARequest.RawWebRequest.ReadClient(Buffer[0], Min(ContentLength, SizeOf(Buffer)));
if BytesRead < 1 then
Break;
AStream.WriteBuffer(Buffer[0], BytesRead);
Dec(ContentLength, BytesRead);
end;
{$IF CompilerVersion <= 28}
Assert(Length(ARequest.RawWebRequest.RawContent) = ARequest.RawWebRequest.ContentLength);
{$ELSE}
ARequest.RawWebRequest.ReadTotalContent;
{$ENDIF}
ContentLength := ARequest.RawWebRequest.ContentLength;
while ContentLength > 0 do
begin
BytesRead := ARequest.RawWebRequest.ReadClient(Buffer[0], Min(ContentLength, SizeOf(Buffer)));
if BytesRead < 1 then
Break;
AStream.WriteBuffer(Buffer[0], BytesRead);
Dec(ContentLength, BytesRead);
end;
{$ENDIF}
AStream.Position := 0;
end;
Expand All @@ -78,13 +86,25 @@ procedure OctetStream(Req: THorseRequest; Res: THorseResponse; Next: {$IF DEFINE
var
LContent: TObject;
LContentTMemoryStream: TMemoryStream;
LContentType: string;
begin
if (Req.MethodType in [mtPost, mtPut, mtPatch]) and (Req.RawWebRequest.ContentType = CONTENT_TYPE) then
LContentType := CONTENT_TYPE;

if THorseOctetStreamConfig.GetInstance.AcceptContentType.Count = 0 then
THorseOctetStreamConfig.GetInstance.AcceptContentType.Add(CONTENT_TYPE);

if (Req.MethodType in [mtPost, mtPut, mtPatch]) then
begin
LContent := TMemoryStream.Create;
LContentTMemoryStream := TMemoryStream(LContent);
GetAllDataAsStream(Req, LContentTMemoryStream);
Req.Body(LContent);
if (MatchText(Req.RawWebRequest.ContentType, THorseOctetStreamConfig.GetInstance.AcceptContentType.ToArray)) then
begin
LContentType := Req.RawWebRequest.ContentType;
LContent := TMemoryStream.Create;
LContentTMemoryStream := TMemoryStream(LContent);
GetAllDataAsStream(Req, LContentTMemoryStream);
Req.Body(LContent);
end
else
raise EHorseException.New.Error('Unknown Content-Type: ' + Req.RawWebRequest.ContentType).Status(THTTPStatus.BadRequest);
end;

Next;
Expand All @@ -96,7 +116,7 @@ procedure OctetStream(Req: THorseRequest; Res: THorseResponse; Next: {$IF DEFINE
TStream(LContent).Position := 0;

if Trim(Res.RawWebResponse.ContentType).IsEmpty then
Res.ContentType(CONTENT_TYPE);
Res.ContentType(LContentType);

if Res.RawWebResponse.GetCustomHeader(CONTENT_DISPOSITION).IsEmpty then
Res.RawWebResponse.SetCustomHeader(CONTENT_DISPOSITION, 'attachment');
Expand All @@ -110,7 +130,7 @@ procedure OctetStream(Req: THorseRequest; Res: THorseResponse; Next: {$IF DEFINE
TFileReturn(LContent).Stream.Position := 0;

if Trim(Res.RawWebResponse.ContentType).IsEmpty then
Res.ContentType(CONTENT_TYPE);
Res.ContentType(LContentType);

if TFileReturn(LContent).&Inline then
Res.RawWebResponse.SetCustomHeader(CONTENT_DISPOSITION, 'inline; ' + 'filename="' + TFileReturn(LContent).Name + '"')
Expand Down

0 comments on commit 7dc2020

Please sign in to comment.