277 lines
7.2 KiB
ObjectPascal
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.
|
|
|