Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | {923} added TSQLRestServerURIContext.InputAsMultiPart() method |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
d9099ee94bb1e430f27f11f891bd5dd5 |
User & Date: | ab 2015-02-18 08:14:13 |
2015-02-18
| ||
14:40 | {924} introducing detailed SOA statistics for method-based and interface-based services, available from the TSQLRestServer.ServiceMethodStat[] property or the associated TServiceFactoryServer.Stats / Stat[] methods, or remotely as an option to the TSQLRestServer.Stat() service check-in: 616d7de50c user: ab tags: trunk | |
08:14 | {923} added TSQLRestServerURIContext.InputAsMultiPart() method check-in: d9099ee94b user: ab tags: trunk | |
07:57 | {922} added MultiPartFormDataDecode() function to decode multipart/form-data POST requests check-in: 2358980405 user: ab tags: trunk | |
Changes to SQLite3/mORMot.pas.
790 791 792 793 794 795 796 797 798 799 800 801 802 803 .... 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 ..... 30013 30014 30015 30016 30017 30018 30019 30020 30021 30022 30023 30024 30025 30026 30027 ..... 30787 30788 30789 30790 30791 30792 30793 30794 30795 30796 30797 30798 30799 30800 30801 30802 30803 30804 30805 30806 30807 30808 30809 30810 30811 30812 30813 30814 30815 30816 30817 30818 30819 30820 30821 30822 30823 30824 30825 30826 30827 30828 30829 30830 30831 30832 30833 30834 30835 30836 30837 30838 30839 30840 |
response to save bandwidth, in TSQLRestServerURIContext.Returns/Results - added TSQLRestServerURIContext.ReturnFile() and ReturnFileFromFolder() methods, for direct fast transmission to a HTTP client, handling "304 Not Modified" and proper mime type recognition - added TSQLRestServerURIContext.Input*OrVoid[] properties - added TSQLRestServerURIContext.SessionRemoteIP, SessionConnectionID, SessionUserName and ResourceFileName properties - added TSQLRestServerURIContext.Redirect() method for HTTP 301 commands - added TSQLRestServer.ServiceMethodRegister() low-level method - added TSQLRestServer.ServiceMethodRegisterPublishedMethods() to allow multi-class method-based services (e.g. for implementing MVC model) - ServiceContext threadvar will now be set in all ORM and SOA process, to allow access to the execution context - to make the implicit explicit, TSQLRestServerURIContext.ID has been ................................................................................ // some characters at decoding from UTF-8 input buffer // - returns Unassigned if the parameter is not found property InputOrVoid[const ParamName: RawUTF8]: variant read GetInputOrVoid; /// retrieve all input paramters from URI as a variant JSON object // - returns Unassigned if no parameter was defined property InputAsTDocVariant: variant read GetInputAsTDocVariant; {$endif} /// retrieve an incoming HTTP header // - the supplied header name is case-insensitive // - you could call e.g. InHeader['remoteip'] to retrieve the caller IP property InHeader[const HeaderName: RawUTF8]: RawUTF8 read GetInHeader; /// retrieve an incoming HTTP cookie value // - the supplied cookie name is case-insensitive property InCookie[CookieName: RawUTF8]: RawUTF8 read GetInCookie write SetInCookie; ................................................................................ exit; // bad ModelRoot -> caller can try another TSQLRestServer end; URI := copy(Call^.url,j+i+2,maxInt); // trim any '/root/' left side of Ctxt.URI ParametersPos := PosEx(RawUTF8('?'),Call^.url,1); if ParametersPos>0 then // '?select=...&where=...' or '?where=...' Parameters := @Call^.url[ParametersPos+1] else if Method=mPost then begin fInputPostContentType := FindIniNameValue(pointer(Call.InHead),HEADER_CONTENT_TYPE_UPPER); if IdemPChar(pointer(fInputPostContentType),'APPLICATION/X-WWW-FORM-URLENCODED') then Parameters := pointer(Call^.InBody); end; // compute Table, TableID and URIBlobFieldName i := PosEx(RawUTF8('/'),URI,1); if i>0 then begin Par := @URI[i+1]; ................................................................................ end; function TSQLRestServerURIContext.GetInputOrVoid(const ParamName: RawUTF8): variant; begin GetVariantFromJSON(pointer(GetInputUTF8OrVoid(ParamName)),false,Result); end; function MultiPartFormDataDecode(const MimeType,Body: RawUTF8; var Names,ContentTypes: TRawUTF8DynArray; var Values: TRawByteStringDynArray): boolean; var boundary: RawUTF8; i: integer; begin result := false; i := PosEx('boundary=',MimeType); if i=0 then exit; boundary := '--'+trim(copy(MimeType,i+9,200)); // to be done raise ECommunicationException.Create('multipart/form-data not implemented yet'); end; function TSQLRestServerURIContext.GetInputAsTDocVariant: variant; var i: integer; v: variant; Names,ContentTypes: TRawUTF8DynArray; Values: TRawByteStringDynArray; // Variants: TVariantDynArray; begin VarClear(result); FillInput; if fInput<>nil then begin with TDocVariantData(result) do begin Init(JSON_OPTIONS[true]); for i := 0 to (length(fInput) shr 1)-1 do begin GetVariantFromJSON(pointer(fInput[i*2+1]),false,v,@JSON_OPTIONS[true]); AddValue(fInput[i*2],v); end; end; end else if (Method=mPOST) and IdemPChar(pointer(fInputPostContentType),'MULTIPART/FORM-DATA') and MultiPartFormDataDecode(fInputPostContentType,Call^.InBody,Names,ContentTypes,Values) then begin //TDocVariantData(result).InitObjectFromVariants(Names,Variants); end; end; {$endif NOVARIANTS} function TSQLRestServerURIContext.GetInHeader(const HeaderName: RawUTF8): RawUTF8; var up: array[byte] of AnsiChar; begin PWord(UpperCopy255(up,HeaderName))^ := ord(':'); result := Trim(FindIniNameValue(pointer(Call.InHead),up)); end; |
> > > > > | < < < < < < < < < < < < < < | < < | < < | > > > > > | > > > > > > > > |
790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 .... 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 ..... 30017 30018 30019 30020 30021 30022 30023 30024 30025 30026 30027 30028 30029 30030 30031 30032 ..... 30792 30793 30794 30795 30796 30797 30798 30799 30800 30801 30802 30803 30804 30805 30806 30807 30808 30809 30810 30811 30812 30813 30814 30815 30816 30817 30818 30819 30820 30821 30822 30823 30824 30825 30826 30827 30828 30829 30830 30831 30832 30833 30834 30835 30836 30837 30838 30839 30840 |
response to save bandwidth, in TSQLRestServerURIContext.Returns/Results - added TSQLRestServerURIContext.ReturnFile() and ReturnFileFromFolder() methods, for direct fast transmission to a HTTP client, handling "304 Not Modified" and proper mime type recognition - added TSQLRestServerURIContext.Input*OrVoid[] properties - added TSQLRestServerURIContext.SessionRemoteIP, SessionConnectionID, SessionUserName and ResourceFileName properties - added TSQLRestServerURIContext.InputAsMultiPart() method - added TSQLRestServerURIContext.Redirect() method for HTTP 301 commands - added TSQLRestServer.ServiceMethodRegister() low-level method - added TSQLRestServer.ServiceMethodRegisterPublishedMethods() to allow multi-class method-based services (e.g. for implementing MVC model) - ServiceContext threadvar will now be set in all ORM and SOA process, to allow access to the execution context - to make the implicit explicit, TSQLRestServerURIContext.ID has been ................................................................................ // some characters at decoding from UTF-8 input buffer // - returns Unassigned if the parameter is not found property InputOrVoid[const ParamName: RawUTF8]: variant read GetInputOrVoid; /// retrieve all input paramters from URI as a variant JSON object // - returns Unassigned if no parameter was defined property InputAsTDocVariant: variant read GetInputAsTDocVariant; {$endif} /// decode any multipart/form-data POST request input // - returns TRUE and set MultiPart array as expected, on success function InputAsMultiPart(var MultiPart: TMultiPartDynArray): Boolean; /// retrieve an incoming HTTP header // - the supplied header name is case-insensitive // - you could call e.g. InHeader['remoteip'] to retrieve the caller IP property InHeader[const HeaderName: RawUTF8]: RawUTF8 read GetInHeader; /// retrieve an incoming HTTP cookie value // - the supplied cookie name is case-insensitive property InCookie[CookieName: RawUTF8]: RawUTF8 read GetInCookie write SetInCookie; ................................................................................ exit; // bad ModelRoot -> caller can try another TSQLRestServer end; URI := copy(Call^.url,j+i+2,maxInt); // trim any '/root/' left side of Ctxt.URI ParametersPos := PosEx(RawUTF8('?'),Call^.url,1); if ParametersPos>0 then // '?select=...&where=...' or '?where=...' Parameters := @Call^.url[ParametersPos+1] else if Method=mPost then begin fInputPostContentType := FindIniNameValue( pointer(Call.InHead),HEADER_CONTENT_TYPE_UPPER); if IdemPChar(pointer(fInputPostContentType),'APPLICATION/X-WWW-FORM-URLENCODED') then Parameters := pointer(Call^.InBody); end; // compute Table, TableID and URIBlobFieldName i := PosEx(RawUTF8('/'),URI,1); if i>0 then begin Par := @URI[i+1]; ................................................................................ end; function TSQLRestServerURIContext.GetInputOrVoid(const ParamName: RawUTF8): variant; begin GetVariantFromJSON(pointer(GetInputUTF8OrVoid(ParamName)),false,Result); end; function TSQLRestServerURIContext.GetInputAsTDocVariant: variant; var i: integer; v: variant; MultiPart: TMultiPartDynArray; begin VarClear(result); FillInput; if fInput<>nil then begin with TDocVariantData(result) do begin Init(JSON_OPTIONS[true]); for i := 0 to (length(fInput) shr 1)-1 do begin GetVariantFromJSON(pointer(fInput[i*2+1]),false,v,@JSON_OPTIONS[true]); AddValue(fInput[i*2],v); end; end; end else if InputAsMultiPart(MultiPart) then with TDocVariantData(result) do begin Init(JSON_OPTIONS[true]); for i := 0 to high(MultiPart) do with MultiPart[i] do begin RawUTF8ToVariant(Content,v); AddValue(Name,v); end; end; end; {$endif NOVARIANTS} function TSQLRestServerURIContext.InputAsMultiPart(var MultiPart: TMultiPartDynArray): Boolean; begin result := (Method=mPOST) and IdemPChar(pointer(fInputPostContentType),'MULTIPART/FORM-DATA') and MultiPartFormDataDecode(fInputPostContentType,Call^.InBody,MultiPart); end; function TSQLRestServerURIContext.GetInHeader(const HeaderName: RawUTF8): RawUTF8; var up: array[byte] of AnsiChar; begin PWord(UpperCopy255(up,HeaderName))^ := ord(':'); result := Trim(FindIniNameValue(pointer(Call.InHead),up)); end; |
Changes to SynCommons.pas.
3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 ..... 24318 24319 24320 24321 24322 24323 24324 24325 24326 24327 24328 24329 24330 24331 24332 ..... 24336 24337 24338 24339 24340 24341 24342 24343 24344 24345 24346 24347 24348 24349 24350 24351 24352 24353 24354 24355 24356 24357 24358 24359 24360 24361 24362 24363 24364 24365 24366 24367 24368 24369 24370 24371 24372 24373 |
type /// used by MultiPartFormDataDecode() to return one item of its data TMultiPart = record Name: RawUTF8; FileName: RawUTF8; ContentType: RawUTF8; Content: RawByteString; end; /// used by MultiPartFormDataDecode() to return all its data items TMultiPartDynArray = array of TMultiPart; /// decode multipart/form-data POST request content // - following RFC1867 ................................................................................ result := ExistsIniNameValue(Headers,HEADER_CONTENT_TYPE_UPPER, ['TEXT/','APPLICATION/JSON','APPLICATION/XML', 'APPLICATION/X-JAVASCRIPT','IMAGE/SVG+XML']); end; function MultiPartFormDataDecode(const MimeType,Body: RawUTF8; var MultiPart: TMultiPartDynArray): boolean; var boundary,encoding: RawUTF8; i,j: integer; P: PUTF8Char; part: TMultiPart; begin result := false; i := PosEx('boundary=',MimeType); if i=0 then ................................................................................ if i<>0 then repeat inc(i,length(boundary)); if i=length(body) then exit; // reached the end P := PUTF8Char(Pointer(Body))+i-1; Finalize(part); encoding := ''; repeat if IdemPCharAndGetNextItem(P,'CONTENT-DISPOSITION: FORM-DATA; NAME="', part.Name,'"') then IdemPCharAndGetNextItem(P,'; FILENAME="',part.FileName,'"') else if IdemPCharAndGetNextItem(P,'CONTENT-TYPE: ',part.ContentType) or IdemPCharAndGetNextItem(P,'CONTENT-TRANSFER-ENCODING: ',encoding) then; GetNextLineBegin(P,P); if P=nil then exit; until PWord(P)^=13+10 shl 8; i := P-PUTF8Char(Pointer(Body))+3; // i = just after header j := PosEx(Body,boundary,i); if j=0 then exit; part.Content := copy(Body,i,j-i); if (encoding<>'7bit') and (encoding<>'8bit') and (encoding<>'binary') then if encoding='base64' then part.Content := Base64ToBin(part.Content) else exit; // unknown encoding - e.g. "quoted-printable" SetLength(MultiPart,length(MultiPart)+1); MultiPart[high(MultiPart)] := part; result := true; i := j; until false; end; function FastLocatePUTF8CharSorted(P: PPUTF8CharArray; R: PtrInt; Value: PUTF8Char): PtrInt; var L,i,cmp: PtrInt; begin if R<0 then |
> | < | | | | | | | | | > | > > | | < | | | | |
3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 ..... 24319 24320 24321 24322 24323 24324 24325 24326 24327 24328 24329 24330 24331 24332 24333 ..... 24337 24338 24339 24340 24341 24342 24343 24344 24345 24346 24347 24348 24349 24350 24351 24352 24353 24354 24355 24356 24357 24358 24359 24360 24361 24362 24363 24364 24365 24366 24367 24368 24369 24370 24371 24372 24373 24374 24375 |
type /// used by MultiPartFormDataDecode() to return one item of its data TMultiPart = record Name: RawUTF8; FileName: RawUTF8; ContentType: RawUTF8; Encoding: RawUTF8; Content: RawByteString; end; /// used by MultiPartFormDataDecode() to return all its data items TMultiPartDynArray = array of TMultiPart; /// decode multipart/form-data POST request content // - following RFC1867 ................................................................................ result := ExistsIniNameValue(Headers,HEADER_CONTENT_TYPE_UPPER, ['TEXT/','APPLICATION/JSON','APPLICATION/XML', 'APPLICATION/X-JAVASCRIPT','IMAGE/SVG+XML']); end; function MultiPartFormDataDecode(const MimeType,Body: RawUTF8; var MultiPart: TMultiPartDynArray): boolean; var boundary: RawUTF8; i,j: integer; P: PUTF8Char; part: TMultiPart; begin result := false; i := PosEx('boundary=',MimeType); if i=0 then ................................................................................ if i<>0 then repeat inc(i,length(boundary)); if i=length(body) then exit; // reached the end P := PUTF8Char(Pointer(Body))+i-1; Finalize(part); repeat if IdemPCharAndGetNextItem(P, 'CONTENT-DISPOSITION: FORM-DATA; NAME="',part.Name,'"') then IdemPCharAndGetNextItem(P,'; FILENAME="',part.FileName,'"') else if IdemPCharAndGetNextItem(P,'CONTENT-TYPE: ',part.ContentType) or IdemPCharAndGetNextItem(P,'CONTENT-TRANSFER-ENCODING: ',part.Encoding) then; GetNextLineBegin(P,P); if P=nil then exit; until PWord(P)^=13+10 shl 8; i := P-PUTF8Char(Pointer(Body))+3; // i = just after header j := PosEx(Body,boundary,i); if j=0 then exit; part.Content := copy(Body,i,j-i-2); // -2 to ignore latest #13#10 {$ifdef UNICODE} if part.ContentType='' then SetCodePage(part.Content,CP_UTF8) else // ensure raw field value is UTF-8 {$endif} if part.Encoding='base64' then // "quoted-printable" not yet handled here part.Content := Base64ToBin(part.Content); SetLength(MultiPart,length(MultiPart)+1); MultiPart[high(MultiPart)] := part; result := true; i := j; until false; end; function FastLocatePUTF8CharSorted(P: PPUTF8CharArray; R: PtrInt; Value: PUTF8Char): PtrInt; var L,i,cmp: PtrInt; begin if R<0 then |
Changes to SynopseCommit.inc.
1 |
'1.18.922'
|
| |
1 |
'1.18.923'
|