Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…ework

# Conflicts:
#	unittests/general/TestServer/WebModuleUnit.pas
  • Loading branch information
danieleteti committed May 2, 2020
2 parents ddc5f4d + 588d555 commit b642900
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 32 deletions.
3 changes: 1 addition & 2 deletions sources/MVCFramework.Commons.pas
Original file line number Diff line number Diff line change
Expand Up @@ -614,8 +614,7 @@ function CamelCase(const Value: string; const MakeFirstUpperToo: Boolean = False
MVC_HTTP_METHODS_WITH_CONTENT: TMVCHTTPMethods = [httpPOST, httpPUT, httpPATCH];

const
MVC_COMPRESSION_TYPE_AS_STRING: array [TMVCCompressionType] of string = ('none',
'deflate', 'gzip');
MVC_COMPRESSION_TYPE_AS_STRING: array [TMVCCompressionType] of string = ('none', 'deflate', 'gzip');
MVC_COMPRESSION_ZLIB_WINDOW_BITS: array [TMVCCompressionType] of Integer = (0, -15, 31);
// WindowBits: http://zlib.net/manual.html#Advanced

Expand Down
76 changes: 48 additions & 28 deletions sources/MVCFramework.Middleware.Compression.pas
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ TMVCCompressionMiddleware = class(TInterfacedObject, IMVCMiddleware)
private
fCompressionThreshold: Integer;
protected
procedure OnAfterControllerAction(Context: TWebContext; const AActionNAme: string;
const Handled: Boolean);
procedure OnBeforeRouting(Context: TWebContext; var Handled: Boolean);
procedure OnBeforeControllerAction(Context: TWebContext;
const AControllerQualifiedClassName: string; const AActionNAme: string; var Handled: Boolean);
procedure OnAfterControllerAction(AContext: TWebContext; const AActionNAme: string;
const AHandled: Boolean);
procedure OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean);
procedure OnBeforeControllerAction(AContext: TWebContext;
const AControllerQualifiedClassName: string; const AActionNAme: string; var AHandled: Boolean);
procedure OnAfterRouting(AContext: TWebContext; const AHandled: Boolean);
public
constructor Create(aCompressionThreshold: Integer = 1024); virtual;
Expand All @@ -63,23 +63,29 @@ constructor TMVCCompressionMiddleware.Create(aCompressionThreshold: Integer);
fCompressionThreshold := aCompressionThreshold;
end;

procedure TMVCCompressionMiddleware.OnAfterControllerAction(Context: TWebContext;
const AActionNAme: string; const Handled: Boolean);
procedure TMVCCompressionMiddleware.OnAfterControllerAction(AContext: TWebContext;
const AActionNAme: string; const AHandled: Boolean);
begin
// do nothing
end;

procedure TMVCCompressionMiddleware.OnAfterRouting(AContext: TWebContext; const AHandled: Boolean);
var
lMemStream: TMemoryStream;
lContentStream: TStream;
lContentStreamHelper: TStream;
lAcceptEncoding: string;
lEncodings: TArray<string>;
lItem: string;
lZStream: TZCompressionStream;
lRespCompressionType: TMVCCompressionType;
lTmpItem: string;
begin
lContentStream := Context.Response.RawWebResponse.ContentStream;
if (lContentStream = nil) or (lContentStream is TFileStream) or (lContentStream.Size <= fCompressionThreshold) then
lContentStream := AContext.Response.RawWebResponse.ContentStream;
if (lContentStream = nil) or (lContentStream.Size <= fCompressionThreshold) then
Exit;

lAcceptEncoding := Context.Request.Headers['Accept-Encoding'];
lAcceptEncoding := AContext.Request.Headers['Accept-Encoding'];
if lAcceptEncoding.IsEmpty then
begin
Exit;
Expand All @@ -106,43 +112,57 @@ procedure TMVCCompressionMiddleware.OnAfterControllerAction(Context: TWebContext
if lRespCompressionType = TMVCCompressionType.ctNone then
Exit;

lContentStream.Position := 0;
{ When it is a TFileStream copy it to a TMemoryStream, as TFileStream is read only }
if lContentStream is TFileStream then
begin
try
lContentStreamHelper := TMemoryStream.Create;
lContentStreamHelper.CopyFrom(lContentStream, 0);
AContext.Response.RawWebResponse.ContentStream := lContentStreamHelper;
except
if Assigned(lContentStreamHelper) then
FreeAndNil(lContentStreamHelper);
raise;
end;
end
else
begin
lContentStreamHelper := lContentStream;
end;

lContentStreamHelper.Position := 0;
lMemStream := TMemoryStream.Create;
try
{TODO -oDanieleT -cGeneral : Use directly lContentStream?}
lZStream := TZCompressionStream.Create(lMemStream, TZCompressionLevel.zcMax, MVC_COMPRESSION_ZLIB_WINDOW_BITS[lRespCompressionType]);
{TODO -oDanieleT -cGeneral : Use directly lContentStreamHelper?}
lZStream := TZCompressionStream.Create(lMemStream, TZCompressionLevel.zcMax,
MVC_COMPRESSION_ZLIB_WINDOW_BITS[lRespCompressionType]);
try
lZStream.CopyFrom(lContentStream, 0);
lZStream.CopyFrom(lContentStreamHelper, 0);
finally
lZStream.Free;
end;
lMemStream.Position := 0;

Context.Response.Content := '';
lContentStream.Size := 0;
lContentStream.CopyFrom(lMemStream, 0);
AContext.Response.Content := '';
lContentStreamHelper.Size := 0;
lContentStreamHelper.CopyFrom(lMemStream, 0);
{$IF Defined(SeattleOrBetter)}
Context.Response.RawWebResponse.ContentEncoding := MVC_COMPRESSION_TYPE_AS_STRING[lRespCompressionType];
AContext.Response.RawWebResponse.ContentEncoding := MVC_COMPRESSION_TYPE_AS_STRING[lRespCompressionType];
{$ELSE}
Context.Response.RawWebResponse.ContentEncoding := AnsiString(MVC_COMPRESSION_TYPE_AS_STRING[lRespCompressionType]);
AContext.Response.RawWebResponse.ContentEncoding := AnsiString(MVC_COMPRESSION_TYPE_AS_STRING[lRespCompressionType]);
{$ENDIF}
finally
lMemStream.Free;
end;
end;

procedure TMVCCompressionMiddleware.OnAfterRouting(AContext: TWebContext; const AHandled: Boolean);
begin

end;
end;

procedure TMVCCompressionMiddleware.OnBeforeControllerAction(Context: TWebContext;
const AControllerQualifiedClassName, AActionNAme: string; var Handled: Boolean);
procedure TMVCCompressionMiddleware.OnBeforeControllerAction(AContext: TWebContext;
const AControllerQualifiedClassName, AActionNAme: string; var AHandled: Boolean);
begin
// do nothing
end;

procedure TMVCCompressionMiddleware.OnBeforeRouting(Context: TWebContext; var Handled: Boolean);
procedure TMVCCompressionMiddleware.OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean);
begin
// do nothing
end;
Expand Down
10 changes: 9 additions & 1 deletion sources/MVCFramework.Middleware.Swagger.pas
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,15 @@ procedure TMVCSwaggerMiddleware.SortApiPaths(ASwagDoc: TSwagDoc);
lPathComparer := TDelegatedComparer<TSwagPath>.Create(
function(const Left, Right: TSwagPath): Integer
begin
Result := CompareText(Left.Operations[0].Tags[0], Right.Operations[0].Tags[0]);
if SameText(Left.Operations[0].Tags[0], JWT_AUTHENTICATION_TAG) or
SameText(Right.Operations[0].Tags[0], JWT_AUTHENTICATION_TAG) then
begin
Result := -1;
end
else
begin
Result := CompareText(Left.Operations[0].Tags[0], Right.Operations[0].Tags[0]);
end;
end);

ASwagDoc.Paths.Sort(lPathComparer);
Expand Down
3 changes: 2 additions & 1 deletion sources/MVCFramework.Swagger.Commons.pas
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ TMVCSwagger = class sealed
end;

const
JWT_AUTHENTICATION_TAG = 'JWT Authentication';
SECURITY_BEARER_NAME = 'bearer';
SECURITY_BASIC_NAME = 'basic';
JWT_JSON_SCHEMA = '{' + sLineBreak + ' "type": "object",' + sLineBreak + ' "properties": {' + sLineBreak +
Expand Down Expand Up @@ -606,7 +607,7 @@ class function TMVCSwagger.GetJWTAuthenticationPath(const AJWTUrlSegment: string
lSwagParam: TSwagRequestParameter;
begin
lSwagPathOp := TSwagPathOperation.Create;
lSwagPathOp.Tags.Add('JWT Authentication');
lSwagPathOp.Tags.Add(JWT_AUTHENTICATION_TAG);
lSwagPathOp.Operation := ohvPost;
lSwagPathOp.Security.Add(SECURITY_BASIC_NAME);
lSwagPathOp.Description := 'Create JSON Web Token';
Expand Down

0 comments on commit b642900

Please # to comment.