added postgresql

This commit is contained in:
Daniele Teti 2020-12-23 14:33:50 +01:00
parent 3fc8d90942
commit d2af1470e4
199 changed files with 6905 additions and 3891 deletions

View File

@ -1,2 +1,2 @@
del /s *.~*;*.exe;*.cfg;*.dof;*.gdb;*.dcu;*.local;*.identcache;*.dSYM;*.o;*.rsm;*.so;*.apk;*.log;*.tvsconfig;*.map
del /s *.~*;*.dof;*.dcu;*.local;*.identcache;*.dSYM;*.o;*.rsm;*.so;*.apk;*.log;*.tvsconfig;*.map
exit

File diff suppressed because it is too large Load Diff

View File

@ -1,671 +0,0 @@
/// wrapper of some Windows-like functions translated to Linux/BSD for FPC
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SynFPCLinux;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.
The Original Code is Synopse mORMot framework.
The Initial Developer of the Original Code is Alfred Glaenzer.
Portions created by the Initial Developer are Copyright (C) 2020
the Initial Developer. All Rights Reserved.
Contributor(s):
- Alan Chate
- Arnaud Bouchez
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. if you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. if you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****
}
interface
{$I Synopse.inc} // set proper flags, and define LINUX for BSD and ANDROID
uses
{$ifdef LINUX}
UnixType,
{$endif LINUX}
SysUtils;
const
{ HRESULT codes, delphi-like }
NOERROR = 0;
NO_ERROR = 0;
INVALID_HANDLE_VALUE = THandle(-1);
LOCALE_USER_DEFAULT = $400;
// for CompareStringW()
NORM_IGNORECASE = 1 shl ord(coIgnoreCase); // [widestringmanager.coIgnoreCase]
/// compatibility function, wrapping Win32 API mutex initialization
procedure InitializeCriticalSection(var cs : TRTLCriticalSection); inline;
/// compatibility function, wrapping Win32 API mutex finalization
procedure DeleteCriticalSection(var cs : TRTLCriticalSection); inline;
{$ifdef LINUX}
/// used by SynCommons to compute the sizes in byte
function getpagesize: Integer; cdecl; external 'c';
/// compatibility function, wrapping Win32 API high resolution timer
// - returns nanoseconds resolution, calling e.g. CLOCK_MONOTONIC on Linux/BSD
procedure QueryPerformanceCounter(out Value: Int64);
/// slightly faster than QueryPerformanceCounter() div 1000 - but not for Windows
// - returns microseconds resolution, calling e.g. CLOCK_MONOTONIC on Linux/BSD
procedure QueryPerformanceMicroSeconds(out Value: Int64); inline;
/// compatibility function, wrapping Win32 API high resolution timer
// - hardcoded to 1e9 for clock_gettime() nanoseconds resolution on Linux/BSD
function QueryPerformanceFrequency(out Value: Int64): boolean;
/// compatibility function, wrapping Win32 API file position change
function SetFilePointer(hFile: cInt; lDistanceToMove: TOff;
lpDistanceToMoveHigh: Pointer; dwMoveMethod: cint): TOff; inline;
/// compatibility function, wrapping Win32 API file size retrieval
function GetFileSize(hFile: cInt; lpFileSizeHigh: PDWORD): DWORD;
/// compatibility function, wrapping Win32 API file truncate at current position
procedure SetEndOfFile(hFile: cInt); inline;
/// compatibility function, wrapping Win32 API file flush to disk
procedure FlushFileBuffers(hFile: cInt); inline;
/// compatibility function, wrapping Win32 API last error code
function GetLastError: longint; inline;
/// compatibility function, wrapping Win32 API last error code
procedure SetLastError(error: longint); inline;
/// compatibility function, wrapping Win32 API text comparison
// - somewhat slow by using two temporary UnicodeString - but seldom called,
// unless our proprietary WIN32CASE collation is used in SynSQLite3
function CompareStringW(GetThreadLocale: DWORD; dwCmpFlags: DWORD; lpString1: Pwidechar;
cchCount1: longint; lpString2: Pwidechar; cchCount2: longint): longint;
/// returns the current UTC time
// - will convert from clock_gettime(CLOCK_REALTIME_COARSE) if available
function GetNowUTC: TDateTime;
/// returns the current UTC time, as Unix Epoch seconds
// - will call clock_gettime(CLOCK_REALTIME_COARSE) if available
function GetUnixUTC: Int64;
/// returns the current UTC time, as Unix Epoch milliseconds
// - will call clock_gettime(CLOCK_REALTIME_COARSE) if available
function GetUnixMSUTC: Int64;
/// returns the current UTC time as TSystemTime
// - will convert from clock_gettime(CLOCK_REALTIME_COARSE) if available
procedure GetNowUTCSystem(out result: TSystemTime);
var
/// will contain the current Linux kernel revision, as one 24-bit integer
// - e.g. $030d02 for 3.13.2, or $020620 for 2.6.32
KernelRevision: cardinal;
/// calls the pthread_setname_np() function, if available on this system
// - under Linux/FPC, this API truncates the name to 16 chars
procedure SetUnixThreadName(ThreadID: TThreadID; const Name: RawByteString);
{$ifdef BSD}
function fpsysctlhwint(hwid: cint): Int64;
function fpsysctlhwstr(hwid: cint; var temp: shortstring): pointer;
{$endif BSD}
{$ifndef DARWIN} // OSX has no clock_gettime() API
{$ifdef BSD}
const // see https://github.com/freebsd/freebsd/blob/master/sys/sys/time.h
CLOCK_REALTIME = 0;
CLOCK_MONOTONIC = 4;
CLOCK_REALTIME_COARSE = 10; // named CLOCK_REALTIME_FAST in FreeBSD 8.1+
CLOCK_MONOTONIC_COARSE = 12;
{$else}
const
CLOCK_REALTIME = 0;
CLOCK_MONOTONIC = 1;
CLOCK_REALTIME_COARSE = 5; // see http://lwn.net/Articles/347811
CLOCK_MONOTONIC_COARSE = 6;
{$endif BSD}
var
// contains CLOCK_REALTIME_COARSE since kernel 2.6.32
CLOCK_REALTIME_FAST: integer = CLOCK_REALTIME;
// contains CLOCK_MONOTONIC_COARSE since kernel 2.6.32
CLOCK_MONOTONIC_FAST: integer = CLOCK_MONOTONIC;
{$endif DARWIN}
{$endif LINUX}
/// compatibility function, to be implemented according to the running OS
// - expect more or less the same result as the homonymous Win32 API function
// - will call clock_gettime(CLOCK_MONOTONIC_COARSE) if available
function GetTickCount64: Int64;
/// compatibility function, to be implemented according to the running OS
// - expect more or less the same result as the homonymous Win32 API function
// - will call clock_gettime(CLOCK_MONOTONIC_COARSE) if available
function GetTickCount: cardinal;
var
/// could be set to TRUE to force SleepHiRes(0) to call the sched_yield API
// - in practice, it has been reported as buggy under POSIX systems
// - even Linus Torvald himself raged against its usage - see e.g.
// https://www.realworldtech.com/forum/?threadid=189711&curpostid=189752
// - you may tempt the devil and try it by yourself
SleepHiRes0Yield: boolean = false;
/// similar to Windows sleep() API call, to be truly cross-platform
// - using millisecond resolution
// - SleepHiRes(0) calls ThreadSwitch on windows, but this POSIX version will
// wait 10 microsecond unless SleepHiRes0Yield is forced to true (bad idea)
// - in respect to RTL's Sleep() function, it will return on ESysEINTR
procedure SleepHiRes(ms: cardinal);
/// check if any char is pending from StdInputHandle file descriptor
function UnixKeyPending: boolean;
implementation
{$ifdef LINUX}
uses
Classes,
Unix,
BaseUnix,
{$ifdef BSD}
sysctl,
{$else}
Linux,
{$endif BSD}
dl;
{$endif LINUX}
procedure InitializeCriticalSection(var cs : TRTLCriticalSection);
begin
InitCriticalSection(cs);
end;
procedure DeleteCriticalSection(var cs : TRTLCriticalSection);
begin
{$ifdef LINUXNOTBSD}
if cs.__m_kind<>0 then
{$endif LINUXNOTBSD}
DoneCriticalSection(cs);
end;
function UnixKeyPending: boolean;
var
fdsin: tfdSet;
begin
fpFD_ZERO(fdsin);
fpFD_SET(StdInputHandle,fdsin);
result := fpSelect(StdInputHandle+1,@fdsin,nil,nil,0)>0;
end;
{$ifdef LINUX}
const // Date Translation - see http://en.wikipedia.org/wiki/Julian_day
HoursPerDay = 24;
MinsPerHour = 60;
SecsPerMin = 60;
MinsPerDay = HoursPerDay*MinsPerHour;
SecsPerDay = MinsPerDay*SecsPerMin;
SecsPerHour = MinsPerHour*SecsPerMin;
C1970 = 2440588;
D0 = 1461;
D1 = 146097;
D2 = 1721119;
UnixDelta = 25569;
C_THOUSAND = Int64(1000);
C_MILLION = Int64(C_THOUSAND * C_THOUSAND);
C_BILLION = Int64(C_THOUSAND * C_THOUSAND * C_THOUSAND);
procedure JulianToGregorian(JulianDN: PtrUInt; out result: TSystemTime);
{$ifdef HASINLINE}inline;{$endif}
var YYear,XYear,Temp,TempMonth: PtrUInt;
begin
Temp := ((JulianDN-D2)*4)-1;
JulianDN := Temp div D1;
XYear := (Temp-(JulianDN*D1)) or 3;
YYear := XYear div D0;
Temp := (((XYear-(YYear*D0)+4) shr 2)*5)-3;
TempMonth := Temp div 153;
result.Day := ((Temp-(TempMonth*153))+5) div 5;
if TempMonth>=10 then begin
inc(YYear);
dec(TempMonth,12-3);
end else
inc(TempMonth,3);
result.Month := TempMonth;
result.Year := YYear+(JulianDN*100);
// initialize fake dayOfWeek - as used by SynCommons.FromGlobalTime RCU128
result.DayOfWeek := 0;
end;
procedure EpochToSystemTime(epoch: PtrUInt; out result: TSystemTime);
var t: PtrUInt;
begin
t := epoch div SecsPerDay;
JulianToGregorian(t+C1970,result);
dec(epoch,t*SecsPerDay);
t := epoch div SecsPerHour;
result.Hour := t;
dec(epoch,t*SecsPerHour);
t := epoch div SecsPerMin;
result.Minute := t;
result.Second := epoch-t*SecsPerMin;
end;
function GetTickCount: cardinal;
begin
result := cardinal(GetTickCount64);
end;
{$ifdef DARWIN}
// clock_gettime() is not implemented: http://stackoverflow.com/a/5167506
type
TTimebaseInfoData = record
Numer: cardinal;
Denom: cardinal;
end;
function mach_absolute_time: UInt64;
cdecl external 'libc.dylib' name 'mach_absolute_time';
function mach_timebase_info(var TimebaseInfoData: TTimebaseInfoData): Integer;
cdecl external 'libc.dylib' name 'mach_timebase_info';
var
mach_timeinfo: TTimebaseInfoData;
mach_timecoeff: double;
mach_timenanosecond: boolean; // very likely to be TRUE on Intel CPUs
procedure QueryPerformanceCounter(out Value: Int64);
begin // returns time in nano second resolution
Value := mach_absolute_time;
if mach_timeinfo.Denom=1 then
if mach_timeinfo.Numer=1 then
// seems to be the case on Intel CPUs
exit else
Value := Value*mach_timeinfo.Numer else
// use floating point to avoid potential overflow
Value := round(Value*mach_timecoeff);
end;
procedure QueryPerformanceMicroSeconds(out Value: Int64);
begin
if mach_timenanosecond then
Value := mach_absolute_time div C_THOUSAND else begin
QueryPerformanceCounter(Value);
Value := Value div C_THOUSAND; // ns to us
end;
end;
function GetTickCount64: Int64;
begin
if mach_timenanosecond then
result := mach_absolute_time else
QueryPerformanceCounter(result);
result := result div C_MILLION; // ns to ms
end;
function GetUnixUTC: Int64;
var tz: timeval;
begin
fpgettimeofday(@tz,nil);
result := tz.tv_sec;
end;
function GetUnixMSUTC: Int64;
var tz: timeval;
begin
fpgettimeofday(@tz,nil);
result := (tz.tv_sec*C_THOUSAND)+tz.tv_usec div C_THOUSAND; // in milliseconds
end;
procedure GetNowUTCSystem(out result: TSystemTime);
var tz: timeval;
begin
fpgettimeofday(@tz,nil);
EpochToSystemTime(tz.tv_sec,result);
result.MilliSecond := tz.tv_usec div C_THOUSAND;
end;
{$else}
{$ifdef BSD}
function clock_gettime(ID: cardinal; r: ptimespec): Integer;
cdecl external 'libc.so' name 'clock_gettime';
function clock_getres(ID: cardinal; r: ptimespec): Integer;
cdecl external 'libc.so' name 'clock_getres';
{$endif BSD}
function GetTickCount64: Int64;
var tp: timespec;
begin
clock_gettime(CLOCK_MONOTONIC_FAST,@tp); // likely = CLOCK_MONOTONIC_COARSE
Result := (Int64(tp.tv_sec) * C_THOUSAND) + (tp.tv_nsec div C_MILLION); // in ms
end;
function GetUnixMSUTC: Int64;
var r: timespec;
begin
clock_gettime(CLOCK_REALTIME_FAST,@r); // likely = CLOCK_REALTIME_COARSE
result := (Int64(r.tv_sec) * C_THOUSAND) + (r.tv_nsec div C_MILLION); // in ms
end;
function GetUnixUTC: Int64;
var r: timespec;
begin
clock_gettime(CLOCK_REALTIME_FAST,@r);
result := r.tv_sec;
end;
procedure QueryPerformanceCounter(out Value: Int64);
var r : TTimeSpec;
begin
clock_gettime(CLOCK_MONOTONIC,@r);
value := r.tv_nsec+r.tv_sec*C_BILLION; // returns nanoseconds resolution
end;
procedure QueryPerformanceMicroSeconds(out Value: Int64);
var r : TTimeSpec;
begin
clock_gettime(CLOCK_MONOTONIC,@r);
value := PtrUInt(r.tv_nsec) div C_THOUSAND+r.tv_sec*C_MILLION; // as microseconds
end;
procedure GetNowUTCSystem(out result: TSystemTime);
var r: timespec;
begin
clock_gettime(CLOCK_REALTIME_FAST,@r); // faster than fpgettimeofday()
EpochToSystemTime(r.tv_sec,result);
result.MilliSecond := r.tv_nsec div C_MILLION;
end;
{$endif DARWIN}
{$ifdef BSD}
function fpsysctlhwint(hwid: cint): Int64;
var mib: array[0..1] of cint;
len: cint;
begin
result := 0;
mib[0] := CTL_HW;
mib[1] := hwid;
len := SizeOf(result);
fpsysctl(pointer(@mib),2,@result,@len,nil,0);
end;
function fpsysctlhwstr(hwid: cint; var temp: shortstring): pointer;
var mib: array[0..1] of cint;
len: cint;
begin
mib[0] := CTL_HW;
mib[1] := hwid;
FillChar(temp,SizeOf(temp),0); // use shortstring as temp 0-terminated buffer
len := SizeOf(temp);
fpsysctl(pointer(@mib),2,@temp,@len,nil,0);
if temp[0]<>#0 then
result := @temp else
result := nil;
end;
{$endif BSD}
function GetNowUTC: TDateTime;
begin
result := GetUnixMSUTC / MSecsPerDay + UnixDelta;
end;
function QueryPerformanceFrequency(out Value: Int64): boolean;
begin
Value := C_BILLION; // 1 second = 1e9 nanoseconds
result := true;
end;
function SetFilePointer(hFile: cInt; lDistanceToMove: TOff;
lpDistanceToMoveHigh: Pointer; dwMoveMethod: cint): TOff;
var offs: Int64;
begin
Int64Rec(offs).Lo := lDistanceToMove;
if lpDistanceToMoveHigh=nil then
Int64Rec(offs).Hi := 0 else
Int64Rec(offs).Hi := PDWord(lpDistanceToMoveHigh)^;
offs := FpLseek(hFile,offs,dwMoveMethod);
result := Int64Rec(offs).Lo;
if lpDistanceToMoveHigh<>nil then
PDWord(lpDistanceToMoveHigh)^ := Int64Rec(offs).Hi;
end;
procedure SetEndOfFile(hFile: cInt);
begin
FpFtruncate(hFile,FPLseek(hFile,0,SEEK_CUR));
end;
procedure FlushFileBuffers(hFile: cInt);
begin
FpFsync(hFile);
end;
function GetLastError: longint;
begin
result := fpgeterrno;
end;
procedure SetLastError(error: longint);
begin
fpseterrno(error);
end;
function CompareStringW(GetThreadLocale: DWORD; dwCmpFlags: DWORD; lpString1: Pwidechar;
cchCount1: longint; lpString2: Pwidechar; cchCount2: longint): longint;
var U1,U2: UnicodeString; // (may be?) faster than WideString
begin // not inlined to avoid try..finally UnicodeString protection
if cchCount1<0 then
cchCount1 := StrLen(lpString1);
SetString(U1,lpString1,cchCount1);
if cchCount2<0 then
cchCount2 := StrLen(lpString2);
SetString(U2,lpString2,cchCount2);
result := widestringmanager.CompareUnicodeStringProc(U1,U2,TCompareOptions(dwCmpFlags));
end;
function GetFileSize(hFile: cInt; lpFileSizeHigh: PDWORD): DWORD;
var FileInfo: TStat;
begin
if fpFstat(hFile,FileInfo)<>0 then
FileInfo.st_Size := 0; // returns 0 on error
result := Int64Rec(FileInfo.st_Size).Lo;
if lpFileSizeHigh<>nil then
lpFileSizeHigh^ := Int64Rec(FileInfo.st_Size).Hi;
end;
procedure SleepHiRes(ms: cardinal);
var timeout: TTimespec;
begin
if ms=0 then // handle SleepHiRes(0) special case
if SleepHiRes0Yield then begin // reported as buggy by Alan on POSIX
ThreadSwitch; // call e.g. pthread's sched_yield API
exit;
end else begin
timeout.tv_sec := 0;
timeout.tv_nsec := 10000; // 10us is around timer resolution on modern HW
end else begin
timeout.tv_sec := ms div 1000;
timeout.tv_nsec := 1000000*(ms mod 1000);
end;
fpnanosleep(@timeout,nil)
// no retry loop on ESysEINTR (as with regular RTL's Sleep)
end;
procedure GetKernelRevision;
var uts: UtsName;
P: PAnsiChar;
tp: timespec;
function GetNext: cardinal;
var c: cardinal;
begin
result := 0;
repeat
c := ord(P^)-48;
if c>9 then
break else
result := result*10+c;
inc(P);
until false;
if P^ in ['.','-',' '] then
inc(P);
end;
begin
if fpuname(uts)=0 then begin
P := @uts.release[0];
KernelRevision := GetNext shl 16+GetNext shl 8+GetNext;
end else
uts.release[0] := #0;
{$ifdef DARWIN}
mach_timebase_info(mach_timeinfo);
mach_timecoeff := mach_timeinfo.Numer/mach_timeinfo.Denom;
mach_timenanosecond := (mach_timeinfo.Numer=1) and (mach_timeinfo.Denom=1);
{$else}
{$ifdef LINUX}
// try Linux kernel 2.6.32+ or FreeBSD 8.1+ fastest clocks
if clock_gettime(CLOCK_REALTIME_COARSE, @tp) = 0 then
CLOCK_REALTIME_FAST := CLOCK_REALTIME_COARSE;
if clock_gettime(CLOCK_MONOTONIC_COARSE, @tp) = 0 then
CLOCK_MONOTONIC_FAST := CLOCK_MONOTONIC_COARSE;
if (clock_gettime(CLOCK_REALTIME_FAST,@tp)<>0) or // paranoid check
(clock_gettime(CLOCK_MONOTONIC_FAST,@tp)<>0) then
raise Exception.CreateFmt('clock_gettime() not supported by %s kernel - errno=%d',
[PAnsiChar(@uts.release),GetLastError]);
{$endif LINUX}
{$endif DARWIN}
end;
type
TExternalLibraries = object
Lock: TRTLCriticalSection;
Loaded: boolean;
{$ifdef LINUX}
pthread: pointer;
{$ifdef LINUXNOTBSD} // see https://stackoverflow.com/a/7989973
pthread_setname_np: function(thread: pointer; name: PAnsiChar): LongInt; cdecl;
{$endif LINUXNOTBSD}
{$endif LINUX}
procedure EnsureLoaded;
procedure Done;
end;
var
ExternalLibraries: TExternalLibraries;
procedure TExternalLibraries.EnsureLoaded;
begin
EnterCriticalSection(Lock);
if not Loaded then begin
{$ifdef LINUX}
pthread := dlopen({$ifdef ANDROID}'libc.so'{$else}'libpthread.so.0'{$endif}, RTLD_LAZY);
if pthread <> nil then begin
{$ifdef LINUXNOTBSD}
@pthread_setname_np := dlsym(pthread, 'pthread_setname_np');
{$endif LINUXNOTBSD}
end;
{$endif LINUX}
Loaded := true;
end;
LeaveCriticalSection(Lock);
end;
procedure TExternalLibraries.Done;
begin
EnterCriticalSection(Lock);
if Loaded then begin
{$ifdef LINUX}
{$ifdef LINUXNOTBSD}
@pthread_setname_np := nil;
{$endif LINUXNOTBSD}
if pthread <> nil then
dlclose(pthread);
{$endif LINUX}
end;
LeaveCriticalSection(Lock);
DeleteCriticalSection(Lock);
end;
procedure SetUnixThreadName(ThreadID: TThreadID; const Name: RawByteString);
var trunc: array[0..15] of AnsiChar; // truncated to 16 bytes (including #0)
i,L: integer;
begin
if Name = '' then
exit;
L := 0; // trim unrelevant spaces and prefixes when filling the 16 chars
i := 1;
if Name[1] = 'T' then
if PCardinal(Name)^ = ord('T') + ord('S') shl 8 + ord('Q') shl 16 + ord('L') shl 24 then
i := 5
else
i := 2;
while i <= length(Name) do begin
if Name[i]>' ' then begin
trunc[L] := Name[i];
inc(L);
if L = high(trunc) then
break;
end;
inc(i);
end;
if L = 0 then
exit;
trunc[L] := #0;
{$ifdef LINUXNOTBSD}
ExternalLibraries.EnsureLoaded;
if Assigned(ExternalLibraries.pthread_setname_np) then
ExternalLibraries.pthread_setname_np(pointer(ThreadID), @trunc[0]);
{$endif LINUXNOTBSD}
end;
initialization
GetKernelRevision;
InitializeCriticalSection(ExternalLibraries.Lock);
finalization
ExternalLibraries.Done;
{$endif LINUX}
end.

View File

@ -1,196 +0,0 @@
/// wrapper around FPC typinfo.pp unit for SynCommons.pas and mORMot.pas
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SynFPCTypInfo;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2020 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.
The Original Code is Synopse mORMot framework.
The Initial Developer of the Original Code is Alfred Glaenzer.
Portions created by the Initial Developer are Copyright (C) 2020
the Initial Developer. All Rights Reserved.
Contributor(s):
- Arnaud Bouchez
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. if you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. if you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****
Unit created to avoid polluting the SynCommons.pas/mORMot.pas namespace
with overloaded typinfo.pp types.
}
interface
{$I Synopse.inc} // define HASINLINE CPU32 CPU64 OWNNORMTOUPPER
uses
SysUtils,
TypInfo;
{$ifdef FPC_PROVIDE_ATTR_TABLE}
type
// if you have a compilation error here, your FPC trunk is too old
// - TTypeData.AttributeTable was introduced in SVN 42356-42411 (2019/07)
// -> undefine FPC_PROVIDE_ATTR_TABLE in Synopse.inc and recompile
PFPCAttributeTable = TypInfo.PAttributeTable;
{$endif FPC_PROVIDE_ATTR_TABLE}
{$ifdef HASALIGNTYPEDATA}
function AlignTypeData(p: pointer): pointer; inline;
function AlignTypeDataClean(p: pointer): pointer; inline;
{$else}
type
AlignTypeData = pointer;
AlignTypeDataClean = pointer;
{$endif HASALIGNTYPEDATA}
{$ifdef FPC_REQUIRES_PROPER_ALIGNMENT}
function AlignToPtr(p: pointer): pointer; inline;
function AlignPTypeInfo(p: pointer): pointer; inline;
{$else FPC_REQUIRES_PROPER_ALIGNMENT}
type
AlignToPtr = pointer;
AlignPTypeInfo = pointer;
{$endif FPC_REQUIRES_PROPER_ALIGNMENT}
type
/// some type definition to avoid inclusion of TypInfo in SynCommons/mORMot.pas
PFPCInterfaceData = TypInfo.PInterfaceData;
PFPCVmtMethodParam = TypInfo.PVmtMethodParam;
PFPCIntfMethodTable = TypInfo.PIntfMethodTable;
PFPCIntfMethodEntry = TypInfo.PIntfMethodEntry;
{$ifdef FPC_NEWRTTI}
PFPCRecInitData = TypInfo.PRecInitData;
{$endif FPC_NEWRTTI}
procedure FPCDynArrayClear(var a: Pointer; TypeInfo: Pointer);
procedure FPCFinalizeArray(p: Pointer; TypeInfo: Pointer; elemCount: PtrUInt);
procedure FPCFinalize(Data: Pointer; TypeInfo: Pointer);
procedure FPCRecordCopy(const Source; var Dest; TypeInfo: pointer);
procedure FPCRecordAddRef(var Data; TypeInfo : pointer);
implementation
procedure FPCDynArrayClear(var a: Pointer; TypeInfo: Pointer);
external name 'FPC_DYNARRAY_CLEAR';
procedure FPCFinalizeArray(p: Pointer; TypeInfo: Pointer; elemCount: PtrUInt);
external name 'FPC_FINALIZE_ARRAY';
procedure FPCFinalize(Data: Pointer; TypeInfo: Pointer);
external name 'FPC_FINALIZE';
procedure FPCRecordCopy(const Source; var Dest; TypeInfo: pointer);
external name 'FPC_COPY';
procedure FPCRecordAddRef(var Data; TypeInfo : pointer);
external name 'FPC_ADDREF';
{$ifdef FPC_REQUIRES_PROPER_ALIGNMENT} // copied from latest typinfo.pp
function AlignToPtr(p: pointer): pointer;
begin
result := align(p,sizeof(p));
end;
function AlignTypeData(p: pointer): pointer;
{$packrecords c}
type
TAlignCheck = record // match RTTI TTypeInfo definition
b : byte; // = TTypeKind
q : qword; // = this is where the PTypeData begins
end;
{$packrecords default}
begin
{$ifdef VER3_0}
result := Pointer(align(p,SizeOf(Pointer)));
{$else VER3_0}
result := Pointer(align(p,PtrInt(@TAlignCheck(nil^).q)));
{$endif VER3_0}
{$ifdef FPC_PROVIDE_ATTR_TABLE}
inc(PByte(result),SizeOf(PFPCAttributeTable)); // ignore attributes table
result := Pointer(align(result,PtrInt(@TAlignCheck(nil^).q)));
{$endif FPC_PROVIDE_ATTR_TABLE}
end;
{$else}
{$ifdef FPC_PROVIDE_ATTR_TABLE}
function AlignTypeData(p: pointer): pointer;
begin
result := p;
inc(PByte(result),SizeOf(PFPCAttributeTable)); // ignore attributes table
end;
{$endif FPC_PROVIDE_ATTR_TABLE}
{$endif FPC_REQUIRES_PROPER_ALIGNMENT}
{$ifdef FPC_REQUIRES_PROPER_ALIGNMENT} // copied from latest typinfo.pp
function AlignTypeDataClean(p: pointer): pointer;
{$packrecords c}
type
TAlignCheck = record // match RTTI TTypeInfo definition
b : byte; // = TTypeKind
q : qword; // = this is where the PTypeData begins
end;
{$packrecords default}
begin
{$ifdef VER3_0}
result := Pointer(align(p,SizeOf(Pointer)));
{$else VER3_0}
result := Pointer(align(p,PtrInt(@TAlignCheck(nil^).q)));
{$endif VER3_0}
end;
function AlignPTypeInfo(p: pointer): pointer; inline;
{$packrecords c}
type
TAlignCheck = record
b : byte;
p : pointer;
end;
{$packrecords default}
begin
Result := Pointer(align(p,PtrInt(@TAlignCheck(nil^).p)))
end;
{$else}
{$ifdef HASALIGNTYPEDATA}
function AlignTypeDataClean(p: pointer): pointer;
begin
result := p;
end;
{$endif HASALIGNTYPEDATA}
{$endif FPC_REQUIRES_PROPER_ALIGNMENT}
end.

View File

@ -175,6 +175,7 @@ end;
type // some cross-platform and cross-compiler definitions
{$ifndef FPC}
PtrInt = {$ifdef CPU64}NativeInt{$else}integer{$endif};
PtrUInt = {$ifdef CPU64}NativeUInt{$else}cardinal{$endif};
{$endif}
{$ifdef DELPHI5OROLDER} // Delphi 5 doesn't have those base types defined :(
@ -198,6 +199,7 @@ end;
// using direct x86 jmp also circumvents Internal Error C11715 for Delphi 5
{$ifdef CPUX86}
function SynLZcompress1(src: PAnsiChar; size: integer; dst: PAnsiChar): integer;
{$ifdef FPC} nostackframe; assembler; {$endif}
asm
push ebp
push ebx
@ -626,13 +628,16 @@ end;
procedure movechars(s,d: PAnsiChar; t: PtrUInt); {$ifdef HASINLINE}inline;{$endif}
// fast code for unaligned and overlapping (see {$define WT}) small blocks
// this code is sometimes used rather than system.move()
var c: AnsiChar; // better code generation on FPC
begin
dec(PtrUInt(s), PtrUInt(d));
inc(t, PtrUInt(d));
inc(PtrUInt(s), t);
inc(PtrUInt(d), t);
PtrInt(t) := -PtrInt(t);
repeat
d^ := s[PtrUInt(d)];
inc(d);
until PtrUInt(d)=t;
c := s[t];
d[t] := c;
inc(t);
until t=0;
end;
const
@ -733,6 +738,7 @@ end;
{$ifdef CPUX86}
// using direct x86 jmp also circumvents Internal Error C11715 for Delphi 5
function SynLZdecompress1(src: PAnsiChar; size: integer; dst: PAnsiChar): integer;
{$ifdef FPC} nostackframe; assembler; {$endif}
asm
push ebp
push ebx
@ -965,12 +971,12 @@ asm // rcx=src, edx=size, r8=dest
jc @29
shr r12, 3
jz @30
@27: mov rbx, qword ptr [r14+rsi]
@27: mov rbx, qword ptr [r14+rsi] // inline move by 8 bytes
mov qword ptr [r8+rsi], rbx
add rsi, 8
dec r12
jnz @27
mov rbx, qword ptr [r14+rsi]
mov rbx, qword ptr [r14+rsi] // 1..7 remaining bytes
and r15, 7
jz @31
@28: mov byte ptr [r8+rsi], bl
@ -979,7 +985,7 @@ asm // rcx=src, edx=size, r8=dest
dec r15
jnz @28
jmp @31
@29: mov bl, byte ptr [r14+rsi]
@29: mov bl, byte ptr [r14+rsi] // overlaping move
mov byte ptr [r8+rsi], bl
inc rsi
dec r12

View File

@ -419,7 +419,7 @@ type
Partials: TSynMustachePartials=nil; Helpers: TSynMustacheHelpers=nil;
OnTranslate: TOnStringTranslate=nil;
EscapeInvert: boolean=false): RawUTF8; overload;
/// search some text withing the {{mustache}} template text
/// search some text within the {{mustache}} template text
function FoundInTemplate(const text: RawUTF8): boolean;
/// read-only access to the raw {{mustache}} template content

View File

@ -96,7 +96,8 @@ type
// are handled with dedicated code, optionally with case-insensitive search
// - consider using TMatchs (or SetMatchs/TMatchDynArray) if you expect to
// search for several patterns, or even TExprParserMatch for expression search
{$ifdef UNICODE}TMatch = record{$else}TMatch = object{$endif}
{$ifdef USERECORDWITHMETHODS}TMatch = record
{$else}TMatch = object{$endif}
private
Pattern, Text: PUTF8Char;
P, T, PMax, TMax: PtrInt;
@ -697,6 +698,20 @@ type
property UTF8Length: boolean read fUTF8Length write fUTF8Length;
end;
resourcestring
sInvalidIPAddress = '"%s" is an invalid IP v4 address';
sInvalidEmailAddress = '"%s" is an invalid email address';
sInvalidPattern = '"%s" does not match the expected pattern';
sCharacter01n = 'character,character,characters';
sInvalidTextLengthMin = 'Expect at least %d %s';
sInvalidTextLengthMax = 'Expect up to %d %s';
sInvalidTextChar = 'Expect at least %d %s %s,Expect up to %d %s %s,'+
'alphabetical,digital,punctuation,lowercase,uppercase,space,'+
'Too much spaces on the left,Too much spaces on the right';
sValidationFailed = '"%s" rule failed';
sValidationFieldVoid = 'An unique key field must not be void';
sValidationFieldDuplicate = 'Value already used for this unique key field';
{ ************ Database types and classes ************************** }
@ -724,6 +739,7 @@ type
/// array of field/parameter/column types for abstract database access
// - this array as a fixed size, ready to handle up to MAX_SQLFIELDS items
TSQLDBFieldTypeArray = array[0..MAX_SQLFIELDS-1] of TSQLDBFieldType;
PSQLDBFieldTypeArray = ^TSQLDBFieldTypeArray;
/// how TSQLVar may be processed
// - by default, ftDate will use seconds resolution unless svoDateWithMS is set
@ -822,7 +838,8 @@ type
// - if no Stream is supplied, a temporary memory stream will be created
// (it's faster to supply one, e.g. any TSQLRest.TempMemoryStream)
constructor Create(aStream: TStream; Expand, withID: boolean;
const Fields: TSQLFieldIndexDynArray=nil; aBufSize: integer=8192); overload;
const Fields: TSQLFieldIndexDynArray=nil; aBufSize: integer=8192;
aStackBuffer: PTextWriterStackBuffer=nil); overload;
/// rewind the Stream position and write void JSON object
procedure CancelAllVoid;
/// write or init field names for appropriate JSON Expand later use
@ -915,7 +932,7 @@ procedure VariantToSQLVar(const Input: variant; var temp: RawByteString;
var Output: TSQLVar);
/// guess the correct TSQLDBFieldType from a variant type
function VariantVTypeToSQLDBFieldType(VType: word): TSQLDBFieldType;
function VariantVTypeToSQLDBFieldType(VType: cardinal): TSQLDBFieldType;
/// guess the correct TSQLDBFieldType from a variant value
function VariantTypeToSQLDBFieldType(const V: Variant): TSQLDBFieldType;
@ -1277,7 +1294,8 @@ type
// - is also safer, since will check for reaching end of buffer
// - raise a EFastReader exception on decoding error (e.g. if a buffer
// overflow may occur) or call OnErrorOverflow/OnErrorData event handlers
{$ifdef FPC_OR_UNICODE}TFastReader = record{$else}TFastReader = object{$endif}
{$ifdef USERECORDWITHMETHODS}TFastReader = record
{$else}TFastReader = object{$endif}
public
/// the current position in the memory
P: PAnsiChar;
@ -1966,9 +1984,6 @@ type
// - this class is thread-safe if you use properly the associated Safe lock
TSynCache = class(TSynPersistentLock)
protected
/// last index in fNameValue.List[] if was added by Find()
// - contains -1 if no previous immediate call to Find()
fFindLastAddedIndex: integer;
fFindLastKey: RawUTF8;
fNameValue: TSynNameValue;
fRamUsed: cardinal;
@ -2166,7 +2181,7 @@ type
fReaderTemp: PRawByteString;
fLoadFromLastUncompressed, fSaveToLastUncompressed: integer;
fLoadFromLastAlgo: TAlgoCompress;
/// low-level virtual methods implementing the persistence reading
/// low-level virtual methods implementing the persistence
procedure LoadFromReader; virtual;
procedure SaveToWriter(aWriter: TFileBufferWriter); virtual;
public
@ -2249,7 +2264,8 @@ type
TRawByteStringGroupValueDynArray = array of TRawByteStringGroupValue;
/// store several RawByteString content with optional concatenation
{$ifdef UNICODE}TRawByteStringGroup = record{$else}TRawByteStringGroup = object{$endif}
{$ifdef USERECORDWITHMETHODS}TRawByteStringGroup = record
{$else}TRawByteStringGroup = object{$endif}
public
/// actual list storing the data
Values: TRawByteStringGroupValueDynArray;
@ -2333,8 +2349,8 @@ type
/// simple stack-allocated type for handling a non-void type names list
// - Delphi "object" is buggy on stack -> also defined as record with methods
{$ifdef FPC_OR_UNICODE}TPropNameList = record
{$else}TPropNameList = object{$endif}
{$ifdef USERECORDWITHMETHODS}TPropNameList = record
{$else}TPropNameList = object{$endif}
public
/// the actual names storage
Values: TRawUTF8DynArray;
@ -2375,7 +2391,8 @@ type
// - bits 0..14 map a 15-bit increasing counter (collision-free)
// - bits 15..30 map a 16-bit process identifier
// - bits 31..63 map a 33-bit UTC time, encoded as seconds since Unix epoch
{$ifdef FPC_OR_UNICODE}TSynUniqueIdentifierBits = record{$else}TSynUniqueIdentifierBits = object{$endif}
{$ifdef USERECORDWITHMETHODS}TSynUniqueIdentifierBits = record
{$else}TSynUniqueIdentifierBits = object{$endif}
public
/// the actual 64-bit storage value
// - in practice, only first 63 bits are used
@ -2655,6 +2672,8 @@ type
function ComputeCredential(previous: boolean; const UserName,PassWord: RawUTF8): cardinal; virtual;
function GetPassword(const UserName: RawUTF8; out Password: RawUTF8): boolean; virtual; abstract;
function GetUsersCount: integer; virtual; abstract;
// check the given Hash challenge, against stored credentials
function CheckCredentials(const UserName: RaWUTF8; Hash: cardinal): boolean; virtual;
public
/// initialize the authentication scheme
constructor Create;
@ -2916,9 +2935,6 @@ type
function NextPendingTask: RawByteString; virtual;
/// flush all pending tasks
procedure Clear; virtual;
/// access to the locking methods of this instance
// - use Safe.Lock/TryLock with a try ... finally Safe.Unlock block
property Safe: PSynlocker read fSafe;
/// access to the internal TPendingTaskListItem.Timestamp stored value
// - corresponding to the current time
// - default implementation is to return GetTickCount64, with a 16 ms
@ -3439,7 +3455,7 @@ type
TBlockingProcessPool = class(TSynPersistent)
protected
fClass: TBlockingProcessPoolItemClass;
fPool: TObjectListLocked;
fPool: TSynObjectListLocked;
fCallCounter: TBlockingProcessPoolCall; // set TBlockingProcessPoolItem.Call
public
/// initialize the pool, for a given implementation class
@ -3497,8 +3513,9 @@ type
TSystemUseDataDynArray = array of TSystemUseData;
/// low-level structure used to compute process memory and CPU usage
{$ifdef FPC_OR_UNICODE}TProcessInfo = record private
{$else}TProcessInfo = object protected{$endif}
{$ifdef USERECORDWITHMETHODS}TProcessInfo = record
{$else}TProcessInfo = object {$endif}
private
{$ifdef MSWINDOWS}
fSysPrevIdle, fSysPrevKernel, fSysPrevUser,
fDiffIdle, fDiffKernel, fDiffUser, fDiffTotal: Int64;
@ -3794,8 +3811,8 @@ type
/// used to store Time Zone information for a single area in TSynTimeZone
// - Delphi "object" is buggy on stack -> also defined as record with methods
{$ifdef FPC_OR_UNICODE}TTimeZoneData = record
{$else}TTimeZoneData = object{$endif}
{$ifdef USERECORDWITHMETHODS}TTimeZoneData = record
{$else}TTimeZoneData = object{$endif}
public
id: TTimeZoneID;
display: RawUTF8;
@ -3938,7 +3955,7 @@ function GetDiskInfo(var aDriveFolderOrFile: TFileName;
{$ifdef MSWINDOWS}; aVolumeName: PFileName = nil{$endif}): boolean;
{ ************ Markup (e.g. Emoji) process ************************** }
{ ************ Markup (e.g. HTML or Emoji) process ******************** }
type
/// tune AddHtmlEscapeWiki/AddHtmlEscapeMarkdown wrapper functions process
@ -4565,9 +4582,9 @@ type
// - you should call OrderedIndexRefresh method to ensure it is sorted
OrderedIndexNotSorted: boolean;
/// all TSynValidate instances registered per each field
Filters: TObjectList;
Filters: TSynObjectList;
/// all TSynValidate instances registered per each field
Validates: TObjectList;
Validates: TSynObjectList;
/// low-level binary comparison used by IDSort and TSynTable.IterateJSONValues
// - P1 and P2 must point to the values encoded in our SBF compact binary format
{$ifdef SORTCOMPAREMETHOD}
@ -4723,18 +4740,18 @@ type
// - is defined either as an object either as a record, due to a bug
// in Delphi 2009/2010 compiler (at least): this structure is not initialized
// if defined as an object on the stack, but will be as a record :(
{$ifdef UNICODE}TSynTableData = record{$else}TSynTableData = object{$endif UNICODE}
{$ifdef UNICODE}private{$else}protected{$endif UNICODE}
VType: TVarType;
Filler: array[1..SizeOf(TVarData)-SizeOf(TVarType)-SizeOf(pointer)*2-4] of byte;
{$ifdef USERECORDWITHMETHODS}TSynTableData = record
{$else}TSynTableData = object {$endif UNICODE}
private
VType: cardinal; // defined as cardinal not as word for proper aligment
VID: integer;
VTable: TSynTable;
VValue: TSBFString;
{$ifndef NOVARIANTS}
function GetFieldValue(const FieldName: RawUTF8): Variant; overload;
function GetFieldVarData(FieldName: PUTF8Char; FieldNameLen: PtrInt; var Value: TVarData): boolean;
procedure GetFieldVariant(const FieldName: RawUTF8; var result: Variant);
procedure SetFieldValue(const FieldName: RawUTF8; const Value: Variant); overload;
function GetField(const FieldName: RawUTF8): Variant;
procedure SetField(const FieldName: RawUTF8; const Value: Variant);
{$endif}
/// raise an exception if VTable=nil
procedure CheckVTableInitialized;
@ -4755,13 +4772,13 @@ type
property SBF: TSBFString read VValue;
{$ifndef NOVARIANTS}
/// set or retrieve a field value from a variant data
property Field[const FieldName: RawUTF8]: Variant read GetFieldValue write SetFieldValue;
property Field[const FieldName: RawUTF8]: Variant read GetField write SetField;
/// get a field value for a specified field
// - this method is faster than Field[], because it won't look for the field name
function GetFieldValue(aField: TSynTableFieldProperties): Variant; overload;
function GetFieldValue(aField: TSynTableFieldProperties): Variant;
/// set a field value for a specified field
// - this method is faster than Field[], because it won't look for the field name
procedure SetFieldValue(aField: TSynTableFieldProperties; const Value: Variant); overload;
procedure SetFieldValue(aField: TSynTableFieldProperties; const Value: Variant);
{$ifdef HASINLINE}inline;{$endif}
{$endif}
/// set a field value for a specified field, from SBF-encoded data
@ -5056,6 +5073,10 @@ type
procedure Copy(var Dest: TVarData; const Source: TVarData;
const Indirect: Boolean); override;
end;
/// initialize TSynTableVariantType if needed, and return the correspongind VType
function SynTableVariantVarType: cardinal;
{$endif NOVARIANTS}
const
@ -5109,6 +5130,11 @@ procedure ToSBFStr(const Value: RawByteString; out Result: TSBFString);
implementation
{$ifdef WITH_FASTMM4STATS}
uses
FastMM4; // override OS information by actual FastMM4 status
{$endif WITH_FASTMM4STATS}
{$ifdef FPCLINUX}
uses
termio,
@ -5131,6 +5157,13 @@ uses
var
SynTableVariantType: TCustomVariantType = nil;
function SynTableVariantVarType: cardinal;
begin
if SynTableVariantType=nil then
SynTableVariantType := SynRegisterCustomVariantType(TSynTableVariantType);
result := SynTableVariantType.VarType;
end;
procedure TSynTableVariantType.Clear(var V: TVarData);
begin
//Assert(V.VType=SynTableVariantType.VarType);
@ -5161,7 +5194,7 @@ function TSynTableVariantType.IntSet(const Instance, Value: TVarData;
var aName: RawUTF8;
begin
FastSetString(aName,Name,NameLen);
TSynTableData(Instance).SetFieldValue(aName,Variant(Value));
TSynTableData(Instance).SetField(aName,Variant(Value));
result := true;
end;
@ -5195,7 +5228,7 @@ end;
{ TSynTable }
{$ifdef CPUX86}
function SortQWord(const A,B: QWord): integer;
function SortQWord(const A,B: QWord): integer; {$ifdef FPC} nostackframe; assembler; {$endif}
asm // Delphi x86 compiler is not efficient, and oldest even incorrect
mov ecx, [eax]
mov eax, [eax + 4]
@ -5211,7 +5244,7 @@ asm // Delphi x86 compiler is not efficient, and oldest even incorrect
@p: mov eax, 1
end;
function SortInt64(const A,B: Int64): integer;
function SortInt64(const A,B: Int64): integer; {$ifdef FPC} nostackframe; assembler; {$endif}
asm // Delphi x86 compiler is not efficient at compiling below code
mov ecx, [eax]
mov eax, [eax + 4]
@ -5964,11 +5997,8 @@ end;
function TSynTable.Data(aID: integer; RecordBuffer: pointer; RecordBufferLen: Integer): Variant;
var data: TSynTableData absolute result;
begin
if SynTableVariantType=nil then
SynTableVariantType := SynRegisterCustomVariantType(TSynTableVariantType);
{$ifndef FPC}if data.VType and VTYPE_STATIC<>0 then{$endif}
VarClear(result);
data.VType := SynTableVariantType.VarType;
VarClear(result);
data.VType := SynTableVariantVarType;
data.VID := aID;
data.VTable := self;
pointer(data.VValue) := nil; // avoid GPF
@ -6198,8 +6228,8 @@ var len: integer;
PA: PAnsiChar absolute FieldBuffer;
PU: PUTF8Char absolute FieldBuffer;
tmp: RawByteString;
{$ifndef UNICODE}
WS: WideString;
{$ifndef HASVARUSTRING}
WS: SynUnicode;
{$endif}
begin
case FieldType of
@ -6234,7 +6264,7 @@ begin
tftWinAnsi: begin
len := FromVarUInt32(PB);
if len>0 then
{$ifdef UNICODE}
{$ifdef HASVARUSTRING}
result := WinAnsiToUnicodeString(PA,len)
{$else}
result := CurrentAnsiConvert.AnsiToAnsi(WinAnsiConvert,PA,len)
@ -6244,7 +6274,7 @@ begin
tftUTF8: begin
len := FromVarUInt32(PB);
if len>0 then
{$ifdef UNICODE}
{$ifdef HASVARUSTRING}
result := UTF8DecodeToUnicodeString(PU,len)
{$else} begin
UTF8ToSynUnicode(PU,len,WS);
@ -6292,7 +6322,7 @@ begin
tftCurrency:
Curr64ToStr(PInt64(FieldBuffer)^,result);
tftDouble:
ExtendedToStr(unaligned(PDouble(FieldBuffer)^),DOUBLE_PRECISION,result);
DoubleToStr(unaligned(PDouble(FieldBuffer)^),result);
// some variable-size field value
tftVarUInt32:
UInt32ToUtf8(FromVarUInt32(PB),result);
@ -6850,12 +6880,12 @@ begin
end;
function TSynTableFieldProperties.AddFilterOrValidate(aFilter: TSynFilterOrValidate): TSynFilterOrValidate;
procedure Add(var List: TObjectList);
begin
if List=nil then
List := TObjectList.Create;
List.Add(result);
end;
procedure Add(var List: TSynObjectList);
begin
if List=nil then
List := TSynObjectList.Create;
List.Add(result);
end;
begin
result := aFilter;
if (self=nil) or (result=nil) then
@ -7170,7 +7200,7 @@ begin
soContains: begin
dec(L,ValueLen);
while L>=0 do begin
while (L>=0) and not(byte(SBF^) in IsWord) do begin
while (L>=0) and not(tcWord in TEXT_CHARS[SBF^]) do begin
dec(L);
inc(SBF);
end; // begin of next word reached
@ -7182,7 +7212,7 @@ begin
end else
if StrCompIL(SBF,Value,ValueLen,0)=0 then
exit;
while (L>=0) and (byte(SBF^) in IsWord) do begin
while (L>=0) and (tcWord in TEXT_CHARS[SBF^]) do begin
dec(L);
inc(SBF);
end; // end of word reached
@ -7310,7 +7340,7 @@ begin
B := P;
repeat
inc(P);
until not(ord(P^) in IsJsonIdentifier);
until not (jcJsonIdentifier in JSON_CHARS[P^]);
FastSetString(select.SubField,B,P-B);
fHasSelectSubFields := true;
end;
@ -7440,7 +7470,7 @@ begin
B := P;
repeat
inc(P);
until not(ord(P^) in IsJsonIdentifier);
until not (jcJsonIdentifier in JSON_CHARS[P^]);
FastSetString(Where.SubField,B,P-B);
fWhereHasSubFields := true;
P := GotoNextNotSpace(P);
@ -7719,7 +7749,7 @@ end;
{$ifndef NOVARIANTS}
function TSynTableData.GetFieldValue(const FieldName: RawUTF8): Variant;
function TSynTableData.GetField(const FieldName: RawUTF8): Variant;
begin
GetFieldVariant(FieldName,result);
end;
@ -7779,15 +7809,16 @@ end;
procedure TSynTableData.Init(aTable: TSynTable; aID: Integer);
begin
VTable := aTable;
VType := SynTableVariantVarType;
VID := aID;
VTable := aTable;
VValue := VTable.DefaultRecordData;
{$ifdef UNICODE}FillcharFast(Filler,SizeOf(Filler),0);{$endif}
end;
procedure TSynTableData.Init(aTable: TSynTable; aID: Integer;
RecordBuffer: pointer; RecordBufferLen: integer);
begin
VType := SynTableVariantVarType;
VTable := aTable;
if (RecordBufferLen=0) or (RecordBuffer=nil) then begin
VID := 0;
@ -7799,7 +7830,7 @@ begin
end;
{$ifndef NOVARIANTS}
procedure TSynTableData.SetFieldValue(const FieldName: RawUTF8;
procedure TSynTableData.SetField(const FieldName: RawUTF8;
const Value: Variant);
var F: TSynTableFieldProperties;
begin
@ -8862,7 +8893,7 @@ begin
{$else}
v := NormToUpperAnsi7Byte[ord(p^)]; // 7 bit char uppercase
{$endif}
if not (v in IsWord) then break;
if not (tcWord in TEXT_BYTES[v]) then break;
inc(p);
dec(v,ord('B'));
if v>high(TSoundExValues) then continue;
@ -8905,7 +8936,7 @@ begin
if Values<>nil then
repeat
v := GetNextUTF8Upper(U);
if not (v in IsWord) then break;
if not (tcWord in TEXT_BYTES[v]) then break;
dec(v,ord('B'));
if v>high(TSoundExValues) then continue;
v := Values[v]; // get soundex value
@ -8983,15 +9014,15 @@ begin
repeat
if A^=#0 then exit else
{$ifdef USENORMTOUPPER}
if not(NormToUpperByte[ord(A^)] in IsWord) then break else inc(A);
{$else} if not(ord(A^) in IsWord) then break else inc(A); {$endif}
if not(tcWord in TEXT_CHARS[NormToUpper[A^]]) then break else inc(A);
{$else} if not(tcWord in TEXT_CHARS[A^]) then break else inc(A); {$endif}
until false;
// find beginning of next word
repeat
if A^=#0 then exit else
{$ifdef USENORMTOUPPER}
if NormToUpperByte[ord(A^)] in IsWord then break else inc(A);
{$else} if ord(A^) in IsWord then break else inc(A); {$endif}
if tcWord in TEXT_CHARS[NormToUpper[A^]] then break else inc(A);
{$else} if tcWord in TEXT_CHARS[A^] then break else inc(A); {$endif}
until false;
until false;
end;
@ -9019,7 +9050,7 @@ begin
c := GetNextUTF8Upper(U);
if c=0 then
exit;
until not(c in IsWord);
until not(tcWord in TEXT_BYTES[c]);
// find beginning of next word
repeat
if U=nil then exit;
@ -9027,7 +9058,7 @@ begin
c := GetNextUTF8Upper(U);
if c=0 then
exit;
until c in IsWord;
until tcWord in TEXT_BYTES[c];
U := V;
until U=nil;
end;
@ -9058,9 +9089,9 @@ begin
end;
if next<>nil then begin
{$ifdef USENORMTOUPPER}
while NormToUpperByte[ord(A^)] in IsWord do inc(A); // go to end of word
while tcWord in TEXT_CHARS[NormToUpper[A^]] do inc(A); // go to end of word
{$else}
while ord(A^) in IsWord do inc(A); // go to end of word
while tcWord in TEXT_CHARS[A^] do inc(A); // go to end of word
{$endif}
next^ := A;
end;
@ -9364,7 +9395,7 @@ const TopLevelTLD: array[0..19] of PUTF8Char = (
begin
if IsValidEmail(pointer(value)) then
repeat
DOM := lowercase(copy(value,PosEx('@',value)+1,100));
DOM := lowercase(copy(value,PosExChar('@',value)+1,100));
if length(DOM)>63 then
break; // exceeded 63-character limit of a DNS name
if (ForbiddenDomains<>'') and (FindCSVIndex(pointer(ForbiddenDomains),DOM)>=0) then
@ -10125,7 +10156,7 @@ end;
{$ifdef CPUINTEL} // crc32c SSE4.2 hardware accellerated dword hash
function crc32csse42(buf: pointer): cardinal;
{$ifdef CPUX86}
{$ifdef CPUX86} {$ifdef FPC} nostackframe; assembler; {$endif}
asm
mov edx, eax
xor eax, eax
@ -12329,7 +12360,7 @@ begin
if result=0 then
exit;
count := result;
if count>length(Values) then // only set length is not big enough
if count>length(Values) then // change Values[] length only if not big enough
SetLength(Values,count);
PI := pointer(Values);
fixedsize := ReadVarUInt32;
@ -12394,9 +12425,7 @@ constructor TSynCache.Create(aMaxCacheRamUsed: cardinal; aCaseSensitive: boolean
begin
inherited Create;
fNameValue.Init(aCaseSensitive);
fNameValue.DynArray.Capacity := 200; // some space for future cached entries
fMaxRamUsed := aMaxCacheRamUsed;
fFindLastAddedIndex := -1;
fTimeoutSeconds := aTimeoutSeconds;
end;
@ -12415,41 +12444,30 @@ end;
procedure TSynCache.Add(const aValue: RawUTF8; aTag: PtrInt);
begin
if (self=nil) or (fFindLastAddedIndex<0) or (fFindLastKey='') then
// fFindLastAddedIndex should have been set by a previous call to Find()
if (self=nil) or (fFindLastKey='') then
exit;
ResetIfNeeded;
inc(fRamUsed,length(aValue));
if fFindLastAddedIndex<0 then // Reset occurred in ResetIfNeeded
fNameValue.Add(fFindLastKey,aValue,aTag) else
with fNameValue.List[fFindLastAddedIndex] do begin // at Find() position
Name := fFindLastKey;
Value := aValue;
Tag := aTag;
fFindLastAddedIndex := -1;
fFindLastKey := '';
end;
fNameValue.Add(fFindLastKey,aValue,aTag);
fFindLastKey := '';
end;
function TSynCache.Find(const aKey: RawUTF8; aResultTag: PPtrInt): RawUTF8;
var added: boolean;
var ndx: integer;
begin
result := '';
if self=nil then
exit;
fFindLastKey := aKey;
if aKey='' then
fFindLastAddedIndex := -1 else begin
fFindLastAddedIndex := fNameValue.DynArray.FindHashedForAdding(aKey,added);
if added then
// expect a further call to Add()
fFindLastKey := aKey else
// match key found
with fNameValue.List[fFindLastAddedIndex] do begin
result := Value;
if aResultTag<>nil then
aResultTag^ := Tag;
fFindLastAddedIndex := -1;
end;
exit;
ndx := fNameValue.Find(aKey);
if ndx<0 then
exit;
with fNameValue.List[ndx] do begin
result := Value;
if aResultTag<>nil then
aResultTag^ := Tag;
end;
end;
@ -12483,16 +12501,10 @@ begin
fSafe.Lock;
try
if Count<>0 then begin
if fRamUsed<131072 then // no capacity change for small cache content
fNameValue.Count := 0 else
with fNameValue.DynArray{$ifdef UNDIRECTDYNARRAY}.InternalDynArray{$endif} do begin
Capacity := 0; // force free all fNameValue.List[] key/value pairs
Capacity := 200; // then reserve some space for future cached entries
end;
fNameValue.DynArray.Clear;
fNameValue.DynArray.ReHash;
result := true; // mark something was flushed
end;
fFindLastAddedIndex := -1; // fFindLastKey should remain untouched for Add()
fRamUsed := 0;
fTimeoutTix := 0;
finally
@ -13700,27 +13712,31 @@ end;
procedure TSynAuthenticationAbstract.AuthenticateUser(const aName, aPassword: RawUTF8);
begin
raise ESynException.CreateFmt('%.AuthenticateUser() is not implemented',[self]);
raise ESynException.CreateUTF8('%.AuthenticateUser() is not implemented',[self]);
end;
procedure TSynAuthenticationAbstract.DisauthenticateUser(const aName: RawUTF8);
begin
raise ESynException.CreateFmt('%.DisauthenticateUser() is not implemented',[self]);
raise ESynException.CreateUTF8('%.DisauthenticateUser() is not implemented',[self]);
end;
function TSynAuthenticationAbstract.CreateSession(const User: RawUTF8; Hash: cardinal): integer;
function TSynAuthenticationAbstract.CheckCredentials(const UserName: RaWUTF8;
Hash: cardinal): boolean;
var password: RawUTF8;
begin
result := GetPassword(UserName,password) and
((ComputeCredential(false,UserName,password)=Hash) or
(ComputeCredential(true,UserName,password)=Hash));
end;
function TSynAuthenticationAbstract.CreateSession(const User: RawUTF8;
Hash: cardinal): integer;
begin
result := 0;
fSafe.Lock;
try
// check the given Hash challenge, against stored credentials
if not GetPassword(User,password) then
if not CheckCredentials(User,Hash) then
exit;
if (ComputeCredential(false,User,password)<>Hash) and
(ComputeCredential(true,User,password)<>Hash) then
exit;
// create the new session
repeat
result := fSessionGenerator;
inc(fSessionGenerator);
@ -14278,7 +14294,7 @@ begin
end;
end;
function VariantVTypeToSQLDBFieldType(VType: word): TSQLDBFieldType;
function VariantVTypeToSQLDBFieldType(VType: cardinal): TSQLDBFieldType;
begin
case VType of
varNull:
@ -14503,11 +14519,16 @@ begin
end;
constructor TJSONWriter.Create(aStream: TStream; Expand, withID: boolean;
const Fields: TSQLFieldIndexDynArray; aBufSize: integer);
const Fields: TSQLFieldIndexDynArray; aBufSize: integer;
aStackBuffer: PTextWriterStackBuffer);
begin
if aStream=nil then
CreateOwnedStream else
inherited Create(aStream,aBufSize);
if aStackBuffer<>nil then
CreateOwnedStream(aStackBuffer^) else
CreateOwnedStream(aBufSize) else
if aStackBuffer<>nil then
inherited Create(aStream,aStackBuffer,SizeOf(aStackBuffer^)) else
inherited Create(aStream,aBufSize);
fExpand := Expand;
fWithID := withID;
fFields := Fields;
@ -15871,7 +15892,7 @@ begin
if aClass=nil then
fClass := TBlockingProcessPoolItem else
fClass := aClass;
fPool := TObjectListLocked.Create(true);
fPool := TSynObjectListLocked.Create;
end;
const
@ -16619,7 +16640,7 @@ var i: integer;
if not withfreespace or not GetDiskInfo(p.mounted,av,fr,tot) then
{$ifdef MSWINDOWS}
FormatShort('%: % (%)',[p.mounted[1],p.name,KB(p.size,nospace)],result) else
FormatShort(F[nospace],[p.mounted[1],p.name,KB(p.size,nospace)],result);
FormatShort(F[nospace],[p.mounted[1],p.name,KB(fr,nospace),KB(tot,nospace)],result);
{$else}
FormatShort('% % (%)',[p.mounted,p.name,KB(p.size,nospace)],result) else
FormatShort(F[nospace],[p.mounted,p.name,KB(fr,nospace),KB(tot,nospace)],result);
@ -17088,7 +17109,7 @@ end;
procedure TSynTimeZone.LoadFromBuffer(const Buffer: RawByteString);
begin
fZones.LoadFromBinary(AlgoSynLZ.Decompress(Buffer),{nohash=}true);
fZones.ReHash(false);
fZones.ReHash;
FreeAndNil(fIds);
FreeAndNil(fDisplays);
end;
@ -17685,7 +17706,7 @@ var W: TTextWriter;
tmp: TTextWriterStackBuffer;
begin
if PosExChar(#$f0,text)=0 then begin
result := text; // no smiley UTF-8 for sure
result := text; // no UTF-8 smiley for sure
exit;
end;
W := TTextWriter.CreateOwnedStream(tmp);

View File

@ -76,11 +76,12 @@
// - it will avoid error like "[DCC Error] E2201 Need imported data reference ($G)
// to access 'VarCopyProc' from unit 'SynCommons'"
// - shall be set at the package options level, and left untouched by default
// - note: you should probably also set "Generate DCUs only" in Project Options
// -> Delphi Compiler -> Output C/C++ -> C/C++ output file generation
{.$define DOPATCHTRTL}
// if defined, the low-level patches made to RecordCopy() low-level function
// as defined in SynCommons.pas will be applied (if applicable to your Delphi
// version) - you should better use it, but we have unset it by default
// if defined, some low-level patches are applied to Delphi or FPC RTL
// - you should better use it, but we have unset it by default
{.$define NEWRTTINOTUSED}
// if defined, the new RTTI (available since Delphi 2010) won't be linked to
@ -103,11 +104,6 @@
{.$define OLDTEXTWRITERFORMAT}
// force TTextWriter.Add(Format) to handle the alternate deprecated $ % tags
{.$define OPT4AMD}
// you may define this to optimize for AMD CPUs - e.g. to use "set of byte"
// which will use BT[mem] opcodes, which are slow on Intel, but fast on AMD
// (with the Delphi x86 compiler, may not be the case for LLVM or FPC)
{.$define FORCE_STRSSE42}
// sse4.2 string instructions may read up to 16 bytes after the actual end buffer
// -> define this if you want StrLen/StrComp/strspn/strcspn to use SSE4.2 opcodes
@ -168,27 +164,29 @@
{$INLINE ON}
{$MINENUMSIZE 1}
{$PACKRECORDS DEFAULT} // force normal alignment
{$PACKSET 1}
{$PACKENUM 1}
{$CODEPAGE UTF8} // otherwise unexpected behavior occurs in most cases
{$undef ENHANCEDRTL} // there is no version of our Enhanced RTL for FPC
{$undef DOPATCHTRTL}
{$define HASINLINE}
{$define HASUINT64}
{$define HASINLINENOTX86}
{$define NODELPHIASM} // ignore low-level System.@LStrFromPCharLen calls
{$define HASAESNI} // should be commented to test project with valgrind
{$define HASTTHREADSTART}
{$define HASINTERFACEASTOBJECT}
{$define EXTENDEDTOSTRING_USESTR} // FloatToText uses str() in FPC
{$define EXTENDEDTOSHORT_USESTR} // FloatToText uses str() in FPC
{$define DOUBLETOSHORT_USEGRISU} // fast double to text
{$define DELPHI5ORFPC}
{$define FPC_OR_PUREPASCAL}
{$define FPC_OR_KYLIX}
{$define FPC_OR_UNICODE}
{$define USERECORDWITHMETHODS}
{$define FPC_OR_DELPHIXE}
{$define FPC_OR_DELPHIXE4}
{$define FPC_ENUMHASINNER}
{$define USE_VTYPE_STATIC} // in our inlined VarClear()
// $if FPC_FULLVERSION>20700 breaks Delphi 6-7 and SynProject :(
{$ifdef VER2_7}
@ -259,25 +257,39 @@
{$define FPCSQLITE3STATIC} // use static/arm-linux\sqlite3.o
{$endif}
{$ifdef CPUAARCH64}
{$ifdef ANDROID}
{$define FPCSQLITE3STATIC} // note: problems with current static/aarch64-linux\sqlite3.o
{$endif}
{$define FPCSQLITE3STATIC} // use:static/aarch64-linux\sqlite3.o
{$endif}
{$endif}
{$ifdef BSD}
// LINUX conditional includes Darwin and BSD family like FreeBSD
{$define LINUX} // not true, but a POSIX/BSD system - see LINUXNOTBSD
{$undef FORCE_STRSSE42} // fails otherwise for sure
{$define ABSOLUTEPASCAL} // NO asm nor redirection (until stabilized)
{$ifdef DARWIN}
{$define FPCSQLITE3STATIC} // we supply Darwin static libs
{$ifdef CPUINTEL}
{$define FPC_PIC} // may have not be defined by the compiler options
{$endif}
{$undef FORCE_STRSSE42} // fails otherwise for sure
{$define ABSOLUTEPASCAL} // NO asm nor redirection (until stabilized)
{$else}
{$define BSDNOTDARWIN} // OSX has some non-standard API calls
{$endif}
{$ifdef FREEBSD}
{$ifdef CPUX86}
{$define FPCSQLITE3STATIC} // we supply i386 static libs
{$endif CPUX86}
{$ifdef CPUX64}
{$define FPCSQLITE3STATIC} // we supply x64 static libs
{$endif CPUX64}
{$endif}
{$ifdef OPENBSD}
{$ifdef CPUX86}
{$define FPCSQLITE3STATIC} // we supply i386 static libs
{$endif CPUX86}
{$ifdef CPUX64}
{$define FPCSQLITE3STATIC} // we supply x64 static libs
{$endif CPUX64}
{$endif}
{$else}
{$ifdef LINUX}
{$define LINUXNOTBSD} // e.g. to disable epoll API
@ -296,15 +308,24 @@
{$define PUREPASCAL} // most asm code is not PIC-safe with global constants
{$endif}
{$ifdef MSWINDOWS}
{$ifdef FPC_X64MM}
{$ifndef FPC_X64MM_WIN} // SynFPCx64MM not yet fully validated on Windows
{$undef FPC_X64MM}
{$endif FPC_X64MM_WIN}
{$endif FPC_X64MM}
{$endif MSWINDOWS}
{$ifdef CPU64}
{$define FPC_64}
{$define PUREPASCAL} // e.g. x64, AARCH64
{$ifdef CPUX64}
{$define CPUINTEL}
{$define FPC_CPUINTEL}
{$ifndef DARWIN}
{$define CPUX64ASM} // Delphi XE4 SSE asm is buggy :(
{$endif DARWIN}
{$ifndef BSD}
{$define CPUX64ASM} // Delphi XE4 or Darwin asm are buggy :(
{$define HASAESNI} // SynCrypto rejected by Darwin asm
{$endif BSD}
{$define FPC_X64} // supports AVX/AVX2/AVX512 - which Delphi doesn't
{$ASMMODE INTEL} // to share asm code with Delphi
{$endif CPUX64}
@ -321,13 +342,24 @@
{$define CPUINTEL}
{$define FPC_CPUINTEL}
{$define FPC_X86}
{$ASMMODE INTEL} // as Delphi expects
{$define TSYNEXTENDED80} // only 32-bit has a true x87 extended type
{$ASMMODE INTEL} // as Delphi expects
{$define HASAESNI} // should be commented to test project with valgrind
{$ifndef FPC_PIC}
{$define CPUX86NOTPIC} // use "absolute" instead of local register
{$endif FPC_PIC}
{$ifndef OPENBSD}
{$define FPC_X86ASM} // if assembler knows popcnt/crc32c opcodes
{$endif OPENBSD}
{$endif CPUX86}
{$endif CPU64}
{$ifdef CPUARM3264}
{$ifdef BSD}
{$undef USE_VTYPE_STATIC} // circumvent bug in VarClear() on BSD + ARM
{$endif BSD}
{$endif CPUARM3264}
{$ifdef ISFPC30}
{$ifndef MSWINDOWS}
// define FPCUSEVERSIONINFO to link low-level executable file information
@ -344,10 +376,7 @@
{$ifdef CPUINTEL}
{$define HASINTERFACERTTI}
{$endif}
{$ifdef CPUARM}
{$define HASINTERFACERTTI}
{$endif}
{$ifdef CPUAARCH64}
{$ifdef CPUARM3264}
{$define HASINTERFACERTTI}
{$endif}
{$endif}
@ -387,23 +416,28 @@
(********************** Delphi Conditionals **********************)
{$define DELPHI_OR_FPC_OLDRTTI}
{$define USE_VTYPE_STATIC} // "and VTYPE_STATIC" test before VarClear()
{$undef FPC_X64MM} // this is a FPC-specific memory manager
{$A+} // force normal alignment
{$ifdef LVCL}
{$define OWNNORMTOUPPER} // NormToUpper[] exists only in our enhanced RTL
{$define NOVARIANTS} // LVCL does not support variants
{$define EXTENDEDTOSTRING_USESTR} // no FloatToText implemented in LVCL
{$endif}
{$define EXTENDEDTOSHORT_USESTR} // no FloatToText implemented in LVCL
{$endif LVCL}
{$ifdef UNICODE}
{$undef ENHANCEDRTL} // Delphi 2009 and up don't have our Enhanced Runtime library
{$define HASVARUSTRING}
{$define HASCODEPAGE}
{$define FPC_OR_UNICODE}
{$define USERECORDWITHMETHODS}
{ due to a bug in Delphi 2009+, we need to fake inheritance of record,
since TDynArrayHashed = object(TDynArray) fails to initialize
http://blog.synopse.info/post/2011/01/29/record-and-object-issue-in-Delphi-2010 }
{$define UNDIRECTDYNARRAY}
{$endif}
{$endif UNICODE}
{$ifndef PUREPASCAL}
{$define CPUINTEL} // Delphi only for Intel by now
@ -413,13 +447,15 @@
{$define CPU64DELPHI}
{$undef CPU32}
{$define PUREPASCAL} // no x86 32 bit asm to be used
{$define EXTENDEDTOSTRING_USESTR} // FloatToText() much slower in x64 mode
{$define EXTENDEDTOSHORT_USESTR} // FloatToText() much slower in x64 mode
{$define DOUBLETOSHORT_USEGRISU} // fast double to text
{$else CPUX64}
{$define CPU32} // Delphi compiler for 32 bit CPU
{$define CPU32DELPHI}
{$undef CPU64}
{$define CPUX86} // for compatibility with older versions of Delphi
{$define CPUX86NOTPIC} // use "absolute" instead of local register
{$define TSYNEXTENDED80} // only 32-bit has a true x87 extended type
{$endif CPUX64}
{$IFDEF CONDITIONALEXPRESSIONS} // Delphi 6 or newer
@ -430,7 +466,7 @@
{$define FPC_OR_KYLIX}
// Kylix 3 will be handled just like Delphi 7
{$undef ENHANCEDRTL} // Enhanced Runtime library not fully tested yet
{$define EXTENDEDTOSTRING_USESTR}
{$define EXTENDEDTOSHORT_USESTR}
{$define DOPATCHTRTL} // nice speed up for server apps
{$define NOVARCOPYPROC}
{$define NOSQLITE3STATIC} // Kylix will use external sqlite3.so
@ -444,7 +480,7 @@
{$define DELPHI6OROLDER}
{$define NOVARCOPYPROC}
{$undef ENHANCEDRTL} // Delphi 6 doesn't have our Enhanced Runtime library
{$define EXTENDEDTOSTRING_USESTR} // no TFormatSettings before Delphi 7
{$define EXTENDEDTOSHORT_USESTR} // no TFormatSettings before Delphi 7
{$else}
{$define ISDELPHI7ANDUP} // Delphi 7 or newer
{$define WITHUXTHEME} // VCL handle UI theming
@ -468,7 +504,7 @@
{$define HASINLINENOTX86}
{$define HASREGION}
{$define HASFASTMM4}
// you can define this so that GetMemoryInfo/TSynMonitorMemory returns
// try to define this so that GetMemoryInfo/TSynMonitorMemory returns
// low-level FastMM4 information
{.$define WITH_FASTMM4STATS}
{$ifend}
@ -483,8 +519,6 @@
{$ifend}
{$if CompilerVersion = 20}
{$define ISDELPHI20092010} // specific compilation issues
// for Delphi 2009 and up, use UNICODE conditional
{$define FPC_OR_UNICODE}
{$ifend}
{$if CompilerVersion = 21}
{$define ISDELPHI20092010} //specific compilation issues
@ -493,7 +527,6 @@
{$define ISDELPHI2010}
{$define ISDELPHI2010_OR_FPC} // eltype2 field
{$define ISDELPHI2010_OR_FPC_NEWRTTI}
{$define FPC_OR_UNICODE}
{$define HASTTHREADSTART}
{$define HASINTERFACEASTOBJECT}
{$ifdef NEWRTTINOTUSED} // option reduce EXE size by disabling much RTTI
@ -557,6 +590,9 @@
{$if CompilerVersion >= 33.0}
{$define ISDELPHI103}
{$ifend}
{$if CompilerVersion >= 34.0}
{$define ISDELPHI104}
{$ifend}
{$ifend CompilerVersion >= 17}
{$ifopt O-} // if we don't expect fast code, don't optimize the framework
{$undef ENHANCEDRTL}
@ -571,7 +607,7 @@
{$define NOVARIANTS}
{$define NOVARCOPYPROC}
{$undef ENHANCEDRTL} // Delphi 5 doesn't have our Enhanced Runtime library
{$define EXTENDEDTOSTRING_USESTR} // no TFormatSettings before Delphi 7
{$define EXTENDEDTOSHORT_USESTR} // no TFormatSettings before Delphi 7
{$undef DOPATCHTRTL}
{$ENDIF CONDITIONALEXPRESSIONS}
@ -630,6 +666,7 @@
{$ifdef ABSOLUTEPASCAL}
{$define ABSOLUTEORPUREPASCAL}
{$define ABSOLUTEPASCALORNOTINTEL}
{$define PUREPASCAL}
{$endif ABSOLUTEPASCAL}
{$ifdef PUREPASCAL}
{$define ABSOLUTEORPUREPASCAL}
@ -651,6 +688,7 @@
{$ifdef CPUX64}
{$if CompilerVersion = 25.0} // exactly XE4
{$define NOSQLITE3STATIC}
// :( to avoid "Fatal: F2084 Internal Error: AV004A7B1F-R03BDA7B0-0"
{$ifend}
{$endif} // other Win32/Win64 Delphi platforms should work as expected
{$endif FPC}
@ -666,11 +704,12 @@
{.$define USELIBCURL} // for testing (no benefit vs WinHTTP)
{$else}
{$define ONLYUSEHTTPSOCKET} // http.sys server is Windows-specific
{$ifndef ANDROID}
// cross-platform libcurl for https -> TCurlHttp and TSQLHttpClientCurl
{$define USELIBCURL}
// cross-platform libcurl for https -> TCurlHttp and TSQLHttpClientCurl
{$define USELIBCURL}
{$ifdef ANDROID}
// for Android, consider using https://github.com/gcesarmza/curl-android-ios
// static libraries and force USELIBCURL in the project conditionals
{$define LIBCURLSTATIC}
{$endif ANDROID}
{$endif MSWINDOWS}

View File

@ -1 +1 @@
'1.18.5770'
'1.18.6186'

Binary file not shown.

Binary file not shown.

View File

@ -45,11 +45,9 @@ uses
{$I dmvcframeworkbuildconsts.inc}
type
TMVCHTTPMethodType = (httpGET, httpPOST, httpPUT, httpDELETE, httpHEAD, httpOPTIONS, httpPATCH,
httpTRACE);
TMVCHTTPMethodType = (httpGET, httpPOST, httpPUT, httpDELETE, httpHEAD, httpOPTIONS, httpPATCH, httpTRACE);
TMVCHTTPMethods = set of TMVCHTTPMethodType;
@ -358,10 +356,9 @@ type
procedure CheckHTTPErrorCode(const AHTTPErrorCode: UInt16);
public
constructor Create(const AMsg: string); overload; virtual;
constructor Create(const AMsg: string; const ADetailedMessage: string;
const AAppErrorCode: UInt16 = 0;
const AHTTPErrorCode: UInt16 = HTTP_STATUS.InternalServerError;
const AErrorItems: TArray<String> = nil); overload; virtual;
constructor Create(const AMsg: string; const ADetailedMessage: string; const AAppErrorCode: UInt16 = 0;
const AHTTPErrorCode: UInt16 = HTTP_STATUS.InternalServerError; const AErrorItems: TArray<String> = nil);
overload; virtual;
constructor Create(const AHTTPErrorCode: UInt16; const AMsg: string); overload; virtual;
constructor Create(const AHTTPErrorCode: UInt16; const AAppErrorCode: Integer; const AMsg: string);
overload; virtual;
@ -590,6 +587,16 @@ type
class function GuidFromString(const AGuidStr: string): TGUID; static;
end;
TMVCHeaders = class(TStringList)
private
function GetValue(const Name: string): string;
procedure SetValue(const Name, Value: string);
public
constructor Create;
function IndexOfName(const Name: string): Integer; override;
property Values[const Name: string]: string read GetValue write SetValue;
end;
TMVCFieldsMapping = TArray<TMVCFieldMap>;
{$SCOPEDENUMS ON}
@ -608,6 +615,9 @@ function URLSafeB64encode(const Value: string; IncludePadding: Boolean; AByteEnc
function URLSafeB64encode(const Value: TBytes; IncludePadding: Boolean): string; overload;
function URLSafeB64Decode(const Value: string; AByteEncoding: IIdTextEncoding = nil): string;
function URLEncode(const aValue: string): String;
function URLDecode(const aValue: string): String;
function ByteToHex(AInByte: Byte): string;
function BytesToHex(ABytes: TBytes): string;
procedure Base64StringToFile(const aBase64String, AFileName: string; const aOverwrite: Boolean = False);
@ -617,9 +627,8 @@ procedure SplitContentMediaTypeAndCharset(const aContentType: string; var aConte
var aContentCharSet: string);
function BuildContentType(const aContentMediaType: string; const aContentCharSet: string): string;
function StrToJSONObject(const aString: String): TJsonObject;
function StrToJSONArray(const aString: String): TJsonArray;
function StrToJSONObject(const AString: String): TJsonObject;
function StrToJSONArray(const AString: String): TJsonArray;
function WrapAsList(const AObject: TObject; AOwnsObject: Boolean = False): IMVCList;
@ -641,22 +650,18 @@ var
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'), ('127.0.0.0', '127.255.255.255'),
('169.254.0.0', '169.254.255.255'),
('10.0.0.0', '10.255.255.255'), ('127.0.0.0', '127.255.255.255'), ('169.254.0.0', '169.254.255.255'),
('172.16.0.0', '172.31.255.255'), ('192.0.2.0', '192.0.2.255'), ('192.88.99.0', '192.88.99.255'),
('192.168.0.0', '192.168.255.255'), ('198.18.0.0', '198.19.255.255'),
('224.0.0.0', '239.255.255.255'),
('192.168.0.0', '192.168.255.255'), ('198.18.0.0', '198.19.255.255'), ('224.0.0.0', '239.255.255.255'),
('240.0.0.0', '255.255.255.255'));
type
TMVCParseAuthentication = class
public
class procedure OnParseAuthentication(AContext: TIdContext; const AAuthType, AAuthData: string; var VUsername,
VPassword: string; var VHandled: Boolean);
class procedure OnParseAuthentication(AContext: TIdContext; const AAuthType, AAuthData: string;
var VUsername, VPassword: string; var VHandled: Boolean);
end;
implementation
uses
@ -681,8 +686,7 @@ begin
if AIP.IsEmpty then
Exit(0);
lPieces := AIP.Split(['.']);
Result := (StrToInt(lPieces[0]) * 16777216) + (StrToInt(lPieces[1]) * 65536) +
(StrToInt(lPieces[2]) * 256) +
Result := (StrToInt(lPieces[0]) * 16777216) + (StrToInt(lPieces[1]) * 65536) + (StrToInt(lPieces[2]) * 256) +
StrToInt(lPieces[3]);
end;
@ -709,6 +713,16 @@ begin
Result := TIdEncoderMIME.EncodeString(aValue);
end;
function URLEncode(const aValue: string): String;
begin
Result := TNetEncoding.URL.Encode(aValue);
end;
function URLDecode(const aValue: string): String;
begin
Result := TNetEncoding.URL.Decode(aValue);
end;
function B64Encode(const aValue: TBytes): string; overload;
begin
// Do not use TNetEncoding
@ -753,9 +767,7 @@ begin
begin
Result := lContentMediaType;
end
else
if lContentMediaType.StartsWith('text/') or lContentMediaType.StartsWith('application/')
then
else if lContentMediaType.StartsWith('text/') or lContentMediaType.StartsWith('application/') then
begin
Result := lContentMediaType + ';charset=' + aContentCharSet.ToLower;
end
@ -775,8 +787,7 @@ begin
begin
lContentTypeValues := aContentType.Split([';']);
aContentMediaType := Trim(lContentTypeValues[0]);
if (Length(lContentTypeValues) > 1) and (lContentTypeValues[1].Trim.StartsWith('charset', True))
then
if (Length(lContentTypeValues) > 1) and (lContentTypeValues[1].Trim.StartsWith('charset', True)) then
begin
aContentCharSet := lContentTypeValues[1].Trim.Split(['='])[1].Trim;
end
@ -803,8 +814,8 @@ begin
SetLength(FErrorItems, 0);
end;
constructor EMVCException.Create(const AMsg, ADetailedMessage: string;
const AAppErrorCode, AHTTPErrorCode: UInt16; const AErrorItems: TArray<String>);
constructor EMVCException.Create(const AMsg, ADetailedMessage: string; const AAppErrorCode, AHTTPErrorCode: UInt16;
const AErrorItems: TArray<String>);
begin
Create(AMsg);
CheckHTTPErrorCode(AHTTPErrorCode);
@ -832,8 +843,7 @@ begin
end;
end;
constructor EMVCException.Create(const AHTTPErrorCode: UInt16;
const AAppErrorCode: Integer; const AMsg: string);
constructor EMVCException.Create(const AHTTPErrorCode: UInt16; const AAppErrorCode: Integer; const AMsg: string);
begin
CheckHTTPErrorCode(AHTTPErrorCode);
Create(AMsg);
@ -841,8 +851,7 @@ begin
FAppErrorCode := AAppErrorCode;
end;
constructor EMVCException.CreateFmt(const AHTTPErrorCode: UInt16;
const AMsg: string; const AArgs: array of const);
constructor EMVCException.CreateFmt(const AHTTPErrorCode: UInt16; const AMsg: string; const AArgs: array of const);
begin
inherited CreateFmt(AMsg, AArgs);
FHttpErrorCode := AHTTPErrorCode;
@ -874,8 +883,7 @@ begin
end;
end;
function TMVCCriticalSectionHelper.DoWithLockTimeout(const AAction: TProc; const ATimeOut: UInt32)
: TWaitResult;
function TMVCCriticalSectionHelper.DoWithLockTimeout(const AAction: TProc; const ATimeOut: UInt32): TWaitResult;
begin
Result := Self.WaitFor(ATimeOut);
if (Result = TWaitResult.wrSignaled) then
@ -892,7 +900,7 @@ procedure TMVCConfig.CheckNotFreezed;
begin
if FFreezed then
begin
raise EMVCException.Create('Configuration in freezed - no more changes allowed') at ReturnAddress;
raise EMVCException.Create('Configuration in freezed - no more changes allowed')at ReturnAddress;
end;
end;
@ -938,8 +946,7 @@ var
lStreamReader: TStreamReader;
lSer: TMVCJsonDataObjectsSerializer;
begin
lStreamReader := TStreamReader.Create(TFileStream.Create(AFileName,
fmOpenRead or fmShareDenyWrite), TEncoding.ASCII);
lStreamReader := TStreamReader.Create(TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite), TEncoding.ASCII);
try
lStreamReader.OwnStream;
lConfigString := lStreamReader.ReadToEnd;
@ -986,7 +993,7 @@ var
I: Integer;
lName: string;
begin
for I := 0 to Strings.Count-1 do
for I := 0 to Strings.Count - 1 do
begin
lName := Strings.Names[I];
Add(lName, Strings.Values[lName]);
@ -1135,8 +1142,7 @@ type
end;
const
GURLSafeBase64CodeTable
: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
GURLSafeBase64CodeTable: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
{ Do not Localize }
procedure TURLSafeEncode.InitComponent;
@ -1287,8 +1293,7 @@ end;
{ TMVCDecoratorObject }
function TMVCDecoratorObject.Add(const PropName,
PropValue: string): IMVCLinkItem;
function TMVCDecoratorObject.Add(const PropName, PropValue: string): IMVCLinkItem;
begin
fData.Items[PropName] := PropValue;
Result := Self;
@ -1355,8 +1360,7 @@ begin
end;
class procedure TMVCParseAuthentication.OnParseAuthentication(AContext: TIdContext; const AAuthType, AAuthData: string;
var VUsername,
VPassword: string; var VHandled: Boolean);
var VUsername, VPassword: string; var VHandled: Boolean);
begin
VHandled := SameText(LowerCase(AAuthType), 'bearer');
end;
@ -1395,7 +1399,7 @@ var
lIsAlpha: Boolean;
lIsNumber: Boolean;
begin
{TODO -oDanieleT -cGeneral : Make this function faster!}
{ TODO -oDanieleT -cGeneral : Make this function faster! }
lNextUpCase := MakeFirstUpperToo;
lPreviousWasUpperCase := True;
lSB := TStringBuilder.Create;
@ -1407,7 +1411,7 @@ begin
lIsUpperCase := CharInSet(C, ['A' .. 'Z']);
lIsNumber := CharInSet(C, ['0' .. '9']);
lIsAlpha := lIsLowerCase or lIsUpperCase;
if not (lIsAlpha or lIsNumber) then
if not(lIsAlpha or lIsNumber) then
begin
lNextUpCase := True;
lPreviousWasUpperCase := False;
@ -1470,15 +1474,13 @@ begin
lIsNumber := CharInSet(C, ['0' .. '9']);
lIsUnderscore := C = '_';
if (I > 0) and (not lLastWasUnderscore) and
((lIsUpperCase and (lLastWasLowercase or lLastWasNumber)) or
(lIsLowerCase and lLastWasNumber) or
(lIsNumber and (not lLastWasNumber))) then
if (I > 0) and (not lLastWasUnderscore) and ((lIsUpperCase and (lLastWasLowercase or lLastWasNumber)) or
(lIsLowerCase and lLastWasNumber) or (lIsNumber and (not lLastWasNumber))) then
begin
lSB.Append('_');
end;
if not (lLastWasUnderscore and lIsUnderscore) then
if not(lLastWasUnderscore and lIsUnderscore) then
begin
lSB.Append(LowerCase(C));
end;
@ -1492,29 +1494,86 @@ begin
end;
end;
function StrToJSONObject(const aString: String): TJsonObject;
function StrToJSONObject(const AString: String): TJsonObject;
begin
Result := MVCFramework.Serializer.JSONDataObjects.StrToJSONObject(aString);
Result := MVCFramework.Serializer.JsonDataObjects.StrToJSONObject(AString);
end;
function StrToJSONArray(const aString: String): TJsonArray;
function StrToJSONArray(const AString: String): TJsonArray;
begin
Result := MVCFramework.Serializer.JSONDataObjects.StrToJSONArray(aString);
Result := MVCFramework.Serializer.JsonDataObjects.StrToJSONArray(AString);
end;
function WrapAsList(const AObject: TObject; AOwnsObject: Boolean = False): IMVCList;
begin
Result := MVCFramework.DuckTyping.WrapAsList(AObject, AOwnsObject);
end;
{ TMVCHeaders }
constructor TMVCHeaders.Create;
begin
inherited;
NameValueSeparator := ':';
end;
function TMVCHeaders.GetValue(const Name: string): string;
var
i: Integer;
begin
i := IndexOfName(Name);
if i >= 0 then
Result := Trim(Copy(Get(i), Length(Name) + 2, MaxInt))
else
Result := '';
end;
procedure TMVCHeaders.SetValue(const Name, Value: string);
var
i: Integer;
begin
i := IndexOfName(Name);
if Value <> '' then
begin
if i < 0 then
i := Add('');
Put(i, Name + NameValueSeparator + Value);
end
else
begin
if i >= 0 then
Delete(i);
end;
end;
function TMVCHeaders.IndexOfName(const Name: string): Integer;
var
P: Integer;
S: string;
lSub: String;
begin
for Result := 0 to GetCount - 1 do
begin
S := Get(Result);
P := AnsiPos(NameValueSeparator, S);
if (P <> 0) then
begin
lSub := Copy(S, 1, P - 1);
if SameText(lSub, Name) then
begin
Exit;
end;
end;
end;
Result := -1;
end;
initialization
gLock := TObject.Create;
// SGR 2017-07-03 : Initialize decoding table for URLSafe Gb64 encoding
TURLSafeDecode.ConstructDecodeTable(GURLSafeBase64CodeTable,
TURLSafeDecode.GSafeBaseBase64DecodeTable);
TURLSafeDecode.ConstructDecodeTable(GURLSafeBase64CodeTable, TURLSafeDecode.GSafeBaseBase64DecodeTable);
GlobalAppExe := ExtractFileName(GetModuleName(HInstance));
GlobalAppName := ChangeFileExt(GlobalAppExe, EmptyStr);

View File

@ -31,6 +31,8 @@
unit MVCFramework.HTTPSys.WebBrokerBridge;
{$DEFINE TRACE}
interface
uses
@ -67,20 +69,12 @@ type
procedure SetServerName(const aName: SockString); override;
end;
TMVCHeaders = class(TStringList)
private
function GetValue(const Name: string): string;
procedure SetValue(const Name, Value: string);
public
constructor Create;
function IndexOfName(const Name: string): Integer; override;
property Values[const Name: string]: string read GetValue write SetValue;
end;
TMVCHTTPSysAppRequest = class(TWebRequest)
private
fRequest: THttpServerRequest;
fHeaders: TMVCHeaders;
fURL: String;
fFullURL: String;
protected
fBody: TBytes;
function GetDateVariable(Index: Integer): TDateTime; override;
@ -206,7 +200,7 @@ begin
fRequest := ARequest;
inherited Create;
fHeaders := TMVCHeaders.Create;
ExtractFields([#13], [], fRequest.InHeaders, fHeaders);
ExtractFields([#13], [], String(fRequest.InHeaders), fHeaders);
// if fRequest.PostDataSize > 0 then
// begin
@ -296,15 +290,12 @@ end;
function TMVCHTTPSysAppRequest.GetRawPathInfo: string;
begin
// Result := fRequest.URI;
raise Exception.Create('DMVCFramework Not Implemented');
Result := GetStringVariable(ReqIDX_PathInfo);
end;
function TMVCHTTPSysAppRequest.GetRemoteIP: string;
begin
Result := fRequest.RemoteIP;
// Result := fRequest.RemoteIP;
// raise Exception.Create('DMVCFramework Not Implemented');
Result := String(fRequest.RemoteIP);
end;
function TMVCHTTPSysAppRequest.GetRawContent: TBytes;
@ -313,20 +304,45 @@ begin
end;
function TMVCHTTPSysAppRequest.GetStringVariable(Index: Integer): string;
var
lIdx: Integer;
begin
case Index of
ReqIDX_Method:
Result := fRequest.Method;
Result := String(fRequest.Method);
// ReqIDX_ProtocolVersion:
// Result := fRequest.Version;
ReqIDX_URL:
Result := fRequest.URL;
begin
if fURL = '' then
begin
fURL := URLDecode(String(fRequest.URL));
end;
Result := fURL;
end;
ReqIDX_Query:
Result := fRequest.FullURL;
begin
if fFullURL = '' then
begin
fFullURL := URLDecode(fRequest.FullURL);
end;
Result := fFullURL.Substring(fFullURL.IndexOf('?') + 1);
end;
ReqIDX_PathInfo:
Result := fRequest.URL;
begin
if fURL = '' then
begin
fURL := URLDecode(String(fRequest.URL));
end;
Result := fURL;
lIdx := Result.IndexOf('?');
if lIdx > -1 then
begin
Result := Result.Substring(0, lIdx);
end;
end;
ReqIDX_PathTranslated:
Result := fRequest.URL;
Result := String(fRequest.URL);
ReqIDX_CacheControl:
Result := fHeaders.Values['Cache-Control']; { do not localize }
ReqIDX_Date:
@ -441,9 +457,9 @@ end;
constructor TMVCHTTPSysAppResponse.Create(AResponse: THttpServerRequest);
begin
inherited Create(nil);
// fRequest := ARequest;
fResponse := AResponse;
fHeaders := TMVCHeaders.Create;
ContentType := BuildContentType(TMVCConstants.DEFAULT_CONTENT_TYPE, TMVCConstants.DEFAULT_CONTENT_CHARSET);
StatusCode := http_status.OK;
end;
@ -455,7 +471,7 @@ end;
function TMVCHTTPSysAppResponse.GetContent: string;
begin
Result := fResponse.OutContent;
Result := String(fResponse.OutContent);
end;
function TMVCHTTPSysAppResponse.GetLogMessage: string;
@ -615,15 +631,9 @@ end;
procedure TMVCHTTPSysAppResponse.SendResponse;
var
lBytes: TBytesStream;
lBuff: PSockString;
lByte: PByte;
lBS: TBytesStream;
lByteArray: TBytes;
lOutContent: SockString;
lSize: Int64;
begin
// raise Exception.Create('not implemented');
if fSent then
Exit;
fSent := True;
@ -636,72 +646,8 @@ begin
ContentStream.Read(lOutContent[1], lSize);
fResponse.OutContent := lOutContent;
end;
fResponse.OutContentType := ContentType;
fResponse.OutContentType := SockString(ContentType);
fResponse.OutCustomHeaders := GetHeaders;
// fResponse.OutContent
// fResponse.OutContentType := 'text/plain';
// fResponse.OutCustomHeaders := 'x-pippo: pluto';
// fResponse.OutContent := 'PIPPO';
// MergeHeaders;
// if (ContentStream = nil) then
// HTTPRequest.WriteString(Content)
// else
// begin
// SendStream(ContentStream);
// ContentStream := nil; // Drop the stream
// end;
// // if (fResponse.ContentType = '') and
// // ((fResponse.ContentText <> '') or (Assigned(FResponseInfo.ContentStream))) and
// // (HTTPApp.DefaultCharSet <> '') then
// // begin
// // // Indicate how to convert UTF16 when write.
// // ContentType := Format('text/html; charset=%s', [HTTPApp.DefaultCharSet]); { Do not Localize }
// // end;
// // fResponse.ContentType := fContentType;
//
// if (ContentStream <> nil) and (ContentStream.Size > 0) then
// begin
// fResponse.StatusCode := StatusCode;
// ContentStream.Position := 0;
// // if TFile.Exists('output.dat') then
// // TFile.Delete('output.dat');
// // var
// // fs := TFileStream.Create('output.dat', fmCreate);
// // try
// // fs.CopyFrom(ContentStream, 0);
// // finally
// // fs.Free;
// // end;
// // ContentStream.Position := 0;
// if ContentStream is TFileStream then
// begin
// var
// l := TMemoryStream.Create;
// l.CopyFrom(ContentStream, 0);
// l.Position := 0;
// SetContentStream(l);
// end;
//
// fResponse.Send(ContentStream, 0, ContentStream.Size,
// procedure(const AConnection: ICrossConnection; const ASuccess: Boolean)
// begin
// // AConnection.SendStream(ContentStream)
// end);
//
// // fResponse.Send(lBytes.Bytes, 0, lBytes.Size,
// // procedure(const AConnection: ICrossConnection; const ASuccess: Boolean)
// // begin
// // lBytes.Free;
// // end);
// // fResponse.SendFile('C:\DEV\dmvcframework\unittests\general\TestServer\bin\www\index.html')
// end
// else
// begin
// fResponse.SendStatus(StatusCode, '');
// end;
end;
procedure TMVCHTTPSysAppResponse.SendStream(AStream: TStream);
@ -733,34 +679,13 @@ end;
procedure TMVCHTTPSysAppResponse.SetContentStream(AValue: TStream);
begin
inherited SetContentStream(AValue);
// fResponse.Header.Add('content-length', AValue.Size.ToString, False);
// FResponseInfo.ContentStream := AValue;
end;
function DoHTTPEncode(const AStr: string): String;
begin
Result := TNetEncoding.URL.Encode(string(AStr));
end;
function TMVCHTTPSysAppResponse.GetHeaders;
function TMVCHTTPSysAppResponse.GetHeaders: SockString;
var
i: Integer;
lSrcCookie: TCookie;
lBuilder: TStringBuilder;
begin
// for i := 0 to Cookies.Count - 1 do
// begin
// lSrcCookie := Cookies[i];
// fResponse.Cookies.AddOrSet(lSrcCookie.Name, lSrcCookie.Value, SecondsBetween(Now, lSrcCookie.Expires),
// lSrcCookie.Path, lSrcCookie.Domain, lSrcCookie.HttpOnly, lSrcCookie.Secure);
// // LDestCookie := FResponseInfo.Cookies.Add;
// // LDestCookie.CookieName := DoHTTPEncode(LSrcCookie.Name);
// // LDestCookie.Value := DoHTTPEncode(LSrcCookie.Value);
// // LDestCookie.Domain := String(LSrcCookie.Domain);
// // LDestCookie.Path := String(LSrcCookie.Path);
// // LDestCookie.Expires := LSrcCookie.Expires;
// // LDestCookie.Secure := LSrcCookie.Secure;
// end;
lBuilder := TStringBuilder.Create(1024);
try
for i := 0 to fHeaders.Count - 1 do
@ -771,8 +696,11 @@ begin
begin
lBuilder.AppendLine(CustomHeaders.Names[i] + ':' + CustomHeaders.ValueFromIndex[i]);
end;
lBuilder.AppendLine('Server: Pippo');
Result := lBuilder.ToString;
for i := 0 to Cookies.Count - 1 do
begin
lBuilder.AppendLine('Set-Cookie: ' + Cookies[i].HeaderValue);
end;
Result := SockString(lBuilder.ToString);
finally
lBuilder.Free;
end;
@ -794,6 +722,7 @@ var
lRequest: TMVCHTTPSysAppRequest;
lResponse: TMVCHTTPSysAppResponse;
begin
Result := http_status.OK;
try
lRequest := TMVCHTTPSysAppRequest.Create(ReqResp);
try
@ -874,19 +803,43 @@ begin
end;
function TMVCHTTPSysWebBrokerBridge.DoHandleRequest(ReqResp: THttpServerRequest): Cardinal;
{$IF defined(TRACE)}
var
lFile: TFileStream;
lDirName, lReqFileName, lRespFileName: string;
const
LINE_BREAK = sLineBreak + sLineBreak + sLineBreak;
{$ENDIF}
begin
lFile := TFile.Create('req.2.dat');
{$IF defined(TRACE)}
lDirName := TPath.Combine(AppPath, 'trace_' + ReqResp.ConnectionID.ToString.PadLeft(10));
lReqFileName := TPath.Combine(lDirName, 'request.txt');
lRespFileName := TPath.Combine(lDirName, 'response.txt');
TDirectory.CreateDirectory(lDirName);
if TFile.Exists(lReqFileName) then
TFile.Delete(lReqFileName);
lFile := TFile.Create(lReqFileName, fmCreate or fmOpenWrite);
try
lFile.Write(ReqResp.InHeaders[1], Length(ReqResp.InHeaders));
lFile.Write(#13#10#13#10#13#10,6);
lFile.Write(LINE_BREAK[1], Length(LINE_BREAK));
lFile.Write(ReqResp.InContent[1], Length(ReqResp.InContent));
finally
lFile.Free;
end;
{$ENDIF}
Result := TMVCHTTPSysWebBrokerBridgeRequestHandler.FWebRequestHandler.Run(ReqResp);
{$IF defined(TRACE)}
if TFile.Exists(lRespFileName) then
TFile.Delete(lRespFileName);
lFile := TFile.Create(lRespFileName, fmCreate or fmOpenWrite);
try
lFile.Write(ReqResp.OutCustomHeaders[1], Length(ReqResp.OutCustomHeaders));
lFile.Write(LINE_BREAK[1], Length(LINE_BREAK));
lFile.Write(ReqResp.OutContent[1], Length(ReqResp.OutContent));
finally
lFile.Free;
end;
{$ENDIF}
end;
procedure TMVCHTTPSysWebBrokerBridge.SetActive(const Value: Boolean);
@ -898,9 +851,10 @@ begin
if Value then
begin
fHttpServer := TMVCHTTPSysServer.Create(False);
fHttpServer.AddUrl('', IntToStr(fDefaultPort), False, '+', True);
fHttpServer.RegisterCompress(CompressDeflate); // our server will deflate html :)
fHttpServer.AddUrl(SockString(''), SockString(IntToStr(fDefaultPort)), False, '+', True);
fHttpServer.RegisterCompress(CompressDeflate);
fHttpServer.OnRequest := DoHandleRequest;
{ TODO -odanielet -cGeneral : Try to find some adaptive and smart number here }
fHttpServer.Clone(31); // will use a thread pool of 32 threads in total
fActive := True;
// // {$IFDEF __CROSS_SSL__}
@ -949,65 +903,6 @@ begin
fServerName := 'DMVCFramework';
end;
{ TMVCHeaders }
constructor TMVCHeaders.Create;
begin
inherited;
NameValueSeparator := ':';
end;
function TMVCHeaders.GetValue(const Name: string): string;
var
i: Integer;
begin
i := IndexOfName(Name);
if i >= 0 then
Result := Trim(Copy(Get(i), Length(Name) + 2, MaxInt))
else
Result := '';
end;
procedure TMVCHeaders.SetValue(const Name, Value: string);
var
i: Integer;
begin
i := IndexOfName(Name);
if Value <> '' then
begin
if i < 0 then
i := Add('');
Put(i, Name + NameValueSeparator + Value);
end
else
begin
if i >= 0 then
Delete(i);
end;
end;
function TMVCHeaders.IndexOfName(const Name: string): Integer;
var
P: Integer;
S: string;
lSub: String;
begin
for Result := 0 to GetCount - 1 do
begin
S := Get(Result);
P := AnsiPos(NameValueSeparator, S);
if (P <> 0) then
begin
lSub := Copy(S, 1, P - 1);
if SameText(lSub, Name) then
begin
Exit;
end;
end;
end;
Result := -1;
end;
initialization
WebReq.WebRequestHandlerProc := MVCHTTPSysWebBrokerBridgeRequestHandler;

View File

@ -73,11 +73,9 @@ type
function SendStaticFileIfPresent(const AContext: TWebContext; const AFileName: string): Boolean;
procedure DoSanityCheck;
public
constructor Create(
const AStaticFilesPath: string = TMVCStaticFilesDefaults.STATIC_FILES_PATH;
constructor Create(const AStaticFilesPath: string = TMVCStaticFilesDefaults.STATIC_FILES_PATH;
const ADocumentRoot: string = TMVCStaticFilesDefaults.DOCUMENT_ROOT;
const AIndexDocument: string = TMVCStaticFilesDefaults.INDEX_DOCUMENT;
const ASPAWebAppSupport: Boolean = True;
const AIndexDocument: string = TMVCStaticFilesDefaults.INDEX_DOCUMENT; const ASPAWebAppSupport: Boolean = True;
const AStaticFilesCharset: string = TMVCStaticFilesDefaults.STATIC_FILES_CONTENT_CHARSET);
destructor Destroy; override;
@ -95,7 +93,9 @@ implementation
uses
System.SysUtils,
System.NetEncoding,
System.IOUtils, System.Classes;
System.IOUtils,
System.Classes,
MVCFramework.Logger;
{ TMVCStaticFilesMiddleware }
@ -119,11 +119,9 @@ begin
fMediaTypes.Add('.gif', TMVCMediaType.IMAGE_GIF);
end;
constructor TMVCStaticFilesMiddleware.Create(
const AStaticFilesPath: string = TMVCStaticFilesDefaults.STATIC_FILES_PATH;
constructor TMVCStaticFilesMiddleware.Create(const AStaticFilesPath: string = TMVCStaticFilesDefaults.STATIC_FILES_PATH;
const ADocumentRoot: string = TMVCStaticFilesDefaults.DOCUMENT_ROOT;
const AIndexDocument: string = TMVCStaticFilesDefaults.INDEX_DOCUMENT;
const ASPAWebAppSupport: Boolean = True;
const AIndexDocument: string = TMVCStaticFilesDefaults.INDEX_DOCUMENT; const ASPAWebAppSupport: Boolean = True;
const AStaticFilesCharset: string = TMVCStaticFilesDefaults.STATIC_FILES_CONTENT_CHARSET);
begin
inherited Create;
@ -186,8 +184,8 @@ begin
// do nothing
end;
procedure TMVCStaticFilesMiddleware.OnBeforeControllerAction(AContext: TWebContext; const AControllerQualifiedClassName,
AActionName: string; var AHandled: Boolean);
procedure TMVCStaticFilesMiddleware.OnBeforeControllerAction(AContext: TWebContext;
const AControllerQualifiedClassName, AActionName: string; var AHandled: Boolean);
begin
// do nothing
end;
@ -213,11 +211,10 @@ begin
Exit;
end;
LogI('File: ' + lPathInfo);
// calculate the actual requested path
if lPathInfo.StartsWith(fStaticFilesPath, True) then
begin
lPathInfo := lPathInfo.Remove(0, fStaticFilesPath.Length);
end;
lPathInfo := lPathInfo.Remove(0, fStaticFilesPath.Length);
lPathInfo := lPathInfo.Replace('/', PathDelim, [rfReplaceAll]);
if lPathInfo.StartsWith(PathDelim) then
begin
@ -227,8 +224,7 @@ begin
{ Now the actual requested path is in lFullPathInfo }
if TMVCStaticContents.IsStaticFile(fDocumentRoot, lPathInfo, lRealFileName,
lIsDirectoryTraversalAttach) then
if TMVCStaticContents.IsStaticFile(fDocumentRoot, lPathInfo, lRealFileName, lIsDirectoryTraversalAttach) then
begin
// check if it's a direct file request
// lIsFileRequest := TMVCStaticContents.IsStaticFile(fDocumentRoot, lPathInfo, lRealFileName,
@ -274,6 +270,14 @@ begin
lFileName := TPath.GetFullPath(TPath.Combine(lFullPathInfo, fIndexDocument));
AHandled := SendStaticFileIfPresent(AContext, lFileName);
end;
if not AHandled then
begin
AContext.Response.StatusCode := HTTP_STATUS.NotFound;
AContext.Response.Content := '404 Not Found';
AHandled := True;
end;
end;
function TMVCStaticFilesMiddleware.SendStaticFileIfPresent(const AContext: TWebContext;

View File

@ -96,6 +96,7 @@ type
fBeforeRequestProc: TBeforeRequestProc;
fRequestCompletedProc: TRequestCompletedProc;
fResponseCompletedProc: TResponseCompletedProc;
fSessionID: string;
procedure DoValidateServerCertificate(const aSender: TObject; const aRequest: TURLRequest;
const aCertificate: TCertificate; var aAccepted: Boolean);
procedure DoBeforeRequest(aRequest: IHTTPRequest);
@ -123,6 +124,8 @@ type
procedure ExecuteAsyncRequest(const aMethod: TMVCHTTPMethodType);
function InternalExecuteRequest(const aMethod: TMVCHTTPMethodType): IMVCRESTResponse;
function ExecuteRequest(const aMethod: TMVCHTTPMethodType): IMVCRESTResponse;
procedure HandleCookies(aCookies: TCookies);
public
constructor Create;
destructor Destroy; override;
@ -320,8 +323,8 @@ type
/// <param name="aOwnsStream">
/// If OwnsStream is true, Stream will be destroyed by IMVCRESTClient.
/// </param>
function AddBody(aBodyStream: TStream; const aOwnsStream: Boolean = True;
const aContentType: string = ''): IMVCRESTClient; overload;
function AddBody(aBodyStream: TStream; const aOwnsStream: Boolean = True; const aContentType: string = '')
: IMVCRESTClient; overload;
/// <summary>
/// Add a body to the requisition
/// </summary>
@ -351,8 +354,8 @@ type
function AddBodyFieldFormData(const aName, aValue: string): IMVCRESTClient; overload;
{$IF defined(RIOORBETTER)}
function AddBodyFieldFormData(const aName: string; aStreamValue: TStream;
const aContentType: string = ''): IMVCRESTClient; overload;
function AddBodyFieldFormData(const aName: string; aStreamValue: TStream; const aContentType: string = '')
: IMVCRESTClient; overload;
{$ENDIF}
/// <summary>
/// Add a field to the x-www-form-urlencoded body. You must set ContentType to application/x-www-form-urlencoded
@ -414,8 +417,8 @@ type
/// <summary>
/// Execute a Patch request.
/// </summary>
function Patch(const aResource: string; aBody: TObject;
const aOwnsBody: Boolean = True): IMVCRESTResponse; overload;
function Patch(const aResource: string; aBody: TObject; const aOwnsBody: Boolean = True): IMVCRESTResponse;
overload;
function Patch(const aResource: string; const aBody: string = '';
const aContentType: string = TMVCMediaType.APPLICATION_JSON): IMVCRESTResponse; overload;
function Patch: IMVCRESTResponse; overload;
@ -466,10 +469,11 @@ type
/// </summary>
TMVCRESTResponse = class(TInterfacedObject, IMVCRESTResponse)
private
fHTTPResponse: IHTTPResponse;
fSuccess: Boolean;
fStatusCode: Integer;
fStatusText: string;
fHeaders: TStrings;
fHeaders: TMVCHeaders;
fCookies: TCookies;
fServer: string;
fContentType: string;
@ -507,7 +511,7 @@ uses
System.NetConsts,
System.NetEncoding,
MVCFramework.Serializer.JsonDataObjects,
System.RegularExpressions;
System.RegularExpressions, IdURI;
{$IF not defined(RIOORBETTER)}
@ -517,8 +521,7 @@ type
function CookieList: TCookies;
end;
{$ENDIF}
{ TMVCRESTClient }
{ TMVCRESTClient }
function TMVCRESTClient.Accept: string;
begin
@ -580,8 +583,8 @@ begin
end;
end;
function TMVCRESTClient.AddBody(aBodyStream: TStream; const aOwnsStream: Boolean;
const aContentType: string): IMVCRESTClient;
function TMVCRESTClient.AddBody(aBodyStream: TStream; const aOwnsStream: Boolean; const aContentType: string)
: IMVCRESTClient;
begin
Result := Self;
@ -620,8 +623,8 @@ end;
{$IF defined(RIOORBETTER)}
function TMVCRESTClient.AddBodyFieldFormData(const aName: string; aStreamValue: TStream;
const aContentType: string): IMVCRESTClient;
function TMVCRESTClient.AddBodyFieldFormData(const aName: string; aStreamValue: TStream; const aContentType: string)
: IMVCRESTClient;
begin
Result := Self;
GetBodyFormData.AddStream(aName, aStreamValue, aContentType);
@ -629,7 +632,6 @@ begin
end;
{$ENDIF}
function TMVCRESTClient.AddBodyFieldURLEncoded(const aName, aValue: string): IMVCRESTClient;
begin
Result := Self;
@ -986,7 +988,6 @@ type
THackURLClient = class(TURLClient);
{$ENDIF}
procedure TMVCRESTClient.DoApplyHeaders;
var
lParam: TMVCRESTParam;
@ -1008,7 +1009,7 @@ begin
fHTTPClient.CustomHeaders[TMVCRESTClientConsts.AUTHORIZATION_HEADER] := fAuthorization;
end;
fHTTPClient.Accept := fAccept;
fHTTPClient.AcceptCharSet := fAcceptCharset;
fHTTPClient.AcceptCharset := fAcceptCharset;
fHTTPClient.AcceptEncoding := fAcceptEncoding;
fHTTPClient.ContentType := fContentType;
end;
@ -1168,8 +1169,7 @@ begin
procedure
begin
fAsyncCompletionHandler(lResponse);
end
);
end);
end
else
begin
@ -1190,8 +1190,7 @@ begin
procedure
begin
fAsyncCompletionHandlerWithError(E);
end
);
end);
end
else
begin
@ -1201,8 +1200,7 @@ begin
end;
end;
ClearAllParams;
end
);
end);
lThread.Start;
end;
@ -1243,8 +1241,8 @@ begin
lResource := fResource;
if not lResource.IsEmpty then
begin
if not Result.EndsWith('/') and
not (lResource.StartsWith('/') or lResource.StartsWith('?') or lResource.StartsWith('#')) then
if not Result.EndsWith('/') and not(lResource.StartsWith('/') or lResource.StartsWith('?') or
lResource.StartsWith('#')) then
begin
Result := Result + '/';
end;
@ -1265,6 +1263,24 @@ begin
fHTTPClient.HandleRedirects := aHandleRedirects;
end;
procedure TMVCRESTClient.HandleCookies(aCookies: TCookies);
var
lCookie: TCookie;
lValue: String;
begin
for lCookie in aCookies do
begin
if SameText(lCookie.Name, 'dtsessionid') then
begin
lValue := TIdURI.URLDecode(lCookie.Value.Trim);
if lValue.Contains('invalid') then
SessionId('')
else
SessionId(lValue);
end;
end;
end;
function TMVCRESTClient.HandleRedirects: Boolean;
begin
Result := fHTTPClient.HandleRedirects;
@ -1331,6 +1347,11 @@ begin
fHTTPClient.ProxySettings := fProxySettings;
lURL := GetFullURL;
if not fSessionID.IsEmpty then
begin
Self.AddCookie(TMVCConstants.SESSION_TOKEN_NAME, SessionId);
end;
DoConvertMVCPathParamsToRESTParams(lURL);
DoApplyPathParams(lURL);
DoApplyQueryParams(lURL);
@ -1348,7 +1369,7 @@ begin
DoBeforeRequest(lRequest);
lResponse := fHTTPClient.Execute(lRequest, nil, []);
HandleCookies(lResponse.Cookies);
lHandled := False;
DoRequestCompleted(lResponse, lHandled);
@ -1407,7 +1428,7 @@ end;
function TMVCRESTClient.Patch(const aResource, aBody: string; const aContentType: string): IMVCRESTResponse;
begin
Resource(aResource);
if not aBody.isEmpty then
if not aBody.IsEmpty then
begin
ClearBody;
AddBody(aBody, aContentType);
@ -1541,8 +1562,8 @@ begin
fHTTPClient.ResponseTimeout := aReadTimeout;
end;
function TMVCRESTClient.RegisterTypeSerializer(const aTypeInfo: PTypeInfo;
aInstance: IMVCTypeSerializer): IMVCRESTClient;
function TMVCRESTClient.RegisterTypeSerializer(const aTypeInfo: PTypeInfo; aInstance: IMVCTypeSerializer)
: IMVCRESTClient;
begin
Result := Self;
fSerializer.RegisterTypeSerializer(aTypeInfo, aInstance);
@ -1561,7 +1582,6 @@ end;
{$IF defined(TOKYOORBETTER)}
function TMVCRESTClient.SecureProtocols: THTTPSecureProtocols;
begin
Result := fHTTPClient.SecureProtocols;
@ -1574,7 +1594,6 @@ begin
end;
{$ENDIF}
function TMVCRESTClient.SerializeObject(aObject: TObject): string;
begin
if ObjectIsList(aObject) then
@ -1584,45 +1603,46 @@ begin
end;
function TMVCRESTClient.SessionId: string;
var
lCookie: TCookie;
lParam: TMVCRESTParam;
// var
// lCookie: TCookie;
// lParam: TMVCRESTParam;
begin
Result := '';
for lParam in fParameters do
begin
if lParam.&Type = TMVCRESTParamType.Cookie then
begin
if SameText(lParam.Name, TMVCConstants.SESSION_TOKEN_NAME) then
begin
Result := lParam.Value;
Break;
end;
end;
end;
if Result.IsEmpty then
begin
for lCookie in fHTTPClient.CookieManager.Cookies do
begin
if SameText(lCookie.Name, TMVCConstants.SESSION_TOKEN_NAME) then
begin
Result := lCookie.Value;
Break;
end;
end;
Result := lCookie.Value;
end;
if Result.Contains('invalid') then
Result := '';
Result := fSessionID;
// Result := '';
//
// for lParam in fParameters do
// begin
// if lParam.&Type = TMVCRESTParamType.Cookie then
// begin
// if SameText(lParam.Name, TMVCConstants.SESSION_TOKEN_NAME) then
// begin
// Result := lParam.Value;
// Break;
// end;
// end;
// end;
//
// if Result.IsEmpty then
// begin
// for lCookie in fHTTPClient.CookieManager.Cookies do
// begin
// if SameText(lCookie.Name, TMVCConstants.SESSION_TOKEN_NAME) then
// begin
// Result := lCookie.Value;
// Break;
// end;
// end;
// Result := lCookie.Value;
// end;
//
// if Result.Contains('invalid') then
// Result := '';
end;
function TMVCRESTClient.SessionId(const aSessionId: string): IMVCRESTClient;
begin
Result := Self;
fSessionID := aSessionId;
AddCookie(TMVCConstants.SESSION_TOKEN_NAME, aSessionId);
end;
@ -1685,8 +1705,8 @@ begin
fResponseCompletedProc := aResponseCompletedProc;
end;
function TMVCRESTClient.SetValidateServerCertificateProc(
aValidateCertificateProc: TValidateServerCertificateProc): IMVCRESTClient;
function TMVCRESTClient.SetValidateServerCertificateProc(aValidateCertificateProc: TValidateServerCertificateProc)
: IMVCRESTClient;
begin
Result := Self;
fValidateServerCertificate := aValidateCertificateProc;
@ -1707,6 +1727,10 @@ end;
function TMVCRESTResponse.Content: string;
begin
if fContent.IsEmpty then
begin
fContent := TMVCRESTClientHelper.GetResponseContentAsString(ContentRawBytes, ContentType);
end;
Result := fContent;
end;
@ -1744,8 +1768,10 @@ end;
constructor TMVCRESTResponse.Create(aHTTPResponse: IHTTPResponse);
begin
fHeaders := TStringList.Create;
SetLength(fContentRawBytes, 0);
inherited Create;
fHTTPResponse := aHTTPResponse;
fHeaders := TMVCHeaders.Create;
// SetLength(fContentRawBytes, 0);
fCookies := TCookies.Create;
FillResponse(aHTTPResponse);
@ -1753,7 +1779,7 @@ end;
destructor TMVCRESTResponse.Destroy;
begin
SetLength(fContentRawBytes, 0);
// SetLength(fContentRawBytes, 0);
FreeAndNil(fHeaders);
FreeAndNil(fCookies);
inherited Destroy;
@ -1773,10 +1799,11 @@ begin
end;
fCookies.AddRange(aHTTPResponse.Cookies.ToArray);
fServer := aHTTPResponse.HeaderValue[TMVCRESTClientConsts.SERVER_HEADER];
fContentRawBytes := TMVCRESTClientHelper.GetResponseContentAsRawBytes(aHTTPResponse.ContentStream,
aHTTPResponse.ContentEncoding);
fContent := TMVCRESTClientHelper.GetResponseContentAsString(fContentRawBytes,
aHTTPResponse.HeaderValue[sContentType]);
fContentRawBytes := [];
// TMVCRESTClientHelper.GetResponseContentAsRawBytes(aHTTPResponse.ContentStream,
// aHTTPResponse.ContentEncoding);
// fContent := TMVCRESTClientHelper.GetResponseContentAsString(fContentRawBytes,
// aHTTPResponse.HeaderValue[sContentType]);
fContentType := aHTTPResponse.HeaderValue[sContentType];
fContentEncoding := aHTTPResponse.ContentEncoding;
fContentLength := aHTTPResponse.ContentLength;
@ -1794,6 +1821,11 @@ end;
function TMVCRESTResponse.ContentRawBytes: TBytes;
begin
if Length(fContentRawBytes) = 0 then
begin
fContentRawBytes := TMVCRESTClientHelper.GetResponseContentAsRawBytes(fHTTPResponse.ContentStream,
fHTTPResponse.ContentEncoding);
end;
Result := fContentRawBytes;
end;
@ -1803,7 +1835,7 @@ var
begin
lStream := TMemoryStream.Create;
try
lStream.Write(fContentRawBytes, Length(fContentRawBytes));
lStream.Write(ContentRawBytes, Length(ContentRawBytes));
lStream.Position := 0;
lStream.SaveToFile(aFileName);
finally
@ -1816,7 +1848,7 @@ begin
if aStream = nil then
raise EMVCRESTClientException.Create('Stream not assigned!');
aStream.Write(fContentRawBytes, Length(fContentRawBytes));
aStream.Write(ContentRawBytes, Length(ContentRawBytes));
end;
function TMVCRESTResponse.Server: string;
@ -1840,7 +1872,6 @@ begin
end;
{$IF not defined(RIOORBETTER)}
{ TCookieManagerHelper }
function TCookieManagerHelper.CookieList: TCookies;

View File

@ -282,8 +282,7 @@ type
property Format: string read FFormat write FFormat;
constructor Create(name: string; Location: TSwagRequestParameterInLocation; AType: TSwagTypeParameter;
APattern: string = '';
AFormat: string = ''); overload;
APattern: string = ''; AFormat: string = ''); overload;
constructor Create(name: string; Location: TSwagRequestParameterInLocation; AType: TClass; APattern: string = '';
AFormat: string = ''); overload;
end;
@ -565,8 +564,7 @@ type
const ASerializationType: TMVCDatasetSerializationType;
const ASerializationAction: TMVCDatasetSerializationAction = nil); overload;
procedure Render(const ADataSet: TDataSet; const AOwns: Boolean; const AIgnoredFields: TMVCIgnoredList;
const ANameCase: TMVCNameCase;
const ASerializationType: TMVCDatasetSerializationType;
const ANameCase: TMVCNameCase; const ASerializationType: TMVCDatasetSerializationType;
const ASerializationAction: TMVCDatasetSerializationAction = nil); overload;
procedure Render(const ATextWriter: TTextWriter; const AOwns: Boolean = True); overload;
procedure Render(const AStream: TStream; const AOwns: Boolean = True); overload;
@ -597,11 +595,9 @@ type
procedure OnRequest(const AContext: TWebContext; const AControllerQualifiedClassName, AActionName: string;
var AAuthenticationRequired: Boolean);
procedure OnAuthentication(const AContext: TWebContext; const AUserName, APassword: string;
AUserRoles: TList<string>;
var AIsValid: Boolean; const ASessionData: TDictionary<string, string>);
AUserRoles: TList<string>; var AIsValid: Boolean; const ASessionData: TDictionary<string, string>);
procedure OnAuthorization(const AContext: TWebContext; AUserRoles: TList<string>;
const AControllerQualifiedClassName: string;
const AActionName: string; var AIsAuthorized: Boolean);
const AControllerQualifiedClassName: string; const AActionName: string; var AIsAuthorized: Boolean);
end;
TMVCRenderer = class(TMVCBase)
@ -669,8 +665,7 @@ type
procedure Render<T: class>(const AStatusCode: Integer; const ACollection: TObjectList<T>; const AOwns: Boolean;
const ASerializationAction: TMVCSerializationAction<T> = nil); overload;
procedure Render<T: class>(const ACollection: TObjectList<T>; const AOwns: Boolean;
const AType: TMVCSerializationType;
const ASerializationAction: TMVCSerializationAction<T> = nil); overload;
const AType: TMVCSerializationType; const ASerializationAction: TMVCSerializationAction<T> = nil); overload;
procedure Render(const ACollection: IMVCList); overload;
procedure Render(const ACollection: IMVCList; const AType: TMVCSerializationType); overload;
procedure Render(const ATextWriter: TTextWriter; const AOwns: Boolean = True); overload;
@ -692,8 +687,7 @@ type
const ASerializationType: TMVCDatasetSerializationType;
const ASerializationAction: TMVCDatasetSerializationAction = nil); overload;
procedure Render(const ADataSet: TDataSet; const AOwns: Boolean; const AIgnoredFields: TMVCIgnoredList;
const ANameCase: TMVCNameCase;
const ASerializationType: TMVCDatasetSerializationType;
const ANameCase: TMVCNameCase; const ASerializationType: TMVCDatasetSerializationType;
const ASerializationAction: TMVCDatasetSerializationAction = nil); overload;
// SSE Support
procedure RenderSSE(const EventID: string; const EventData: string; EventName: string = '';
@ -813,8 +807,7 @@ type
/// <param name="AActionName">Method name of the matching controller method.</param>
/// <param name="AHandled">If set to True the Request would finished. Response must be set by the implementor. Default value is False.</param>
procedure OnBeforeControllerAction(AContext: TWebContext; const AControllerQualifiedClassName: string;
const AActionName: string;
var AHandled: Boolean);
const AActionName: string; var AHandled: Boolean);
/// <summary>
/// Procedure is called after the specific controller method was called.
/// It is still possible to cancel or to completly modifiy the request.
@ -833,12 +826,10 @@ type
end;
TMVCExceptionHandlerProc = reference to procedure(E: Exception; SelectedController: TMVCController;
WebContext: TWebContext;
var ExceptionHandled: Boolean);
WebContext: TWebContext; var ExceptionHandled: Boolean);
TMVCRouterLogState = (rlsRouteFound, rlsRouteNotFound);
TMVCRouterLogHandlerProc = reference to procedure(const Router: TMVCCustomRouter;
const RouterLogState: TMVCRouterLogState;
const WebContext: TWebContext);
const RouterLogState: TMVCRouterLogState; const WebContext: TWebContext);
TMVCEngine = class(TComponent)
private const
@ -872,8 +863,7 @@ type
procedure FixUpWebModule;
procedure ExecuteBeforeRoutingMiddleware(const AContext: TWebContext; var AHandled: Boolean);
procedure ExecuteBeforeControllerActionMiddleware(const AContext: TWebContext;
const AControllerQualifiedClassName: string;
const AActionName: string; var AHandled: Boolean);
const AControllerQualifiedClassName: string; const AActionName: string; var AHandled: Boolean);
procedure ExecuteAfterControllerActionMiddleware(const AContext: TWebContext; const AActionName: string;
const AHandled: Boolean);
procedure ExecuteAfterRoutingMiddleware(const AContext: TWebContext; const AHandled: Boolean);
@ -893,8 +883,7 @@ type
class procedure ClearSessionCookiesAlreadySet(const ACookies: TCookieCollection); static;
public
constructor Create(const AWebModule: TWebModule; const AConfigAction: TProc<TMVCConfig> = nil;
const ACustomLogger: ILogWriter = nil);
reintroduce;
const ACustomLogger: ILogWriter = nil); reintroduce;
destructor Destroy; override;
function GetSessionBySessionId(const ASessionId: string): TWebSession;
@ -1323,7 +1312,6 @@ constructor TMVCWebRequest.Create(const AWebRequest: TWebRequest;
begin
inherited Create;
FBody := EmptyStr;
// FContentType := TMVCConstants.DEFAULT_CONTENT_TYPE;
FCharset := TMVCConstants.DEFAULT_CONTENT_CHARSET;
FWebRequest := AWebRequest;
FSerializers := ASerializers;
@ -1334,6 +1322,10 @@ end;
procedure TMVCWebRequest.DefineContentType;
begin
SplitContentMediaTypeAndCharset(FWebRequest.GetFieldByName('Content-Type'), FContentMediaType, FCharset);
if FContentMediaType.IsEmpty then
begin
FContentMediaType := TMVCConstants.DEFAULT_CONTENT_TYPE;
end;
FContentType := BuildContentType(FContentMediaType, FCharset);
end;
@ -1732,8 +1724,7 @@ begin
else
LRoles := '';
AWebSession[TMVCConstants.CURRENT_USER_SESSION_KEY] := FUserName + '$$' + DateTimeToISOTimeStamp(FLoggedSince) + '$$'
+ FRealm +
'$$' + LRoles;
+ FRealm + '$$' + LRoles;
end;
procedure TUser.SetCustomData(const Value: TMVCCustomData);
@ -2007,8 +1998,7 @@ begin
end;
function TMVCEngine.AddController(const AControllerClazz: TMVCControllerClazz;
const ACreateAction: TMVCControllerCreateAction;
const AURLSegment: string): TMVCEngine;
const ACreateAction: TMVCControllerCreateAction; const AURLSegment: string): TMVCEngine;
begin
FControllers.Add(TMVCControllerDelegate.Create(AControllerClazz, ACreateAction, AURLSegment));
Result := Self;
@ -2075,15 +2065,14 @@ begin
rlsRouteFound:
begin
Log(TLogLevel.levNormal, Context.Request.HTTPMethodAsString + ':' + Context.Request.PathInfo + ' [' +
Context.Request.ClientIp +
'] -> ' + Sender.GetQualifiedActionName + ' - ' + IntToStr(Context.Response.StatusCode) + ' ' +
Context.Response.ReasonString);
Context.Request.ClientIp + '] -> ' + Sender.GetQualifiedActionName + ' - ' +
IntToStr(Context.Response.StatusCode) + ' ' + Context.Response.ReasonString);
end;
rlsRouteNotFound:
begin
Log(TLogLevel.levNormal, Context.Request.HTTPMethodAsString + ':' + Context.Request.PathInfo + ' [' +
Context.Request.ClientIp +
'] -> {NOT FOUND} - ' + IntToStr(Context.Response.StatusCode) + ' ' + Context.Response.ReasonString);
Context.Request.ClientIp + '] -> {NOT FOUND} - ' + IntToStr(Context.Response.StatusCode) + ' ' +
Context.Response.ReasonString);
end;
else
raise EMVCException.Create('Invalid RouterLogState');
@ -2169,8 +2158,7 @@ begin
if ARequest.ContentLength > FConfigCache_MaxRequestSize then
begin
raise EMVCException.CreateFmt(http_status.RequestEntityTooLarge,
'Request size exceeded the max allowed size [%d KiB] (1)',
[(FConfigCache_MaxRequestSize div 1024)]);
'Request size exceeded the max allowed size [%d KiB] (1)', [(FConfigCache_MaxRequestSize div 1024)]);
end;
{$IFDEF BERLINORBETTER}
@ -2180,8 +2168,7 @@ begin
if ARequest.ContentLength > FConfigCache_MaxRequestSize then
begin
raise EMVCException.CreateFmt(http_status.RequestEntityTooLarge,
'Request size exceeded the max allowed size [%d KiB] (2)',
[(FConfigCache_MaxRequestSize div 1024)]);
'Request size exceeded the max allowed size [%d KiB] (2)', [(FConfigCache_MaxRequestSize div 1024)]);
end;
{$ENDIF}
lParamsTable := TMVCRequestParamsTable.Create;
@ -2199,8 +2186,8 @@ begin
if not lHandled then
begin
if lRouter.ExecuteRouting(ARequest.PathInfo,
lContext.Request.GetOverwrittenHTTPMethod { lContext.Request.HTTPMethod } ,
ARequest.ContentType, ARequest.Accept, FControllers, FConfig[TMVCConfigKey.DefaultContentType],
lContext.Request.GetOverwrittenHTTPMethod { lContext.Request.HTTPMethod } , ARequest.ContentType,
ARequest.Accept, FControllers, FConfig[TMVCConfigKey.DefaultContentType],
FConfig[TMVCConfigKey.DefaultContentCharset], lParamsTable, lResponseContentMediaType,
lResponseContentCharset) then
begin
@ -2222,8 +2209,7 @@ begin
lSelectedController.ApplicationSession := FApplicationSession;
lContext.ParamsTable := lParamsTable;
ExecuteBeforeControllerActionMiddleware(lContext, lRouter.ControllerClazz.QualifiedClassName,
lRouter.MethodToCall.name,
lHandled);
lRouter.MethodToCall.name, lHandled);
if lHandled then
Exit(True);
@ -2323,8 +2309,7 @@ begin
else
begin
SendRawHTTPStatus(lContext, http_status.InternalServerError,
Format('[%s] %s', [EIO.Classname, EIO.Message]),
EIO.Classname);
Format('[%s] %s', [EIO.Classname, EIO.Message]), EIO.Classname);
end;
end;
end;
@ -2333,8 +2318,7 @@ begin
if not CustomExceptionHandling(Ex, lSelectedController, lContext) then
begin
Log.ErrorFmt('[%s] %s (Custom message: "%s")',
[Ex.Classname, Ex.Message, 'Global Action Exception Handler'],
LOGGERPRO_TAG);
[Ex.Classname, Ex.Message, 'Global Action Exception Handler'], LOGGERPRO_TAG);
if Assigned(lSelectedController) then
begin
lSelectedController.ResponseStatus(http_status.InternalServerError);
@ -2356,8 +2340,7 @@ begin
if not CustomExceptionHandling(Ex, lSelectedController, lContext) then
begin
Log.ErrorFmt('[%s] %s (Custom message: "%s")',
[Ex.Classname, Ex.Message, 'After Routing Exception Handler'],
LOGGERPRO_TAG);
[Ex.Classname, Ex.Message, 'After Routing Exception Handler'], LOGGERPRO_TAG);
if Assigned(lSelectedController) then
begin
{ middlewares *must* not raise unhandled exceptions }
@ -2408,8 +2391,7 @@ begin
end;
procedure TMVCEngine.ExecuteBeforeControllerActionMiddleware(const AContext: TWebContext;
const AControllerQualifiedClassName: string;
const AActionName: string; var AHandled: Boolean);
const AControllerQualifiedClassName: string; const AActionName: string; var AHandled: Boolean);
var
Middleware: IMVCMiddleware;
begin
@ -2451,8 +2433,7 @@ begin
end;
procedure TMVCEngine.FillActualParamsForAction(const AContext: TWebContext;
const AActionFormalParams: TArray<TRttiParameter>;
const AActionName: string; var AActualParams: TArray<TValue>);
const AActionFormalParams: TArray<TRttiParameter>; const AActionName: string; var AActualParams: TArray<TValue>);
var
lParamName: string;
I: Integer;
@ -2484,8 +2465,8 @@ begin
on E: Exception do
begin
raise EMVCException.CreateFmt(http_status.BadRequest,
'Invalid Integer value for param [%s] - [CLASS: %s][MSG: %s]',
[AActionFormalParams[I].name, E.Classname, E.Message]);
'Invalid Integer value for param [%s] - [CLASS: %s][MSG: %s]', [AActionFormalParams[I].name, E.Classname,
E.Message]);
end;
end;
tkInt64:
@ -2495,8 +2476,8 @@ begin
on E: Exception do
begin
raise EMVCException.CreateFmt(http_status.BadRequest,
'Invalid Int64 value for param [%s] - [CLASS: %s][MSG: %s]',
[AActionFormalParams[I].name, E.Classname, E.Message]);
'Invalid Int64 value for param [%s] - [CLASS: %s][MSG: %s]', [AActionFormalParams[I].name, E.Classname,
E.Message]);
end;
end;
tkUString:
@ -2595,8 +2576,7 @@ begin
end
else
raise EMVCException.CreateFmt('Invalid type for parameter %s. Allowed types are ' +
ALLOWED_TYPED_ACTION_PARAMETERS_TYPES,
[lParamName]);
ALLOWED_TYPED_ACTION_PARAMETERS_TYPES, [lParamName]);
end
else
begin
@ -2685,8 +2665,7 @@ begin
end;
procedure TMVCEngine.SendRawHTTPStatus(const AContext: TWebContext; const HTTPStatusCode: Integer;
const AReasonString: string;
const AClassName: string);
const AReasonString: string; const AClassName: string);
var
lSer: IMVCSerializer;
lError: TMVCErrorResponse;
@ -2707,7 +2686,8 @@ begin
AContext.Response.SetContentType(BuildContentType(TMVCMediaType.TEXT_HTML,
AContext.Config[TMVCConfigKey.DefaultContentCharset]));
end
else if AContext.Request.ClientPrefer(AContext.Config[TMVCConfigKey.DefaultContentType]) and Serializers.TryGetValue(AContext.Config[TMVCConfigKey.DefaultContentType], lSer) then
else if AContext.Request.ClientPrefer(AContext.Config[TMVCConfigKey.DefaultContentType]) and
Serializers.TryGetValue(AContext.Config[TMVCConfigKey.DefaultContentType], lSer) then
begin
AContext.Response.SetContent(lSer.SerializeObject(lError));
AContext.Response.SetContentType(BuildContentType(AContext.Config[TMVCConfigKey.DefaultContentType],
@ -2717,8 +2697,8 @@ begin
begin
AContext.Response.SetContentType(BuildContentType(TMVCMediaType.TEXT_PLAIN,
AContext.Config[TMVCConfigKey.DefaultContentCharset]));
AContext.Response.SetContent(FConfigCache_ServerSignature + sLineBreak + 'HTTP ' + HTTPStatusCode.ToString + ': ' +
AReasonString);
AContext.Response.SetContent(FConfigCache_ServerSignature + sLineBreak + 'HTTP ' + HTTPStatusCode.ToString + ': '
+ AReasonString);
end;
AContext.Response.SetStatusCode(HTTPStatusCode);
AContext.Response.SetReasonString(AReasonString);
@ -2822,9 +2802,8 @@ begin
begin
AResponse.ContentType := TMVCMediaType.TEXT_PLAIN;
AResponse.Content := Config[TMVCConfigKey.ServerName] + ' ERROR:' + sLineBreak + 'Exception raised of class: ' +
AException.Classname +
sLineBreak + '***********************************************' + sLineBreak + AException.Message + sLineBreak +
'***********************************************';
AException.Classname + sLineBreak + '***********************************************' + sLineBreak +
AException.Message + sLineBreak + '***********************************************';
end;
end;
@ -2936,8 +2915,7 @@ end;
{ TMVCControllerDelegate }
constructor TMVCControllerDelegate.Create(const AClazz: TMVCControllerClazz;
const ACreateAction: TMVCControllerCreateAction;
const AURLSegment: string = '');
const ACreateAction: TMVCControllerCreateAction; const AURLSegment: string = '');
begin
inherited Create;
FClazz := AClazz;
@ -2952,8 +2930,8 @@ begin
Result := TPath.GetExtension(AStaticFileName).ToLower = '.' + AConfig[TMVCConfigKey.DefaultViewFileExtension].ToLower;
end;
class function TMVCStaticContents.IsStaticFile(const AViewPath, AWebRequestPath: string;
out ARealFileName: string; out AIsDirectoryTraversalAttack: Boolean): Boolean;
class function TMVCStaticContents.IsStaticFile(const AViewPath, AWebRequestPath: string; out ARealFileName: string;
out AIsDirectoryTraversalAttack: Boolean): Boolean;
var
lFileName: string;
lWebRoot: string;
@ -3443,8 +3421,7 @@ begin
end;
procedure TMVCRenderer.Render<T>(const ACollection: TObjectList<T>; const AOwns: Boolean;
const AType: TMVCSerializationType;
const ASerializationAction: TMVCSerializationAction<T>);
const AType: TMVCSerializationType; const ASerializationAction: TMVCSerializationAction<T>);
var
lSerializationAction: TMVCSerializationAction;
begin
@ -3589,35 +3566,35 @@ begin
if (GetContext.Response.StatusCode = http_status.OK) then
ResponseStatus(http_status.InternalServerError, AException.Message + ' [' + AException.Classname + ']');
// if (not GetContext.Request.IsAjax) and (GetContext.Request.ClientPrefer(TMVCMediaType.TEXT_HTML)) then
// begin
// SetContentType(TMVCMediaType.TEXT_HTML);
// Render(AException, False);
// exit;
// ResponseStream.Clear;
// ResponseStream.Append
// ('<html><head><style>pre { padding: 15px; color: #000000; background-color: #e0e0e0; }</style></head><body>')
// .Append('<h1>' + Config[TMVCConfigKey.ServerName] + ': Error Raised</h1>')
// .AppendFormat('<pre>HTTP Return Code: %d' + sLineBreak,
// [GetContext.Response.StatusCode]).AppendFormat('HTTP Reason Text: "%s"</pre>',
// [GetContext.Response.ReasonString])
// .Append('<h3><pre>').AppendFormat('Exception Class Name : %s' + sLineBreak, [AException.Classname])
// .AppendFormat('Exception Message : %s' + sLineBreak, [AException.Message]).Append('</pre></h3>');
// if Assigned(AExceptionItems) and (AExceptionItems.Count > 0) then
// begin
// ResponseStream.Append('<h2><pre>');
// for S in AExceptionItems do
// ResponseStream.AppendLine('- ' + S);
// ResponseStream.Append('</pre><h2>');
// end
// else
// begin
// ResponseStream.AppendLine('<pre>No other information available</pre>');
// end;
// ResponseStream.Append('</body></html>');
// RenderResponseStream;
// end
// else
// if (not GetContext.Request.IsAjax) and (GetContext.Request.ClientPrefer(TMVCMediaType.TEXT_HTML)) then
// begin
// SetContentType(TMVCMediaType.TEXT_HTML);
// Render(AException, False);
// exit;
// ResponseStream.Clear;
// ResponseStream.Append
// ('<html><head><style>pre { padding: 15px; color: #000000; background-color: #e0e0e0; }</style></head><body>')
// .Append('<h1>' + Config[TMVCConfigKey.ServerName] + ': Error Raised</h1>')
// .AppendFormat('<pre>HTTP Return Code: %d' + sLineBreak,
// [GetContext.Response.StatusCode]).AppendFormat('HTTP Reason Text: "%s"</pre>',
// [GetContext.Response.ReasonString])
// .Append('<h3><pre>').AppendFormat('Exception Class Name : %s' + sLineBreak, [AException.Classname])
// .AppendFormat('Exception Message : %s' + sLineBreak, [AException.Message]).Append('</pre></h3>');
// if Assigned(AExceptionItems) and (AExceptionItems.Count > 0) then
// begin
// ResponseStream.Append('<h2><pre>');
// for S in AExceptionItems do
// ResponseStream.AppendLine('- ' + S);
// ResponseStream.Append('</pre><h2>');
// end
// else
// begin
// ResponseStream.AppendLine('<pre>No other information available</pre>');
// end;
// ResponseStream.Append('</body></html>');
// RenderResponseStream;
// end
// else
begin
R := TMVCErrorResponse.Create;
try
@ -3701,8 +3678,7 @@ begin
end;
procedure TMVCRenderer.Render(const ADataSet: TDataSet; const AOwns: Boolean;
const ASerializationType: TMVCDatasetSerializationType;
const ASerializationAction: TMVCDatasetSerializationAction);
const ASerializationType: TMVCDatasetSerializationType; const ASerializationAction: TMVCDatasetSerializationAction);
begin
Render(ADataSet, AOwns, [], ASerializationType, ASerializationAction);
end;
@ -3740,8 +3716,8 @@ end;
{ TMVCBaseView }
constructor TMVCBaseViewEngine.Create(const AEngine: TMVCEngine; const AWebContext: TWebContext;
const AViewModel: TMVCViewDataObject;
const AViewDataSets: TObjectDictionary<string, TDataSet>; const AContentType: string);
const AViewModel: TMVCViewDataObject; const AViewDataSets: TObjectDictionary<string, TDataSet>;
const AContentType: string);
begin
inherited Create;
Engine := AEngine;
@ -3781,8 +3757,7 @@ begin
else
begin
lFileName := ExpandFileName(IncludeTrailingPathDelimiter(GetApplicationFileNamePath +
Config.Value[TMVCConfigKey.ViewPath]) +
lFileName);
Config.Value[TMVCConfigKey.ViewPath]) + lFileName);
end;
if FileExists(lFileName) then

View File

@ -137,10 +137,10 @@ type
[Test]
procedure TestBasicAuth01;
[Test]
//[Category('this')]
// [Category('this')]
procedure TestEntityWithArrays;
[Test]
//[Category('this')]
// [Category('this')]
procedure TestEntityWithEmptyArrays;
[Test]
procedure TestBasicAuth02;
@ -352,7 +352,7 @@ var
res: IMVCRESTResponse;
begin
res := RESTClient.Get('/logout');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Logout Failed');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Logout Failed: ' + res.Content);
end;
procedure TBaseServerTest.Setup;
@ -397,10 +397,7 @@ begin
procedure(E: Exception)
begin
OK := false;
end)
.AddPathParam('par1', 1)
.AddPathParam('par2', 2)
.AddPathParam('par3', 3)
end).AddPathParam('par1', 1).AddPathParam('par2', 2).AddPathParam('par3', 3)
.Delete('/req/with/params/($par1)/($par2)/($par3)');
// wait for thred finish
@ -435,10 +432,7 @@ begin
end,
procedure(E: Exception)
begin
end)
.AddPathParam('par1', 1)
.AddPathParam('par2', 2)
.AddPathParam('par3', 3)
end).AddPathParam('par1', 1).AddPathParam('par2', 2).AddPathParam('par3', 3)
.Get('/req/with/params/($par1)/($par2)/($par3)');
// wait for thred finish
@ -446,7 +440,7 @@ begin
r := evt.WaitFor(2000);
until r = TWaitResult.wrSignaled;
Assert.isTrue(Assigned(j));
Assert.IsTrue(Assigned(j));
Assert.areEqual('1', j.Get('par1').JsonValue.Value);
j.Free;
finally
@ -476,11 +470,7 @@ begin
end,
procedure(E: Exception)
begin
end)
.AddPathParam('par1', 1)
.AddPathParam('par2', 2)
.AddPathParam('par3', 3)
.Post('/echo/($par1)/($par2)/($par3)',
end).AddPathParam('par1', 1).AddPathParam('par2', 2).AddPathParam('par3', 3).Post('/echo/($par1)/($par2)/($par3)',
TSystemJSON.JSONValueToString(System.JSON.TJSONObject.Create(TJSONPair.Create('from client', 'hello world'))));
// wait for thred finish
@ -488,7 +478,7 @@ begin
r := evt.WaitFor(2000);
until r = TWaitResult.wrSignaled;
Assert.isTrue(Assigned(j));
Assert.IsTrue(Assigned(j));
Assert.areEqual('from server', j.Get('echo').JsonValue.Value);
j.Free;
finally
@ -517,11 +507,7 @@ begin
end,
procedure(E: Exception)
begin
end)
.AddPathParam('par1', 1)
.AddPathParam('par2', 2)
.AddPathParam('par3', 3)
.Put('/echo/($par1)/($par2)/($par3)',
end).AddPathParam('par1', 1).AddPathParam('par2', 2).AddPathParam('par3', 3).Put('/echo/($par1)/($par2)/($par3)',
TSystemJSON.JSONValueToString(System.JSON.TJSONObject.Create(System.JSON.TJSONPair.Create('from client',
'hello world'))));
@ -530,7 +516,7 @@ begin
r := evt.WaitFor(2000);
until r = TWaitResult.wrSignaled;
Assert.isTrue(Assigned(j));
Assert.IsTrue(Assigned(j));
Assert.areEqual('from server', j.Get('echo').JsonValue.Value);
j.Free;
finally
@ -543,7 +529,7 @@ var
lRes: IMVCRESTResponse;
begin
RESTClient.SetBasicAuthorization('user1', 'user1');
Assert.AreEqual('Basic dXNlcjE6dXNlcjE=', RESTClient.Authorization);
Assert.areEqual('Basic dXNlcjE6dXNlcjE=', RESTClient.Authorization);
lRes := RESTClient.Get('/private/role1');
Assert.areEqual<Integer>(HTTP_STATUS.OK, lRes.StatusCode);
end;
@ -631,7 +617,7 @@ var
lRes: IMVCRESTResponse;
begin
lRes := RESTClient.Get('/privatecustom/role1');
Assert.AreEqual<Integer>(HTTP_STATUS.Unauthorized, lRes.StatusCode);
Assert.areEqual<Integer>(HTTP_STATUS.Unauthorized, lRes.StatusCode);
Assert.areEqual('/system/users/logged', lRes.HeaderValue('X-LOGIN-URL'));
Assert.areEqual('POST', lRes.HeaderValue('X-LOGIN-METHOD'));
@ -686,14 +672,14 @@ begin
lJSON.AddPair('password', 'user1');
lRes := RESTClient.Accept('text/html').Post('/system/users/logged', TSystemJSON.JSONValueToString(lJSON, false));
SplitContentMediaTypeAndCharset(lRes.ContentType, lContentType, lContentCharset);
Assert.AreEqual(lContentType, TMVCMediaType.APPLICATION_JSON);
Assert.areEqual(lContentType, TMVCMediaType.APPLICATION_JSON);
Assert.areEqual<Integer>(HTTP_STATUS.OK, lRes.StatusCode);
Assert.areEqual('/system/users/logged', lRes.HeaderValue('X-LOGOUT-URL'));
Assert.areEqual('DELETE', lRes.HeaderValue('X-LOGOUT-METHOD'));
Assert.areEqual('{"status":"OK"}', lRes.Content);
lCookieValue := lRes.CookieByName(TMVCConstants.SESSION_TOKEN_NAME).Value;
Assert.AreNotEqual('', lCookieValue, 'Session cookie not returned after login');
Assert.isFalse(lCookieValue.Contains('invalid'), 'Returned an invalid session token');
Assert.IsFalse(lCookieValue.Contains('invalid'), 'Returned an invalid session token');
lRes := RESTClient.Get('/privatecustom/role2');
Assert.areEqual<Integer>(HTTP_STATUS.Forbidden, lRes.StatusCode,
@ -714,7 +700,7 @@ begin
lJSON := System.JSON.TJSONObject.Create;
try
// no request body
lRes := RESTClient.AddBody('',TMVCMediaType.APPLICATION_JSON).Post('/system/users/logged');
lRes := RESTClient.AddBody('', TMVCMediaType.APPLICATION_JSON).Post('/system/users/logged');
Assert.areEqual<Integer>(HTTP_STATUS.BadRequest, lRes.StatusCode,
'Empty request body doesn''t return HTTP 400 Bad Request');
@ -788,7 +774,7 @@ var
lRes: IMVCRESTResponse;
lJSON: System.JSON.TJSONObject;
lLogoutUrl: string;
lPass: Boolean;
lPass: boolean;
lCookie: TCookie;
begin
lJSON := System.JSON.TJSONObject.Create;
@ -801,16 +787,16 @@ begin
lLogoutUrl := lRes.HeaderValue('X-LOGOUT-URL');
lRes := RESTClient.Delete(lLogoutUrl);
lPass := False;
lPass := false;
for lCookie in lRes.Cookies do
begin
if lCookie.Value.Contains('invalid') then
begin
lPass := True;
lPass := true;
Break;
end;
end;
Assert.isTrue(lPass, 'No session cookie cleanup in the response');
Assert.IsTrue(lPass, 'No session cookie cleanup in the response');
finally
lJSON.Free;
end;
@ -830,7 +816,7 @@ begin
Assert.areEqual<Integer>(500, lJSON.I['statuscode'], lJSON.ToJSON());
Assert.areEqual<string>('error', lJSON.S['reasonstring'], lJSON.ToJSON());
Assert.areEqual(0, lJSON.A['items'].Count, lJSON.ToJSON());
Assert.isTrue(lJSON.IsNull('data'), lJSON.ToJSON());
Assert.IsTrue(lJSON.IsNull('data'), lJSON.ToJSON());
finally
lJSON.Free;
end;
@ -851,7 +837,7 @@ begin
Assert.areEqual<Integer>(HTTP_STATUS.BadRequest, lJSON.I['statuscode'], lJSON.ToJSON());
Assert.areEqual<string>('error', lJSON.S['reasonstring'], lJSON.ToJSON());
Assert.areEqual(0, lJSON.A['items'].Count, lJSON.ToJSON());
Assert.isTrue(lJSON.IsNull('data'), lJSON.ToJSON());
Assert.IsTrue(lJSON.IsNull('data'), lJSON.ToJSON());
finally
lJSON.Free;
end;
@ -872,7 +858,7 @@ begin
Assert.areEqual('error', lJSON.S['reasonstring'], lJSON.ToJSON());
Assert.areEqual(999, lJSON.I['apperrorcode'], lJSON.ToJSON());
Assert.areEqual(0, lJSON.A['items'].Count, lJSON.ToJSON());
Assert.isTrue(lJSON.IsNull('data'), lJSON.ToJSON());
Assert.IsTrue(lJSON.IsNull('data'), lJSON.ToJSON());
finally
lJSON.Free;
end;
@ -896,7 +882,7 @@ begin
Assert.areEqual(2, lJSON.A['items'].Count, lJSON.ToJSON());
Assert.areEqual('erritem1', lJSON.A['items'].O[0].S['message'], lJSON.ToJSON());
Assert.areEqual('erritem2', lJSON.A['items'].O[1].S['message'], lJSON.ToJSON());
Assert.isTrue(lJSON.IsNull('data'), lJSON.ToJSON());
Assert.IsTrue(lJSON.IsNull('data'), lJSON.ToJSON());
finally
lJSON.Free;
end;
@ -939,8 +925,8 @@ var
begin
lObj1 := TEntityWithArray.Create;
try
lObj1.Names := ['one','two','three'];
lObj1.Values := [1,2,3];
lObj1.Names := ['one', 'two', 'three'];
lObj1.Values := [1, 2, 3];
lObj1.Booleans := [true, false];
lBody := GetDefaultSerializer.SerializeObject(lObj1);
@ -949,22 +935,22 @@ begin
try
GetDefaultSerializer.DeserializeObject(lRes.Content, lObj2);
Assert.AreEqual(4, Length(lObj2.Names));
Assert.AreEqual(lObj1.Names[0], lObj2.Names[0]);
Assert.AreEqual(lObj1.Names[1], lObj2.Names[1]);
Assert.AreEqual(lObj1.Names[2], lObj2.Names[2]);
Assert.AreEqual('added', lObj2.Names[3]);
Assert.areEqual(4, Length(lObj2.Names));
Assert.areEqual(lObj1.Names[0], lObj2.Names[0]);
Assert.areEqual(lObj1.Names[1], lObj2.Names[1]);
Assert.areEqual(lObj1.Names[2], lObj2.Names[2]);
Assert.areEqual('added', lObj2.Names[3]);
Assert.AreEqual(4, Length(lObj2.Values));
Assert.AreEqual(lObj1.Values[0], lObj2.Values[0]);
Assert.AreEqual(lObj1.Values[1], lObj2.Values[1]);
Assert.AreEqual(lObj1.Values[2], lObj2.Values[2]);
Assert.AreEqual(99, lObj2.Values[3]);
Assert.areEqual(4, Length(lObj2.Values));
Assert.areEqual(lObj1.Values[0], lObj2.Values[0]);
Assert.areEqual(lObj1.Values[1], lObj2.Values[1]);
Assert.areEqual(lObj1.Values[2], lObj2.Values[2]);
Assert.areEqual(99, lObj2.Values[3]);
Assert.AreEqual(3, Length(lObj2.Booleans));
Assert.AreEqual(lObj1.Booleans[0], lObj2.Booleans[0]);
Assert.AreEqual(lObj1.Booleans[1], lObj2.Booleans[1]);
Assert.AreEqual(True, lObj2.Booleans[2]);
Assert.areEqual(3, Length(lObj2.Booleans));
Assert.areEqual(lObj1.Booleans[0], lObj2.Booleans[0]);
Assert.areEqual(lObj1.Booleans[1], lObj2.Booleans[1]);
Assert.areEqual(true, lObj2.Booleans[2]);
finally
lObj2.Free;
end;
@ -983,19 +969,19 @@ begin
try
lBody := GetDefaultSerializer.SerializeObject(lObj1);
lRes := RESTClient.Post('/entitywitharrays', lBody);
lRes := RESTClient.Post('/entitywitharrays', lBody);
lObj2 := TEntityWithArray.Create;
try
GetDefaultSerializer.DeserializeObject(lRes.Content, lObj2);
Assert.AreEqual(1, Length(lObj2.Names));
Assert.AreEqual('added', lObj2.Names[0]);
Assert.areEqual(1, Length(lObj2.Names));
Assert.areEqual('added', lObj2.Names[0]);
Assert.AreEqual(1, Length(lObj2.Values));
Assert.AreEqual(99, lObj2.Values[0]);
Assert.areEqual(1, Length(lObj2.Values));
Assert.areEqual(99, lObj2.Values[0]);
Assert.AreEqual(1, Length(lObj2.Booleans));
Assert.AreEqual(True, lObj2.Booleans[0]);
Assert.areEqual(1, Length(lObj2.Booleans));
Assert.areEqual(true, lObj2.Booleans[0]);
finally
lObj2.Free;
end;
@ -1015,7 +1001,7 @@ begin
try
for I := 0 to lJArr.Count - 1 do
begin
Assert.isTrue(lJArr[I].A[TMVCConstants.HATEOAS_PROP_NAME].Count = 2, '_links doesn''t exists');
Assert.IsTrue(lJArr[I].A[TMVCConstants.HATEOAS_PROP_NAME].Count = 2, '_links doesn''t exists');
Assert.areEqual(lJArr[I].A[TMVCConstants.HATEOAS_PROP_NAME].O[0].S[HATEOAS.REL], 'test0');
Assert.areEqual(lJArr[I].A[TMVCConstants.HATEOAS_PROP_NAME].O[1].S[HATEOAS.REL], 'test1');
end;
@ -1054,7 +1040,7 @@ begin
for I := 0 to lJSONArr.Count - 1 do
begin
lJSONObj := lJSONArr.Items[I] as System.JSON.TJSONObject;
Assert.isFalse(lJSONObj.GetValue<string>('firstname').IsEmpty);
Assert.IsFalse(lJSONObj.GetValue<string>('firstname').IsEmpty);
end;
finally
lJSONArr.Free;
@ -1079,17 +1065,15 @@ begin
for lCompType in CompressionTypes do
begin
RESTClient.AcceptEncoding(lCompType);
lRes := RESTClient
.AddQueryStringParam('count', 100)
.Get('/wrappedpeople');
lRes := RESTClient.AddQueryStringParam('count', 100).Get('/wrappedpeople');
Assert.areEqual(CompressionTypeResult[j], lRes.HeaderValue('Content-Encoding'));
lJSONArr := TMVCJsonDataObjectsSerializer.ParseArray(lRes.Content);
try
for I := 0 to lJSONArr.Count - 1 do
begin
Assert.isFalse(lJSONArr.O[I].S['firstname'].IsEmpty);
Assert.isFalse(lJSONArr.O[I].S['lastname'].IsEmpty);
Assert.isFalse(lJSONArr.O[I].S['dob'].IsEmpty);
Assert.IsFalse(lJSONArr.O[I].S['firstname'].IsEmpty);
Assert.IsFalse(lJSONArr.O[I].S['lastname'].IsEmpty);
Assert.IsFalse(lJSONArr.O[I].S['dob'].IsEmpty);
Assert.areEqual<TJsonDataType>(jdtBool, lJSONArr.O[I].Types['married']);
end;
finally
@ -1128,13 +1112,13 @@ begin
lRes := RESTClient.Accept(TMVCMediaType.TEXT_HTML).Get('/static.html');
Assert.areEqual(404, lRes.StatusCode, '/static.html');
lRes := RESTClient.HandleRedirects(False).Accept(TMVCMediaType.TEXT_HTML).Get('/static');
lRes := RESTClient.HandleRedirects(false).Accept(TMVCMediaType.TEXT_HTML).Get('/static');
Assert.areEqual(301, lRes.StatusCode, '/static');
lRes := RESTClient.Accept(TMVCMediaType.TEXT_HTML).Get('/static/');
Assert.areEqual(200, lRes.StatusCode, '/static/');
lRes := RESTClient.HandleRedirects(False).Accept(TMVCMediaType.TEXT_HTML).Get('/static/folder1');
lRes := RESTClient.HandleRedirects(false).Accept(TMVCMediaType.TEXT_HTML).Get('/static/folder1');
Assert.areEqual(301, lRes.StatusCode, '/static/folder1');
lRes := RESTClient.Accept(TMVCMediaType.TEXT_HTML).Get('/static/folder1/');
@ -1202,8 +1186,7 @@ begin
P.LastName := StringOfChar('*', 1000);
P.DOB := EncodeDate(1979, 1, 1);
P.Married := true;
r := RESTClient.Accept(TMVCMediaType.APPLICATION_JSON)
.Post('/objects', GetDefaultSerializer.SerializeObject(P));
r := RESTClient.Accept(TMVCMediaType.APPLICATION_JSON).Post('/objects', GetDefaultSerializer.SerializeObject(P));
finally
P.Free;
end;
@ -1220,38 +1203,38 @@ begin
Assert.areEqual<Integer>(HTTP_STATUS.OK, lRes.StatusCode, lRes.Content);
lJSON := StrToJSONObject(lRes.Content);
try
Assert.isTrue(lJSON.Contains('ncCamelCase_Single'), lJSON.ToJSON());
Assert.isTrue(lJSON.Contains('ncLowerCase_Single'), lJSON.ToJSON());
Assert.isTrue(lJSON.Contains('ncPascalCase_Single'), lJSON.ToJSON());
Assert.isTrue(lJSON.Contains('ncUpperCase_Single'), lJSON.ToJSON());
Assert.IsTrue(lJSON.Contains('ncCamelCase_Single'), lJSON.ToJSON());
Assert.IsTrue(lJSON.Contains('ncLowerCase_Single'), lJSON.ToJSON());
Assert.IsTrue(lJSON.Contains('ncPascalCase_Single'), lJSON.ToJSON());
Assert.IsTrue(lJSON.Contains('ncUpperCase_Single'), lJSON.ToJSON());
Assert.isTrue(lJSON.Contains('ncCamelCase_List'), lJSON.ToJSON());
Assert.isTrue(lJSON.Contains('ncLowerCase_List'), lJSON.ToJSON());
Assert.isTrue(lJSON.Contains('ncPascalCase_List'), lJSON.ToJSON());
Assert.isTrue(lJSON.Contains('ncUpperCase_List'), lJSON.ToJSON());
Assert.IsTrue(lJSON.Contains('ncCamelCase_List'), lJSON.ToJSON());
Assert.IsTrue(lJSON.Contains('ncLowerCase_List'), lJSON.ToJSON());
Assert.IsTrue(lJSON.Contains('ncPascalCase_List'), lJSON.ToJSON());
Assert.IsTrue(lJSON.Contains('ncUpperCase_List'), lJSON.ToJSON());
Assert.areEqual(jdtObject, lJSON.Types['ncCamelCase_Single']);
Assert.areEqual(jdtObject, lJSON.Types['ncLowerCase_Single']);
Assert.areEqual(jdtObject, lJSON.Types['ncPascalCase_Single']);
Assert.areEqual(jdtObject, lJSON.Types['ncUpperCase_Single']);
Assert.isTrue(lJSON.O['ncCamelCase_Single'].Contains('custNo'), lJSON.O['ncCamelCase_Single'].ToJSON());
Assert.isTrue(lJSON.O['ncLowerCase_Single'].Contains('cust_no'), lJSON.O['ncLowerCase_Single'].ToJSON());
Assert.isTrue(lJSON.O['ncPascalCase_Single'].Contains('CustNo'), lJSON.O['ncPascalCase_Single'].ToJSON());
Assert.isTrue(lJSON.O['ncUpperCase_Single'].Contains('CUST_NO'), lJSON.O['ncUpperCase_Single'].ToJSON());
Assert.IsTrue(lJSON.O['ncCamelCase_Single'].Contains('custNo'), lJSON.O['ncCamelCase_Single'].ToJSON());
Assert.IsTrue(lJSON.O['ncLowerCase_Single'].Contains('cust_no'), lJSON.O['ncLowerCase_Single'].ToJSON());
Assert.IsTrue(lJSON.O['ncPascalCase_Single'].Contains('CustNo'), lJSON.O['ncPascalCase_Single'].ToJSON());
Assert.IsTrue(lJSON.O['ncUpperCase_Single'].Contains('CUST_NO'), lJSON.O['ncUpperCase_Single'].ToJSON());
Assert.areEqual(jdtArray, lJSON.Types['ncCamelCase_List']);
Assert.areEqual(jdtArray, lJSON.Types['ncLowerCase_List']);
Assert.areEqual(jdtArray, lJSON.Types['ncPascalCase_List']);
Assert.areEqual(jdtArray, lJSON.Types['ncUpperCase_List']);
Assert.isTrue(lJSON.A['ncCamelCase_List'][0].ObjectValue.Contains('custNo'),
Assert.IsTrue(lJSON.A['ncCamelCase_List'][0].ObjectValue.Contains('custNo'),
lJSON.A['ncCamelCase_List'][0].ObjectValue.ToJSON());
Assert.isTrue(lJSON.A['ncLowerCase_List'][0].ObjectValue.Contains('cust_no'),
Assert.IsTrue(lJSON.A['ncLowerCase_List'][0].ObjectValue.Contains('cust_no'),
lJSON.A['ncLowerCase_List'][0].ObjectValue.ToJSON());
Assert.isTrue(lJSON.A['ncPascalCase_List'][0].ObjectValue.Contains('CustNo'),
Assert.IsTrue(lJSON.A['ncPascalCase_List'][0].ObjectValue.Contains('CustNo'),
lJSON.A['ncPascalCase_List'][0].ObjectValue.ToJSON());
Assert.isTrue(lJSON.A['ncUpperCase_List'][0].ObjectValue.Contains('CUST_NO'),
Assert.IsTrue(lJSON.A['ncUpperCase_List'][0].ObjectValue.Contains('CUST_NO'),
lJSON.A['ncUpperCase_List'][0].ObjectValue.ToJSON());
finally
@ -1300,8 +1283,7 @@ begin
P.DOB := EncodeDate(1979, 1, 1);
P.Married := true;
try
r := RESTClient.Accept(TMVCMediaType.APPLICATION_JSON)
.Post('/objects', GetDefaultSerializer.SerializeObject(P)
r := RESTClient.Accept(TMVCMediaType.APPLICATION_JSON).Post('/objects', GetDefaultSerializer.SerializeObject(P)
{ Mapper.ObjectToJSONObject(P) }
);
except
@ -1338,10 +1320,7 @@ var
begin
JSON := System.JSON.TJSONObject.Create;
JSON.AddPair('client', 'clientdata');
r := RESTClient
.AddPathParam('par1', 1)
.AddPathParam('par2', 2)
.AddPathParam('par3', 3)
r := RESTClient.AddPathParam('par1', 1).AddPathParam('par2', 2).AddPathParam('par3', 3)
.Post('/echo/($par1)/($par2)/($par3)', TSystemJSON.JSONValueToString(JSON));
JSON := TSystemJSON.StringAsJSONObject(r.Content);
try
@ -1368,16 +1347,14 @@ var
lContentType: string;
lContentCharset: string;
begin
res := RESTClient
.Accept('application/json')
.Post('/testconsumes', TSystemJSON.JSONValueToString(TJSONString.Create('Hello World')),
BuildContentType('application/json', 'utf-8'));
res := RESTClient.Accept('application/json').Post('/testconsumes',
TSystemJSON.JSONValueToString(TJSONString.Create('Hello World')), BuildContentType('application/json', 'utf-8'));
Assert.areEqual<Integer>(HTTP_STATUS.OK, res.StatusCode);
Assert.areEqual('Hello World', res.Content);
SplitContentMediaTypeAndCharset(res.ContentType, lContentType, lContentCharset);
Assert.AreEqual(lContentType, TMVCMediaType.APPLICATION_JSON, True);
Assert.AreEqual(lContentCharset, TMVCCharSet.UTF_8, True);
Assert.areEqual(lContentType, TMVCMediaType.APPLICATION_JSON, true);
Assert.areEqual(lContentCharset, TMVCCharSet.UTF_8, true);
end;
procedure TServerTest.TestProducesConsumes02;
@ -1386,17 +1363,13 @@ var
lContentType: string;
lContentCharset: string;
begin
res := RESTClient
.Accept('text/plain')
.Post('/testconsumes', 'Hello World', 'text/plain');
res := RESTClient.Accept('text/plain').Post('/testconsumes', 'Hello World', 'text/plain');
Assert.areEqual('Hello World', res.Content);
SplitContentMediaTypeAndCharset(res.ContentType, lContentType, lContentCharset);
Assert.AreEqual(lContentType, TMVCMediaType.TEXT_PLAIN, True);
Assert.AreEqual(lContentCharset, TMVCCharSet.UTF_8, True);
Assert.areEqual(lContentType, TMVCMediaType.TEXT_PLAIN, true);
Assert.areEqual(lContentCharset, TMVCCharSet.UTF_8, true);
res := RESTClient
.Accept('text/plain')
.Post('/testconsumes', '{"name": "Daniele"}');
res := RESTClient.Accept('text/plain').Post('/testconsumes', '{"name": "Daniele"}');
Assert.areEqual<Integer>(HTTP_STATUS.NotFound, res.StatusCode);
end;
@ -1406,25 +1379,22 @@ var
lContentType: string;
lContentCharset: string;
begin
res := RESTClient
.Accept(TMVCMediaType.TEXT_PLAIN)
.Post('/testconsumes/textiso8859_1', 'àèéìòù', BuildContentType(TMVCMediaType.TEXT_PLAIN, TMVCCharSet.ISO88591));
res := RESTClient.Accept(TMVCMediaType.TEXT_PLAIN).Post('/testconsumes/textiso8859_1', 'àèéìòù',
BuildContentType(TMVCMediaType.TEXT_PLAIN, TMVCCharSet.ISO88591));
Assert.areEqual<Integer>(HTTP_STATUS.OK, res.StatusCode);
// Assert.AreNotEqual('àèéìòù', res.Content, 'non iso8859-1 text is rendered ok whan should not');
// Assert.AreNotEqual('àèéìòù', res.Content, 'non iso8859-1 text is rendered ok whan should not');
SplitContentMediaTypeAndCharset(res.ContentType, lContentType, lContentCharset);
Assert.AreEqual(lContentType, TMVCMediaType.TEXT_PLAIN);
Assert.AreEqual(lContentCharset, TMVCCharset.ISO88591);
Assert.areEqual(lContentType, TMVCMediaType.TEXT_PLAIN);
Assert.areEqual(lContentCharset, TMVCCharSet.ISO88591);
res := RESTClient
.Accept(TMVCMediaType.TEXT_PLAIN)
.Post('/testconsumes/textiso8859_1', 'this is an iso8859-1 text',
BuildContentType(TMVCMediaType.TEXT_PLAIN, TMVCCharSet.ISO88591));
res := RESTClient.Accept(TMVCMediaType.TEXT_PLAIN).Post('/testconsumes/textiso8859_1', 'this is an iso8859-1 text',
BuildContentType(TMVCMediaType.TEXT_PLAIN, TMVCCharSet.ISO88591));
Assert.areEqual<Integer>(HTTP_STATUS.OK, res.StatusCode);
SplitContentMediaTypeAndCharset(res.ContentType, lContentType, lContentCharset);
Assert.areEqual('this is an iso8859-1 text', res.Content);
Assert.AreEqual(lContentType, TMVCMediaType.TEXT_PLAIN);
Assert.AreEqual(lContentCharset, TMVCCharset.ISO88591);
Assert.areEqual(lContentType, TMVCMediaType.TEXT_PLAIN);
Assert.areEqual(lContentCharset, TMVCCharSet.ISO88591);
end;
procedure TServerTest.TestPUTWithParamsAndJSONBody;
@ -1434,10 +1404,7 @@ var
begin
JSON := System.JSON.TJSONObject.Create;
JSON.AddPair('client', 'clientdata');
r := RESTClient
.AddPathParam('par1', 1)
.AddPathParam('par2', 2)
.AddPathParam('par3', 3)
r := RESTClient.AddPathParam('par1', 1).AddPathParam('par2', 2).AddPathParam('par3', 3)
.Put('/echo/($par1)/($par2)/($par3)', TSystemJSON.JSONValueToString(JSON));
JSON := TSystemJSON.StringAsJSONObject(r.Content);
@ -1456,12 +1423,8 @@ var
begin
JSON := System.JSON.TJSONObject.Create;
JSON.AddPair('client', 'clientdata');
r := RESTClient
.AddHeader(TMVCConstants.X_HTTP_Method_Override, 'PUT')
.AddPathParam('par1', 1)
.AddPathParam('par2', 2)
.AddPathParam('par3', 3)
.Post('/echo/($par1)/($par2)/($par3)', TSystemJSON.JSONValueToString(JSON));
r := RESTClient.AddHeader(TMVCConstants.X_HTTP_Method_Override, 'PUT').AddPathParam('par1', 1).AddPathParam('par2', 2)
.AddPathParam('par3', 3).Post('/echo/($par1)/($par2)/($par3)', TSystemJSON.JSONValueToString(JSON));
JSON := TSystemJSON.StringAsJSONObject(r.Content);
try
@ -1497,10 +1460,7 @@ begin
r := RESTClient.Get('/req/with/params');
Assert.areEqual<Integer>(HTTP_STATUS.NotFound, r.StatusCode, '/req/with/params');
r := RESTClient
.AddPathParam('par1', '1')
.AddPathParam('par2', '2')
.AddPathParam('par3', '3')
r := RESTClient.AddPathParam('par1', '1').AddPathParam('par2', '2').AddPathParam('par3', '3')
.Get('/req/with/params/($par1)/($par2)/($par3)');
Assert.areEqual<Integer>(HTTP_STATUS.OK, r.StatusCode);
@ -1514,25 +1474,15 @@ begin
lJSON.Free;
end;
r := RESTClient
.AddPathParam('par1', 1)
.AddPathParam('par2', 2)
.AddPathParam('par3', 3)
r := RESTClient.AddPathParam('par1', 1).AddPathParam('par2', 2).AddPathParam('par3', 3)
.Post('/req/with/params/($par1)/($par2)/($par3)');
Assert.areEqual<Integer>(HTTP_STATUS.NotFound, r.StatusCode);
r := RESTClient
.AddPathParam('par1', 1)
.AddPathParam('par2', 2)
.AddPathParam('par3', 3)
r := RESTClient.AddPathParam('par1', 1).AddPathParam('par2', 2).AddPathParam('par3', 3)
.Put('/req/with/params/($par1)/($par2)/($par3)');
Assert.areEqual<Integer>(HTTP_STATUS.NotFound, r.StatusCode);
r := RESTClient
.AddPathParam('par1', 1)
.AddPathParam('par2', 2)
.AddPathParam('par3', 3)
r := RESTClient.AddPathParam('par1', 1).AddPathParam('par2', 2).AddPathParam('par3', 3)
.Delete('/req/with/params/($par1)/($par2)/($par3)');
Assert.areEqual<Integer>(HTTP_STATUS.OK, r.StatusCode);
lJSON := TSystemJSON.StringAsJSONObject(r.Content);
@ -1551,13 +1501,10 @@ procedure TServerTest.TestReqWithURLMappedParams(const par1, par2, par3: string)
var
r: IMVCRESTResponse;
begin
r := RESTClient
.AddPathParam('par1', par1)
.AddPathParam('par2', par2)
.AddPathParam('par3', par3)
r := RESTClient.AddPathParam('par1', par1).AddPathParam('par2', par2).AddPathParam('par3', par3)
.Get('/req/with/params/($par1)/($par2)/($par3)');
Assert.areEqual<Integer>(HTTP_STATUS.OK, r.StatusCode,
Format('URL mapped fails for these characters: "%s","%s","%s"', [par1, par2, par3]));
Assert.areEqual<Integer>(HTTP_STATUS.OK, r.StatusCode, Format('URL mapped fails for these characters: "%s","%s","%s"',
[par1, par2, par3]));
end;
procedure TServerTest.TestResponseAccepted;
@ -1567,7 +1514,7 @@ var
begin
r := RESTClient.Post('/responses/accepted');
Assert.areEqual<Integer>(HTTP_STATUS.Accepted, r.StatusCode);
Assert.isTrue(r.StatusText.Contains('Accepted'));
Assert.IsTrue(r.StatusText.Contains('Accepted'));
lJSON := StrToJSONObject(r.Content);
try
Assert.areEqual(2, lJSON.O['task'].Count);
@ -1584,7 +1531,7 @@ var
begin
r := RESTClient.Post('/responses/created');
Assert.areEqual<Integer>(HTTP_STATUS.Created, r.StatusCode);
Assert.isTrue(r.StatusText.Contains('Created'));
Assert.IsTrue(r.StatusText.Contains('Created'));
Assert.IsEmpty(r.Content);
end;
@ -1594,7 +1541,7 @@ var
begin
r := RESTClient.Get('/responses/nocontent');
Assert.areEqual<Integer>(HTTP_STATUS.NoContent, r.StatusCode);
Assert.isTrue(r.StatusText.Contains('No Content'));
Assert.IsTrue(r.StatusText.Contains('No Content'));
Assert.IsEmpty(r.Content);
end;
@ -1636,17 +1583,17 @@ begin
lNullableTest := TNullablesTest.Create();
try
lSer.DeserializeObject(lRes.Content, lNullableTest);
Assert.isFalse(lNullableTest.f_int2.HasValue);
Assert.isFalse(lNullableTest.f_int4.HasValue);
Assert.isFalse(lNullableTest.f_int8.HasValue);
Assert.isFalse(lNullableTest.f_date.HasValue);
Assert.isFalse(lNullableTest.f_time.HasValue);
Assert.isFalse(lNullableTest.f_datetime.HasValue);
Assert.isFalse(lNullableTest.f_bool.HasValue);
Assert.isFalse(lNullableTest.f_float4.HasValue);
Assert.isFalse(lNullableTest.f_float8.HasValue);
Assert.isFalse(lNullableTest.f_string.HasValue);
Assert.isFalse(lNullableTest.f_currency.HasValue);
Assert.IsFalse(lNullableTest.f_int2.HasValue);
Assert.IsFalse(lNullableTest.f_int4.HasValue);
Assert.IsFalse(lNullableTest.f_int8.HasValue);
Assert.IsFalse(lNullableTest.f_date.HasValue);
Assert.IsFalse(lNullableTest.f_time.HasValue);
Assert.IsFalse(lNullableTest.f_datetime.HasValue);
Assert.IsFalse(lNullableTest.f_bool.HasValue);
Assert.IsFalse(lNullableTest.f_float4.HasValue);
Assert.IsFalse(lNullableTest.f_float8.HasValue);
Assert.IsFalse(lNullableTest.f_string.HasValue);
Assert.IsFalse(lNullableTest.f_currency.HasValue);
{ TODO -oDanieleT -cGeneral : Compare streams too }
// Assert.AreEqual('0123456789', lNullableTest.f_blob.Value, 0);
finally
@ -1700,7 +1647,7 @@ begin
lRes := RESTClient.Accept(TMVCMediaType.TEXT_HTML).Get('/static/');
Assert.areEqual(200, lRes.StatusCode, '/static/');
lRes := RESTClient.HandleRedirects(False).Accept(TMVCMediaType.TEXT_HTML).Get('/static');
lRes := RESTClient.HandleRedirects(false).Accept(TMVCMediaType.TEXT_HTML).Get('/static');
Assert.areEqual(301, lRes.StatusCode, '/static');
Assert.areEqual('/static/', lRes.HeaderValue('Location'), 'Wrong redirect');
end;
@ -1722,7 +1669,7 @@ begin
begin
lUrl := '..\' + lUrl;
lRes := RESTClient.Accept(TMVCMediaType.TEXT_HTML).Get('/static/' + lUrl);
Assert.areEqual(404, lRes.StatusCode, 'Fail with: ' + '/static/' + lUrl);
Assert.IsTrue((lRes.StatusCode = 403) or (lRes.StatusCode = 404), 'Fail with: ' + '/static/' + lUrl);
end;
end;
@ -1733,17 +1680,17 @@ var
lUrl: string;
begin
lRes := RESTClient.Accept(TMVCMediaType.TEXT_HTML).Get('/static/..\..\donotdeleteme.txt');
Assert.areEqual(404, lRes.StatusCode);
Assert.IsTrue((lRes.StatusCode = 404) or (lRes.StatusCode = 403));
lRes := RESTClient.Accept(TMVCMediaType.TEXT_HTML).Get('/static/../../donotdeleteme.txt');
Assert.areEqual(404, lRes.StatusCode);
Assert.IsTrue((lRes.StatusCode = 404) or (lRes.StatusCode = 403));
lUrl := 'Windows\win.ini';
for I := 1 to 30 do
begin
lUrl := '..\' + lUrl;
lRes := RESTClient.Accept(TMVCMediaType.TEXT_HTML).Get('/static/' + lUrl);
Assert.areEqual(404, lRes.StatusCode, 'Fail with: ' + '/static/' + lUrl);
Assert.IsTrue((lRes.StatusCode = 404) or (lRes.StatusCode = 403), 'Fail with: ' + '/static/' + lUrl);
end;
end;
@ -1852,23 +1799,29 @@ var
c1: IMVCRESTClient;
res: IMVCRESTResponse;
S: string;
lC: TCookie;
begin
c1 := TMVCRESTClient.New.BaseURL(TEST_SERVER_ADDRESS, 9999);
c1.Accept(TMVCMediaType.APPLICATION_JSON);
res := c1.Post('/session/daniele teti'); // imposto un valore in sessione
S := res.HeaderValue('Set-Cookie');
Assert.isFalse(S.Contains('Expires'), 'Session cookie contains "expires" attribute');
// S := res.HeaderValue('Set-Cookie');
lC := res.CookieByName('dtsessionid');
Assert.IsFalse(S.Contains('Expires'), 'Session cookie contains "expires" attribute');
c1.AddCookie('dtsessionid', lC.Value);
res := c1.Get('/session'); // rileggo il valore dalla sessione
Assert.areEqual('daniele teti', res.Content);
c1.Accept(TMVCMediaType.TEXT_PLAIN);
c1.AddCookie('dtsessionid', lC.Value);
res := c1.Get('/session');
// rileggo il valore dalla sessione
Assert.areEqual('daniele teti', res.Content);
// aggiungo altri cookies
c1.AddCookie('dtsessionid', lC.Value);
res := c1.Get('/lotofcookies'); // rileggo il valore dalla sessione
Assert.areEqual<Integer>(HTTP_STATUS.OK, res.StatusCode);
c1.Accept(TMVCMediaType.TEXT_PLAIN);
c1.AddCookie('dtsessionid', lC.Value);
res := c1.Get('/session'); // rileggo il valore dalla sessione
Assert.areEqual('daniele teti', res.Content);
end;
@ -1903,9 +1856,9 @@ begin
{ directory traversal attacks receive always 404 }
lUrl := '..\' + lUrl;
lRes := RESTClient.Accept(TMVCMediaType.TEXT_HTML).Get('/spa/' + lUrl);
Assert.areEqual(404, lRes.StatusCode);
Assert.Contains(lRes.Content, '[EMVCException] Not Found', true);
Assert.Contains(lRes.Content, '<p>HTTP 404</p>', true);
Assert.IsTrue((404 = lRes.StatusCode) or (403 = lRes.StatusCode));
// Assert.Contains(lRes.Content, 'Not Found', true);
// Assert.Contains(lRes.Content, '404', true);
end;
end;
@ -1941,7 +1894,7 @@ var
begin
// ----------------------'/typed/all/($ParString)/($ParInteger)/($ParInt64)/($ParSingle)/($ParDouble)/($ParExtended)')');
res := RESTClient.Get('/typed/all/mystring/1234/12345678/12.3/1234.5678/1234.5678');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
lJObj := TSystemJSON.StringAsJSONObject(res.Content);
try
Assert.areEqual('mystring', lJObj.GetValue('ParString').Value, 'ParString');
@ -1960,7 +1913,7 @@ var
res: IMVCRESTResponse;
begin
res := RESTClient.Get('/typed/booleans/true/false/1/0');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.areEqual('true.false.true.false', res.Content.ToLower);
end;
@ -1969,7 +1922,7 @@ var
res: IMVCRESTResponse;
begin
res := RESTClient.Get('/typed/double1/1234.5678');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.areEqual('1234.5678 modified from server', res.Content);
end;
@ -1979,7 +1932,7 @@ var
res: IMVCRESTResponse;
begin
res := RESTClient.Get('/typed/extended1/1234.5678');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.areEqual('1234.5678 modified from server', res.Content);
end;
@ -1989,7 +1942,7 @@ var
res: IMVCRESTResponse;
begin
res := RESTClient.Get('/typed/int641/12345678');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.areEqual('12345678 modified from server', res.Content);
end;
@ -1998,7 +1951,7 @@ var
res: IMVCRESTResponse;
begin
res := RESTClient.Get('/typed/integer1/1234');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.areEqual('1234 modified from server', res.Content);
end;
@ -2007,7 +1960,7 @@ var
res: IMVCRESTResponse;
begin
res := RESTClient.Get('/typed/integer1/boom');
Assert.isTrue(res.StatusCode = HTTP_STATUS.BadRequest, 'Cannot route');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.BadRequest, 'Cannot route');
Assert.Contains(res.Content, 'EConvertError');
Assert.Contains(res.Content, '''boom'' is not a valid');
end;
@ -2017,7 +1970,7 @@ var
res: IMVCRESTResponse;
begin
res := RESTClient.Get('/typed/single1/1234.5');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.areEqual('1234.5 modified from server', res.Content);
end;
@ -2074,15 +2027,15 @@ var
res: IMVCRESTResponse;
begin
res := RESTClient.Get('/typed/tguid1/{161BEA56-480B-40A8-AF0E-7FDF6B08E121}');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.areEqual('{161BEA56-480B-40A8-AF0E-7FDF6B08E121} modified from server', res.Content);
res := RESTClient.Get('/typed/tguid1/161BEA56-480B-40A8-AF0E-7FDF6B08E121');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.areEqual('{161BEA56-480B-40A8-AF0E-7FDF6B08E121} modified from server', res.Content);
res := RESTClient.Get('/typed/tguid1/161BEA56480B40A8AF0E7FDF6B08E121');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Cannot route');
Assert.areEqual('{161BEA56-480B-40A8-AF0E-7FDF6B08E121} modified from server', res.Content);
end;
@ -2194,7 +2147,7 @@ var
res: IMVCRESTResponse;
begin
res := RESTClient.AddPathParam('username', UserName).Get('/login/{username}');
Assert.isTrue(res.StatusCode = HTTP_STATUS.OK, 'Login Failed');
Assert.IsTrue(res.StatusCode = HTTP_STATUS.OK, 'Login Failed');
end;
{ TJSONRPCServerTest }
@ -2208,7 +2161,8 @@ end;
procedure TJSONRPCServerTest.TestHooks;
begin
var lRequest1: IJSONRPCRequest := TJSONRPCRequest.Create(1234, 'request1');
var
lRequest1: IJSONRPCRequest := TJSONRPCRequest.Create(1234, 'request1');
var
lResp := FExecutor3.ExecuteRequest(lRequest1);
Assert.areEqual('OnBeforeRoutingHook|OnBeforeCallHook|OnAfterCallHook',
@ -2219,11 +2173,12 @@ procedure TJSONRPCServerTest.TestHooksNotif;
var
lResp: IJSONRPCResponse;
begin
var lNotif: IJSONRPCNotification := TJSONRPCNotification.Create('Notif1');
var
lNotif: IJSONRPCNotification := TJSONRPCNotification.Create('Notif1');
lResp := FExecutor3.ExecuteNotification(lNotif);
Assert.areEqual('OnBeforeRoutingHook|OnBeforeCallHook|OnAfterCallHook',
FExecutor3.HTTPResponse.HeaderValue['x-history']);
Assert.isFalse(lResp.IsError);
Assert.IsFalse(lResp.IsError);
Assert.WillRaise(
procedure
begin
@ -2235,10 +2190,11 @@ procedure TJSONRPCServerTest.TestHooksNotifWhenOnAfterCallHookRaisesError;
var
lResp: IJSONRPCResponse;
begin
var lNotif: IJSONRPCNotification := TJSONRPCNotification.Create('error_OnAfterCallHook');
var
lNotif: IJSONRPCNotification := TJSONRPCNotification.Create('error_OnAfterCallHook');
lResp := FExecutor3.ExecuteNotification(lNotif);
Assert.areEqual('', FExecutor3.HTTPResponse.HeaderValue['x-history']);
Assert.isTrue(lResp.IsError);
Assert.IsTrue(lResp.IsError);
Assert.WillNotRaise(
procedure
begin
@ -2250,10 +2206,11 @@ procedure TJSONRPCServerTest.TestHooksNotifWhenOnBeforeCallHookRaisesError;
var
lResp: IJSONRPCResponse;
begin
var lNotif: IJSONRPCNotification := TJSONRPCNotification.Create('error_OnBeforeCallHook');
var
lNotif: IJSONRPCNotification := TJSONRPCNotification.Create('error_OnBeforeCallHook');
lResp := FExecutor3.ExecuteNotification(lNotif);
Assert.areEqual('', FExecutor3.HTTPResponse.HeaderValue['x-history']);
Assert.isTrue(lResp.IsError);
Assert.IsTrue(lResp.IsError);
Assert.WillNotRaise(
procedure
begin
@ -2265,10 +2222,11 @@ procedure TJSONRPCServerTest.TestHooksNotifWhenOnBeforeRoutingHookRaisesError;
var
lResp: IJSONRPCResponse;
begin
var lNotif: IJSONRPCNotification := TJSONRPCNotification.Create('error_OnBeforeRoutingHook');
var
lNotif: IJSONRPCNotification := TJSONRPCNotification.Create('error_OnBeforeRoutingHook');
lResp := FExecutor3.ExecuteNotification(lNotif);
Assert.areEqual('', FExecutor3.HTTPResponse.HeaderValue['x-history']);
Assert.isTrue(lResp.IsError);
Assert.IsTrue(lResp.IsError);
Assert.WillNotRaise(
procedure
begin
@ -2280,38 +2238,42 @@ procedure TJSONRPCServerTest.TestHooksWhenMethodRaisesError;
var
lResp: IJSONRPCResponse;
begin
var lRequest1: IJSONRPCRequest := TJSONRPCRequest.Create(1234, 'RequestWithError');
var
lRequest1: IJSONRPCRequest := TJSONRPCRequest.Create(1234, 'RequestWithError');
lResp := FExecutor3.ExecuteRequest(lRequest1);
Assert.areEqual('OnBeforeRoutingHook|OnBeforeCallHook|OnAfterCallHook|error',
FExecutor3.HTTPResponse.HeaderValue['x-history']);
Assert.isTrue(lResp.IsError, 'Method raised error but response is not an error');
Assert.IsTrue(lResp.IsError, 'Method raised error but response is not an error');
end;
procedure TJSONRPCServerTest.TestHooksWhenOnAfterCallHookRaisesError;
begin
var lRequest1: IJSONRPCRequest := TJSONRPCRequest.Create(1234, 'error_OnAfterCallHook');
var
lRequest1: IJSONRPCRequest := TJSONRPCRequest.Create(1234, 'error_OnAfterCallHook');
var
lResp := FExecutor3.ExecuteRequest(lRequest1);
Assert.isTrue(lResp.IsError, lResp.ToString(true));
Assert.IsTrue(lResp.IsError, lResp.ToString(true));
Assert.areEqual(lResp.Error.ErrMessage, 'error_OnAfterCallHook');
end;
procedure TJSONRPCServerTest.TestHooksWhenOnBeforeCallHookRaisesError;
begin
var lRequest1: IJSONRPCRequest := TJSONRPCRequest.Create(1234, 'error_OnBeforeCallHook');
var
lRequest1: IJSONRPCRequest := TJSONRPCRequest.Create(1234, 'error_OnBeforeCallHook');
var
lResp := FExecutor3.ExecuteRequest(lRequest1);
Assert.isTrue(lResp.IsError, lResp.ToString(true));
Assert.IsTrue(lResp.IsError, lResp.ToString(true));
Assert.areEqual(lResp.Error.ErrMessage, 'error_OnBeforeCallHook');
end;
procedure TJSONRPCServerTest.TestHooksWhenOnBeforeRoutingHookRaisesError;
begin
var lRequest1: IJSONRPCRequest := TJSONRPCRequest.Create(1234, 'error_OnBeforeRoutingHook');
var
lRequest1: IJSONRPCRequest := TJSONRPCRequest.Create(1234, 'error_OnBeforeRoutingHook');
var
lResp := FExecutor3.ExecuteRequest(lRequest1);
Assert.isTrue(lResp.IsError, lResp.ToString(true));
Assert.IsTrue(lResp.IsError, lResp.ToString(true));
Assert.areEqual(lResp.Error.ErrMessage, 'error_OnBeforeRoutingHook');
end;
@ -2323,7 +2285,7 @@ begin
lReq.Method := 'NotifWithError';
var
lResp := FExecutor3.ExecuteNotification(lReq);
Assert.isTrue(lResp.IsError);
Assert.IsTrue(lResp.IsError);
Assert.Contains(lResp.Error.ErrMessage, 'BOOM NOTIF');
end;
@ -2349,7 +2311,7 @@ begin
lResp := FExecutor.ExecuteRequest(lReq);
Assert.IsNotNull(lResp.Error);
Assert.areEqual(-32601, lResp.Error.Code);
Assert.isTrue(lResp.Error.ErrMessage.StartsWith('Method [nonexist] not found.'));
Assert.IsTrue(lResp.Error.ErrMessage.StartsWith('Method [nonexist] not found.'));
end;
procedure TJSONRPCServerTest.TestRequestWithParams_DT_T_ret_DT;
@ -2390,7 +2352,7 @@ begin
lReq.RequestID := 1234;
lRPCResp := FExecutor.ExecuteRequest(lReq);
Assert.isTrue(lRPCResp.IsError);
Assert.IsTrue(lRPCResp.IsError);
Assert.Contains(lRPCResp.Error.ErrMessage, 'cannot find parameter', true);
end;
@ -2445,8 +2407,8 @@ begin
lReq.Method := 'MyRequest';
lReq.RequestID := 1234;
lResp := FExecutor.ExecuteRequest(lReq);
Assert.isFalse(lResp.IsError);
Assert.isTrue(lResp.Result.AsBoolean);
Assert.IsFalse(lResp.IsError);
Assert.IsTrue(lResp.Result.AsBoolean);
end;
procedure TJSONRPCServerTest.TestRequestWithParams_I_I_ret_I;
@ -2536,7 +2498,7 @@ begin
lReq.Params.AddByName('Multiplier', 4);
lReq.RequestID := 1234;
lRPCResp := FExecutor.ExecuteRequest(lReq);
Assert.isFalse(lRPCResp.IsError);
Assert.IsFalse(lRPCResp.IsError);
Assert.areEqual('DanieleDanieleDanieleDaniele', lRPCResp.Result.AsString);
lRPCResp := FExecutor2.ExecuteRequest(lReq);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
.\bin\initdb -D .\data -E UTF8 --lc-collate=en_US.UTF8 --lc-ctype=en_US.UTF8 --locale=en_US

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More