mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 07:45:54 +01:00
parent
d6a04c89c2
commit
f108357a92
@ -34,7 +34,7 @@ implementation
|
||||
|
||||
|
||||
uses
|
||||
MVCFramework.RESTClient, ObjectsMappers,
|
||||
MVCFramework.RESTClient,
|
||||
MVCFramework.SystemJSONUtils,
|
||||
MVCFramework.TypesAliases;
|
||||
|
||||
@ -80,7 +80,7 @@ begin
|
||||
.Header('jwtusername', 'user1')
|
||||
.Header('jwtpassword', 'user1');
|
||||
lRest := lClient.doPOST('/login', []);
|
||||
lJSON := TSystemJSON.BodyAsJSONObject(lRest);
|
||||
lJSON := TSystemJSON.StringAsJSONObject(lRest.BodyAsString);
|
||||
try
|
||||
JWT := lJSON.GetValue('token').Value;
|
||||
finally
|
||||
|
@ -45,7 +45,8 @@ begin
|
||||
// setting up the correct SSE headers
|
||||
ContentType := 'text/event-stream';
|
||||
Context.Response.SetCustomHeader('Cache-Control', 'no-cache');
|
||||
Context.Response.SetCustomHeader('Connection', 'keep-alive');
|
||||
// WARNING!! keep-alive heaer has been set directly on the server!
|
||||
// Context.Response.SetCustomHeader('Connection', 'keep-alive');
|
||||
|
||||
// render the response using SSE compliant data format
|
||||
|
||||
@ -58,7 +59,6 @@ begin
|
||||
// before trying to reconnect.
|
||||
ResponseStream.Append('retry: 100'#13);
|
||||
|
||||
|
||||
ResponseStream.Append('event: stockupdate'#13);
|
||||
|
||||
// actual message
|
||||
|
@ -30,6 +30,7 @@ begin
|
||||
Writeln(Format('Starting HTTP Server on port %d', [APort]));
|
||||
LServer := TIdHTTPWebBrokerBridge.Create(nil);
|
||||
try
|
||||
LServer.KeepAlive := True;
|
||||
LServer.DefaultPort := APort;
|
||||
LServer.Active := True;
|
||||
LogI(Format('Server started on port 8080', [APort]));
|
||||
|
@ -6,9 +6,10 @@
|
||||
<title>Server Sent Events DelphiMVCFramework Example - Stock Tickets</title>
|
||||
|
||||
<style media="screen" type="text/css">
|
||||
body {
|
||||
font-family: "Verdana", Tahoma, serif;
|
||||
}
|
||||
body {
|
||||
font-family: "Verdana", Tahoma, serif;
|
||||
}
|
||||
|
||||
H1 {
|
||||
text-align: center;
|
||||
font-size: 150%;
|
||||
|
@ -411,6 +411,10 @@ function B64Encode(const AValue: string): string; overload;
|
||||
function B64Encode(const AValue: TBytes): string; overload;
|
||||
function B64Decode(const AValue: string): string;
|
||||
|
||||
function URLSafeB64encode(const Value: string; IncludePadding: Boolean): String; overload;
|
||||
function URLSafeB64encode(const Value: TBytes; IncludePadding: Boolean): String; overload;
|
||||
function URLSafeB64Decode(const Value: string): String;
|
||||
|
||||
function ByteToHex(AInByte: Byte): string;
|
||||
function BytesToHex(ABytes: TBytes): string;
|
||||
|
||||
@ -419,6 +423,9 @@ var
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
IdCoder3to4;
|
||||
|
||||
const
|
||||
RESERVED_IPS: array [1 .. 11] of array [1 .. 2] of string =
|
||||
(('0.0.0.0', '0.255.255.255'), ('10.0.0.0', '10.255.255.255'),
|
||||
@ -455,16 +462,19 @@ end;
|
||||
|
||||
function B64Encode(const AValue: string): string; overload;
|
||||
begin
|
||||
//Do not use TNetEncoding
|
||||
Result := TIdEncoderMIME.EncodeString(AValue);
|
||||
end;
|
||||
|
||||
function B64Encode(const AValue: TBytes): string; overload;
|
||||
begin
|
||||
//Do not use TNetEncoding
|
||||
Result := TIdEncoderMIME.EncodeBytes(TIdBytes(AValue));
|
||||
end;
|
||||
|
||||
function B64Decode(const AValue: string): string;
|
||||
begin
|
||||
//Do not use TNetEncoding
|
||||
Result := TIdDecoderMIME.DecodeString(AValue);
|
||||
end;
|
||||
|
||||
@ -712,10 +722,98 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
type
|
||||
TURLSafeEncode = class(TIdEncoder3to4)
|
||||
protected
|
||||
procedure InitComponent; override;
|
||||
public
|
||||
|
||||
end;
|
||||
TURLSafeDecode = class(TIdDecoder4to3)
|
||||
protected
|
||||
class var GSafeBaseBase64DecodeTable: TIdDecodeTable;
|
||||
procedure InitComponent; override;
|
||||
public
|
||||
|
||||
end;
|
||||
|
||||
const
|
||||
GURLSafeBase64CodeTable: string =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; {Do not Localize}
|
||||
|
||||
|
||||
procedure TURLSafeEncode.InitComponent;
|
||||
begin
|
||||
inherited;
|
||||
FCodingTable := ToBytes(GURLSafeBase64CodeTable);
|
||||
FFillChar := '='; {Do not Localize}
|
||||
end;
|
||||
|
||||
procedure TURLSafeDecode.InitComponent;
|
||||
begin
|
||||
inherited;
|
||||
FDecodeTable := GSafeBaseBase64DecodeTable;
|
||||
FCodingTable := ToBytes(GURLSafeBase64CodeTable);
|
||||
FFillChar := '='; {Do not Localize}
|
||||
end;
|
||||
|
||||
function URLSafeB64encode(const Value: string; IncludePadding: Boolean): String; overload;
|
||||
begin
|
||||
if IncludePadding then
|
||||
Result := TURLSafeEncode.EncodeString(Value)
|
||||
else
|
||||
Result := TURLSafeEncode.EncodeString(Value).Replace('=', '', [rfReplaceAll]);
|
||||
end;
|
||||
|
||||
/// <summary>
|
||||
/// Remove "trimmed" character from the end of the string passed as parameter
|
||||
/// </summary>
|
||||
/// <param name="Value">Original string</param>
|
||||
/// <param name="TrimmedChar">Character to remove</param>
|
||||
/// <returns>Resulting string</returns>
|
||||
function RTrim(const Value: string; TrimmedChar: char): string;
|
||||
var
|
||||
Strlen: Integer;
|
||||
begin
|
||||
Strlen := Length(Value);
|
||||
while (Strlen>0) and (Value[Strlen]=TrimmedChar) do
|
||||
dec(StrLen);
|
||||
result := copy(value, 1, StrLen)
|
||||
end;
|
||||
|
||||
function URLSafeB64encode(const Value: TBytes; IncludePadding: Boolean): String; overload;
|
||||
begin
|
||||
|
||||
if IncludePadding then
|
||||
Result := TURLSafeEncode.EncodeBytes(TIdBytes(Value))
|
||||
else
|
||||
Result := RTrim(TURLSafeEncode.EncodeBytes(TIdBytes(Value)), '=');
|
||||
end;
|
||||
|
||||
function URLSafeB64Decode(const Value: string): String;
|
||||
begin
|
||||
// SGR 2017-07-03 : b64url might not include padding. Need to add it before decoding
|
||||
case Length(value) mod 4 of
|
||||
0:
|
||||
begin
|
||||
Result := TURLSafeDecode.DecodeString(Value);
|
||||
end;
|
||||
2:
|
||||
Result := TURLSafeDecode.DecodeString(Value + '==');
|
||||
3:
|
||||
Result := TURLSafeDecode.DecodeString(Value + '=');
|
||||
else
|
||||
raise EExternalException.Create('Illegal base64url length');
|
||||
end;
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
Lock := TObject.Create;
|
||||
|
||||
// SGR 2017-07-03 : Initialize decoding table for URLSafe Gb64 encoding
|
||||
TURLSafeDecode.ConstructDecodeTable(GURLSafeBase64CodeTable, TURLSafeDecode.GSafeBaseBase64DecodeTable);
|
||||
|
||||
GlobalAppExe := ExtractFileName(GetModuleName(HInstance));
|
||||
GlobalAppName := ChangeFileExt(GlobalAppExe, EmptyStr);
|
||||
GlobalAppPath := IncludeTrailingPathDelimiter(ExtractFilePath(GetModuleName(HInstance)));
|
||||
|
@ -24,91 +24,114 @@
|
||||
|
||||
unit MVCFramework.HMAC;
|
||||
|
||||
{$I dmvcframework.inc}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
System.Generics.Collections,
|
||||
EncdDecd,
|
||||
IdHMAC;
|
||||
|
||||
type
|
||||
|
||||
EMVCHMACException = class(Exception)
|
||||
private
|
||||
{ private declarations }
|
||||
protected
|
||||
{ protected declarations }
|
||||
public
|
||||
{ public declarations }
|
||||
|
||||
end;
|
||||
|
||||
THMACClass = class of TIdHMAC;
|
||||
IHMAC = interface
|
||||
['{95134024-BBAF-4E52-A4C3-54189672E18A}']
|
||||
function HashValue(const Input, key: String): TBytes;
|
||||
end;
|
||||
|
||||
function HMAC(const AAlgorithm: String; const AInput, AKey: string): TBytes;
|
||||
procedure RegisterHMACAlgorithm(const AAlgorithm: String; AClazz: THMACClass);
|
||||
procedure UnRegisterHMACAlgorithm(const AAlgorithm: String);
|
||||
|
||||
function HMAC(const Algorithm: String; const Input, Key: string): TBytes;
|
||||
procedure RegisterHMACAlgorithm(const Algorithm: String; Impl: IHMAC);
|
||||
procedure UnRegisterHMACAlgorithm(const Algorithm: String);
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
IdSSLOpenSSL,
|
||||
IdHash,
|
||||
IdGlobal,
|
||||
IdHMACMD5,
|
||||
IdHMACSHA1;
|
||||
|
||||
IdSSLOpenSSL, IdHash, IdGlobal, IdHMACMD5,
|
||||
IdHMACSHA1, System.Generics.Collections;
|
||||
|
||||
var
|
||||
GHMACRegistry: TDictionary<string, THMACClass>;
|
||||
GHMACRegistry: TDictionary<string, IHMAC>;
|
||||
|
||||
function HMAC(const AAlgorithm: String; const AInput, AKey: string): TBytes;
|
||||
var
|
||||
LHMAC: TIdHMAC;
|
||||
begin
|
||||
if not GHMACRegistry.ContainsKey(AAlgorithm) then
|
||||
raise EMVCHMACException.CreateFmt('Unknown HMAC algorithm [%s]', [AAlgorithm]);
|
||||
type
|
||||
THMACClass = class of TIdHMAC;
|
||||
|
||||
TIdHMACWrapper = class(TInterfacedObject, IHMAC)
|
||||
private
|
||||
FClass: THMACClass;
|
||||
public
|
||||
constructor Create(IdClass: THMACClass);
|
||||
function HashValue(const Input: string;
|
||||
const key: string): System.TArray<System.Byte>;
|
||||
|
||||
LHMAC := GHMACRegistry[AAlgorithm].Create;
|
||||
try
|
||||
LHMAC.Key := ToBytes(AKey);
|
||||
Result := TBytes(LHMAC.HashValue(ToBytes(AInput)));
|
||||
finally
|
||||
LHMAC.Free;
|
||||
end;
|
||||
|
||||
function HMAC(const Algorithm: String; const Input, Key: string): TBytes;
|
||||
begin
|
||||
if not GHMACRegistry.ContainsKey(Algorithm) then
|
||||
raise EMVCHMACException.CreateFmt('Unknown HMAC algorithm [%s]', [Algorithm]);
|
||||
|
||||
result := GHMACRegistry[Algorithm].HashValue(Input, Key);
|
||||
end;
|
||||
|
||||
procedure RegisterHMACAlgorithm(const AAlgorithm: String; AClazz: THMACClass);
|
||||
procedure RegisterHMACAlgorithm(const Algorithm: String; Impl: IHMAC);
|
||||
begin
|
||||
if GHMACRegistry.ContainsKey(AAlgorithm) then
|
||||
if GHMACRegistry.ContainsKey(Algorithm) then
|
||||
raise EMVCHMACException.Create('Algorithm already registered');
|
||||
GHMACRegistry.Add(AAlgorithm, AClazz);
|
||||
GHMACRegistry.Add(Algorithm, Impl);
|
||||
end;
|
||||
|
||||
procedure UnRegisterHMACAlgorithm(const AAlgorithm: String);
|
||||
procedure UnRegisterHMACAlgorithm(const Algorithm: String);
|
||||
begin
|
||||
GHMACRegistry.Remove(AAlgorithm);
|
||||
GHMACRegistry.Remove(Algorithm);
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{ TIdHMACWrapper }
|
||||
|
||||
constructor TIdHMACWrapper.Create(IdClass: THMACClass);
|
||||
begin
|
||||
FClass := IdClass;
|
||||
end;
|
||||
|
||||
function TIdHMACWrapper.HashValue(const Input,
|
||||
key: string): System.TArray<System.Byte>;
|
||||
var
|
||||
lHMAC: TIdHMAC;
|
||||
begin
|
||||
Assert(IdSSLOpenSSL.LoadOpenSSLLibrary, 'HMAC requires OpenSSL libraries');
|
||||
lHMAC := FClass.Create;
|
||||
try
|
||||
lHMAC.Key := ToBytes(Key, IndyTextEncoding_UTF8);
|
||||
Result := TBytes(lHMAC.HashValue(ToBytes(Input, IndyTextEncoding_UTF8)));
|
||||
finally
|
||||
lHMAC.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
Assert(IdSSLOpenSSL.LoadOpenSSLLibrary, 'HMAC requires OpenSSL libraries');
|
||||
|
||||
GHMACRegistry := TDictionary<string, THMACClass>.Create;
|
||||
|
||||
// registering based on hash function
|
||||
RegisterHMACAlgorithm('md5', TIdHMACMD5);
|
||||
RegisterHMACAlgorithm('sha1', TIdHMACSHA1);
|
||||
RegisterHMACAlgorithm('sha224', TIdHMACSHA224);
|
||||
RegisterHMACAlgorithm('sha256', TIdHMACSHA256);
|
||||
RegisterHMACAlgorithm('sha384', TIdHMACSHA384);
|
||||
RegisterHMACAlgorithm('sha512', TIdHMACSHA512);
|
||||
GHMACRegistry := TDictionary<string, IHMAC>.Create;
|
||||
|
||||
// the same using the JWT naming
|
||||
RegisterHMACAlgorithm('HS256', TIdHMACSHA256);
|
||||
RegisterHMACAlgorithm('HS384', TIdHMACSHA384);
|
||||
RegisterHMACAlgorithm('HS512', TIdHMACSHA512);
|
||||
//registering based on hash function
|
||||
RegisterHMACAlgorithm('md5', TIdHMACWrapper.create(TIdHMACMD5));
|
||||
RegisterHMACAlgorithm('sha1', TIdHMACWrapper.create(TIdHMACSHA1));
|
||||
RegisterHMACAlgorithm('sha224', TIdHMACWrapper.create(TIdHMACSHA224));
|
||||
RegisterHMACAlgorithm('sha256', TIdHMACWrapper.create(TIdHMACSHA256));
|
||||
RegisterHMACAlgorithm('sha384', TIdHMACWrapper.create(TIdHMACSHA384));
|
||||
RegisterHMACAlgorithm('sha512', TIdHMACWrapper.create(TIdHMACSHA512));
|
||||
|
||||
|
||||
//the same using the JWT naming
|
||||
RegisterHMACAlgorithm('HS256', TIdHMACWrapper.create(TIdHMACSHA256));
|
||||
RegisterHMACAlgorithm('HS384', TIdHMACWrapper.create(TIdHMACSHA384));
|
||||
RegisterHMACAlgorithm('HS512', TIdHMACWrapper.create(TIdHMACSHA512));
|
||||
|
||||
finalization
|
||||
|
||||
|
@ -29,11 +29,8 @@ unit MVCFramework.JWT;
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
System.DateUtils,
|
||||
System.Generics.Collections,
|
||||
MVCFramework.Commons,
|
||||
MVCFramework.HMAC,
|
||||
MVCFramework,
|
||||
MVCFramework.TypesAliases,
|
||||
MVCFramework.Patches;
|
||||
|
||||
@ -181,6 +178,7 @@ type
|
||||
|
||||
TJWTCustomClaims = class(TJWTDictionaryObject)
|
||||
property Items; default;
|
||||
function AsCustomData: TMVCCustomData;
|
||||
end;
|
||||
|
||||
TJWT = class
|
||||
@ -212,6 +210,12 @@ type
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.SysUtils
|
||||
, MVCFramework.Commons
|
||||
, MVCFramework.HMAC
|
||||
, System.DateUtils;
|
||||
|
||||
{ TJWTRegisteredClaims }
|
||||
|
||||
function TJWTRegisteredClaims.GetAudience: String;
|
||||
@ -334,6 +338,13 @@ begin
|
||||
Items[Index] := IntToStr(DateTimeToUnix(Value, False));
|
||||
end;
|
||||
|
||||
{ TJWTCustomClaims }
|
||||
|
||||
function TJWTCustomClaims.AsCustomData: TMVCCustomData;
|
||||
begin
|
||||
Result := TMVCCustomData.Create(FClaims);
|
||||
end;
|
||||
|
||||
{ TJWT }
|
||||
|
||||
function TJWT.CheckExpirationTime(Payload: TJSONObject;
|
||||
@ -475,11 +486,11 @@ begin
|
||||
lPayload.AddPair(lCustomClaimName, FCustomClaims[lCustomClaimName]);
|
||||
end;
|
||||
|
||||
lHeaderEncoded := B64Encode(lHeader.ToJSON);
|
||||
lPayloadEncoded := B64Encode(lPayload.ToJSON);
|
||||
lHeaderEncoded := URLSafeB64encode(lHeader.ToString, False);
|
||||
lPayloadEncoded := URLSafeB64encode(lPayload.ToString, False);
|
||||
lToken := lHeaderEncoded + '.' + lPayloadEncoded;
|
||||
lBytes := HMAC(HMACAlgorithm, lToken, FSecretKey);
|
||||
lHash := B64Encode(lBytes);
|
||||
lHash := URLSafeB64encode(lBytes, false);
|
||||
Result := lToken + '.' + lHash;
|
||||
finally
|
||||
lPayload.Free;
|
||||
@ -505,7 +516,7 @@ begin
|
||||
Exit(False);
|
||||
end;
|
||||
|
||||
lJHeader := TJSONObject.ParseJSONValue(B64Decode(lPieces[0])) as TJSONObject;
|
||||
lJHeader := TJSONObject.ParseJSONValue(URLSafeB64Decode(lPieces[0])) as TJSONObject;
|
||||
try
|
||||
if not Assigned(lJHeader) then
|
||||
begin
|
||||
@ -513,7 +524,7 @@ begin
|
||||
Exit(False);
|
||||
end;
|
||||
|
||||
lJPayload := TJSONObject.ParseJSONValue(B64Decode(lPieces[1])) as TJSONObject;
|
||||
lJPayload := TJSONObject.ParseJSONValue(URLSafeB64Decode(lPieces[1])) as TJSONObject;
|
||||
try
|
||||
if not Assigned(lJPayload) then
|
||||
begin
|
||||
@ -529,8 +540,9 @@ begin
|
||||
|
||||
lAlgName := lJAlg.Value;
|
||||
Result := Token = lPieces[0] + '.' + lPieces[1] + '.' +
|
||||
B64Encode(
|
||||
HMAC(lAlgName, lPieces[0] + '.' + lPieces[1], FSecretKey)
|
||||
URLSafeB64encode(
|
||||
HMAC(lAlgName, lPieces[0] + '.' + lPieces[1], FSecretKey),
|
||||
False
|
||||
);
|
||||
|
||||
// if the token is correctly signed and has not been tampered,
|
||||
@ -589,9 +601,9 @@ begin
|
||||
raise EMVCJWTException.Create(lError);
|
||||
|
||||
lPieces := Token.Split(['.']);
|
||||
lJHeader := TJSONObject.ParseJSONValue(B64Decode(lPieces[0])) as TJSONObject;
|
||||
lJHeader := TJSONObject.ParseJSONValue(URLSafeB64Decode(lPieces[0])) as TJSONObject;
|
||||
try
|
||||
lJPayload := TJSONObject.ParseJSONValue(B64Decode(lPieces[1])) as TJSONObject;
|
||||
lJPayload := TJSONObject.ParseJSONValue(URLSafeB64Decode(lPieces[1])) as TJSONObject;
|
||||
try
|
||||
// loading data from token into self
|
||||
FHMACAlgorithm := lJHeader.Values['alg'].Value;
|
||||
|
@ -90,6 +90,7 @@ uses
|
||||
type
|
||||
|
||||
TSessionData = TDictionary<string, string>;
|
||||
TMVCCustomData = TSessionData;
|
||||
TMVCBaseViewEngine = class;
|
||||
TMVCViewEngineClass = class of TMVCBaseViewEngine;
|
||||
|
||||
@ -798,15 +799,9 @@ end;
|
||||
function TMVCWebRequest.Body: string;
|
||||
var
|
||||
Encoding: TEncoding;
|
||||
Buffer: TArray<Byte>;
|
||||
I: Integer;
|
||||
|
||||
{$IFNDEF BERLINORBETTER}
|
||||
|
||||
BufferOut: TArray<Byte>;
|
||||
|
||||
{$ENDIF}
|
||||
|
||||
begin
|
||||
{ TODO -oEzequiel -cRefactoring : Refactoring the method TMVCWebRequest.Body }
|
||||
if (FBody = EmptyStr) then
|
||||
@ -815,17 +810,21 @@ begin
|
||||
try
|
||||
|
||||
{$IFDEF BERLINORBETTER}
|
||||
|
||||
FWebRequest.ReadTotalContent; //Otherwise ISAPI Raises "Empty JSON BODY"
|
||||
if (FCharset = EmptyStr) then
|
||||
begin
|
||||
SetLength(Buffer, 10);
|
||||
for I := 0 to 9 do
|
||||
Buffer[I] := FWebRequest.RawContent[I];
|
||||
TEncoding.GetBufferEncoding(Buffer, Encoding, TEncoding.Default);
|
||||
SetLength(Buffer, 0);
|
||||
TEncoding.GetBufferEncoding(FWebRequest.RawContent, Encoding, TEncoding.Default);
|
||||
// SetLength(Buffer, 10);
|
||||
// for I := 0 to 9 do
|
||||
// Buffer[I] := FWebRequest.RawContent[I];
|
||||
// TEncoding.GetBufferEncoding(Buffer, Encoding, TEncoding.Default);
|
||||
// SetLength(Buffer, 0);
|
||||
end
|
||||
else
|
||||
begin
|
||||
Encoding := TEncoding.GetEncoding(FCharset);
|
||||
end;
|
||||
|
||||
FBody := Encoding.GetString(FWebRequest.RawContent);
|
||||
|
||||
{$ELSE}
|
||||
@ -834,23 +833,25 @@ begin
|
||||
FWebRequest.ReadClient(Buffer[0], FWebRequest.ContentLength);
|
||||
if (FCharset = EmptyStr) then
|
||||
begin
|
||||
SetLength(BufferOut, 10);
|
||||
for I := 0 to 9 do
|
||||
begin
|
||||
BufferOut[I] := Buffer[I];
|
||||
end;
|
||||
TEncoding.GetBufferEncoding(BufferOut, Encoding, TEncoding.Default);
|
||||
SetLength(BufferOut, 0);
|
||||
TEncoding.GetBufferEncoding(Buffer, Encoding, TEncoding.Default);
|
||||
// SetLength(BufferOut, 10);
|
||||
// for I := 0 to 9 do
|
||||
// begin
|
||||
// BufferOut[I] := Buffer[I];
|
||||
// end;
|
||||
// TEncoding.GetBufferEncoding(BufferOut, Encoding, TEncoding.Default);
|
||||
// SetLength(BufferOut, 0);
|
||||
end
|
||||
else
|
||||
begin
|
||||
Encoding := TEncoding.GetEncoding(FCharset);
|
||||
end;
|
||||
FBody := Encoding.GetString(Buffer);
|
||||
|
||||
{$ENDIF}
|
||||
|
||||
finally
|
||||
if Assigned(Encoding) then
|
||||
Encoding.Free;
|
||||
Encoding.Free;
|
||||
end;
|
||||
end;
|
||||
Result := FBody;
|
||||
|
@ -1,2 +1,2 @@
|
||||
const
|
||||
DMVCFRAMEWORK_VERSION = '3.0.0 hydrogen RC6';
|
||||
DMVCFRAMEWORK_VERSION = '3.0.0 hydrogen RC7';
|
Loading…
Reference in New Issue
Block a user