xtool/contrib/mORMot/SyNode/SyNodeBinding_HTTPClient.pas

277 lines
7.2 KiB
ObjectPascal

/// `http` module support bindings for SyNode
// TODO - current implemenattion is not filly compartible with nodeJS
// - this unit is a part of the freeware Synopse framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
//
// Contributor(s):
// - Pavel Mashlyakovsky
unit SyNodeBinding_HTTPClient;
interface
{$I Synopse.inc}
{$I SyNode.inc}
uses
SysUtils, SynCrtSock, SynCommons, SpiderMonkey;
type
{$M+}
THTTPClient = class
private
fClient: THttpRequest;
fInHeaders: RawUTF8;
FMethod: RawUTF8;
FWriter: TTextWriter;
FInData: RawByteString;
FRespHeaders: SockString;
FRespText: SockString;
FKeepAlive: Cardinal;
FResponseStatus: integer;
fConnectTimeout: integer;
fSendTimeout: integer;
fReceiveTimeout: integer;
protected
function GetRespText: RawUTF8;
function GetRespHeaders: RawUTF8;
function sendFile(const URL: RawUTF8; aFileName: string): boolean;
public
destructor Destroy; override;
published
// for timeout explain see http://msdn.microsoft.com/en-us/library/windows/desktop/aa384116%28v=vs.85%29.aspx
constructor Create();
function initialize(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
function write(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
function writeEnd(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
function read(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
//function doRequest(const URL: RawUTF8; const ContentType: RawUTF8 = ''): boolean;
function doRequest(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
property method: RawUTF8 read FMethod write FMethod;
property keepAlive: cardinal read FKeepAlive write FKeepAlive default 1;
property headers: RawUTF8 read fInHeaders write fInHeaders;
property responseText: RawUTF8 read GetRespText;
property responseHeaders: RawUTF8 read GetRespHeaders;
property responseStatus: integer read FResponseStatus;
end;
{$M-}
implementation
uses
SynZip, SyNode, SyNodeSimpleProto, SyNodeReadWrite;
{ THTTPClient }
constructor THTTPClient.Create();
begin
inherited Create;
FMethod := 'POST';
FKeepAlive := 1;
end;
function THTTPClient.initialize(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
var
in_argv: PjsvalVector;
aServer, aPort: AnsiString;
aHttps: boolean;
aProxyName, aProxyByPass: RawUTF8;
begin
in_argv := vp.argv;
Result := true;
try
if (fClient <> nil) then raise ESMException.Create('already initialized');
if (argc < 2) or (not in_argv[0].isString) or (not in_argv[1].isString) then
raise ESMException.Create('invalid usage');
aServer := in_argv[0].asJSString.ToAnsi(cx);
aPort := in_argv[1].asJSString.ToAnsi(cx);
if (argc > 2) and (in_argv[2].isBoolean) then
aHttps := in_argv[2].asBoolean
else
aHttps := false;
if (argc > 4) and (in_argv[4].isString) then
aProxyName := in_argv[4].asJSString.ToUTF8(cx)
else
aProxyName := '';
if (argc > 5) and (in_argv[5].isString) then
aProxyByPass := in_argv[5].asJSString.ToUTF8(cx)
else
aProxyByPass := '';
if (argc > 6) and (in_argv[6].isInteger) then
fConnectTimeout := in_argv[6].asInteger
else
fConnectTimeout := HTTP_DEFAULT_CONNECTTIMEOUT;
if (argc > 7) and (in_argv[7].isInteger) then
fSendTimeout := in_argv[7].asInteger
else
fSendTimeout := HTTP_DEFAULT_SENDTIMEOUT;
if (argc > 8) and (in_argv[8].isInteger) then
fReceiveTimeout := in_argv[8].asInteger
else
fReceiveTimeout := HTTP_DEFAULT_RECEIVETIMEOUT;
fClient := {$IFDEF MSWINDOWS}TWinHTTP{$ELSE}TCurlHTTP{$ENDIF}
.Create(aServer, aPort, aHttps, aProxyName, aProxyByPass, fConnectTimeout, fSendTimeout, fReceiveTimeout);
if (argc > 3) and (in_argv[3].isBoolean) then
fClient.RegisterCompress(CompressGZip);
if aHttps then
fClient.IgnoreSSLCertificateErrors := true;
except
on E: Exception do begin
Result := False;
JSError(cx, E);
end;
end;
end;
destructor THTTPClient.Destroy;
begin
fClient.Free;
inherited Destroy;
end;
//function THTTPClient.doRequest(const URL: RawUTF8; const ContentType: RawUTF8 = ''): boolean;
function THTTPClient.doRequest(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
var
URL: SockString;
in_argv: PjsvalVector;
begin
FRespHeaders := '';
FRespText := '';
in_argv := vp.argv;
Result := true;
try
if (argc <> 1) or (not in_argv[0].isString) then
raise ESMException.Create('usage doRequest(URL: string)');
URL := in_argv[0].asJSString.ToAnsi(cx);
try
FResponseStatus := fClient.Request(URL, FMethod, keepAlive, fInHeaders, FInData, '', FRespHeaders, FRespText);
except
on E: EOSError do
FResponseStatus := E.ErrorCode;
end;
except
on E: Exception do begin
Result := False;
JSError(cx, E);
end;
end;
FInData := '';
FInHeaders := '';
end;
function THTTPClient.read(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
begin
Result := true;
try
vp.rval := SyNodeReadWrite.SMRead_impl(cx, argc, vp.argv, FRespText);
except
on E: Exception do begin
Result := False;
JSError(cx, E);
end;
end;
end;
//function THTTPClient.request(const URL, data: RawUTF8): boolean;
//begin
// FInData := data;
// Result := doRequest(URL);
//end;
function THTTPClient.sendFile(const URL: RawUTF8; aFileName: string): boolean;
var
buffer: RawByteString;
begin
FResponseStatus := 500;
try
if not FileExists(aFileName) then begin
FRespText := 'File not found';
FResponseStatus := 404;
end;
try
buffer := StringFromFile(aFileName);
FResponseStatus := fClient.Request(URL, FMethod, FKeepAlive, fInHeaders, buffer, '', FRespHEaders, FRespText);
except
on E: EOSError do begin
FResponseStatus := E.ErrorCode;
FRespText := StringToUTF8(E.Message);
end;
end;
finally
Result := (FResponseStatus = 200);
end;
end;
function THTTPClient.write(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
begin
if (FWriter = nil) then
FWriter := TTextWriter.CreateOwnedStream;
Result := True;
try
vp.rval := SyNodeReadWrite.SMWrite_impl(cx, argc, vp.argv, FWriter);
except
on E: Exception do begin
Result := False;
JSError(cx, E);
end;
end;
end;
function THTTPClient.writeEnd(cx: PJSContext; argc: uintN; var vp: JSArgRec): Boolean;
var
data: RawUTF8;
begin
Result := True;
if (argc > 0 ) then
Result := write(cx, argc, vp);
if Result then
FWriter.SetText(data);
FInData := data;
FreeAndNil(FWriter);
end;
function THTTPClient.GetRespText: RawUTF8;
begin
Result := FRespText;
end;
function THTTPClient.GetRespHeaders: RawUTF8;
begin
Result := FRespHeaders;
end;
function SyNodeBindingProc_synode_http(const aEngine: TSMEngine; const bindingNamespaceName: SynUnicode): jsval;
var
obj: PJSRootedObject;
cx: PJSContext;
begin
cx := aEngine.cx;
obj := cx.NewRootedObject(cx.NewObject(nil));
try
aEngine.defineClass(THTTPClient, TSMSimpleRTTIProtoObject, obj);
Result := obj.ptr.ToJSValue;
finally
cx.FreeRootedObject(obj);
end;
end;
initialization
TSMEngineManager.RegisterBinding('synode_http', SyNodeBindingProc_synode_http);
end.