Kas
RTC Expired
Posts: 17
|
|
« Reply #15 on: July 25, 2018, 04:23:22 PM » |
|
This will work too if i kept a list GetConnection and called them with : procedure WakeUpByConnection(conn:TRtcConnection); var ob:TObject; i:RtcIntPtr; begin CS.Acquire; try if assigned(List) then begin i:=List.search_min(ob); while assigned(ob) do begin if (ob is TRtcDelayedCall)then if TRtcDelayedCall(ob).Conn=conn then TRtcDelayedCall(ob).WakeUp; i:=List.search_g(i, ob); end; end; finally CS.Release; end; end;
|
|
|
Logged
|
|
|
|
D.Tkalcec (RTC)
|
|
« Reply #16 on: July 25, 2018, 04:36:10 PM » |
|
Since (A) all delayed call objects are kept in a single global list and (B) the only reason to force a wake-up call on all delayed call objects would be to make sure that blocked threads waiting for a wake-up call get "unblocked" before the application terminates, I think that adding a global "WakeUpAllDelayedCalls" procedure is the simplest solution to this problem, so ... I'm going to add it to the "rtcSrvModule.pas" unit in the next RTC SDK update.
|
|
|
Logged
|
|
|
|
Kas
RTC Expired
Posts: 17
|
|
« Reply #17 on: July 25, 2018, 04:56:15 PM » |
|
Thank you Danijel, that is great. But may adding (B) will be helpful too ! if TRtcMessengerServer kept a list of only delayed connections and be used like this procedure TRtcMessageServer.ProcessData(aRequest, aReply: TStream); begin EnterEvent; try FConList.Add(Con); TMyProvider(Con).ExecuteRequest(aRequest, aReply); FConList.Remove(Con); finally LeaveEvent; end; end;
Then TRtcMessageServer can wake only the blocked calls, calling (B) from TRtcMessengerServer Disconnect and Destroy will resolve all the specific problem that comes with TRtcMessengerServer simulations. and (A) is helpful in all. Thank you again ,i see you already updated the version, while i still writing a post !, and that is awesome.
|
|
|
Logged
|
|
|
|
D.Tkalcec (RTC)
|
|
« Reply #18 on: July 25, 2018, 05:02:00 PM » |
|
Even though delayed call objects need a reference to the Connection object from which they were initiated, Connection components (like TRtcMessageServer) do NOT have any knowledge about Delayed Calls. Delayed Call objects are implemented at a higher level and used ONLY by the "TRtcServerModule" component.
Anyway ... I've released the update with the "WakeUpAllDelayedCalls" procedure in the "rtcSrvModule.pas" unit. Since this is also the unit where the "TRtcServerModule" component is implemented, it will be in the "uses" clause of any unit implementing server-side remote functions, so calling "WakeUpAllDelayedCalls" from the "finalize" section of a unit where delayed calls are used would probably be the "cleanest" solution.
|
|
|
Logged
|
|
|
|
Kas
RTC Expired
Posts: 17
|
|
« Reply #19 on: July 25, 2018, 05:11:30 PM » |
|
Thank you very much, that is enough. I will prepare an example ( simple and easy ) and post it here.
|
|
|
Logged
|
|
|
|
Kas
RTC Expired
Posts: 17
|
|
« Reply #20 on: July 25, 2018, 06:21:20 PM » |
|
unit Main;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, rtcConn, rtcSrvModule, rtcPortalGate, rtcSystem, rtcInfo, rtcMsgSrv, rtcTcpSrv, Vcl.StdCtrls, rtcDataSrv;
type TForm6 = class(TForm) RtcMessageServer1: TRtcMessageServer; RtcTcpServer1: TRtcTcpServer; Gateway: TRtcPortalGateway; procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure FormCreate(Sender: TObject); procedure RtcMessageServer1Disconnect(Sender: TRtcConnection); procedure RtcTcpServer1Connect(Sender: TRtcConnection); procedure RtcTcpServer1DataReceived(Sender: TRtcConnection); procedure RtcTcpServer1Disconnect(Sender: TRtcConnection); private public end;
var Form6: TForm6;
implementation
{$R *.dfm}
procedure TForm6.FormClose(Sender: TObject; var Action: TCloseAction); begin RtcTcpServer1.StopListen; RtcMessageServer1.StopListen; WakeUpAllDelayedCalls; RtcTcpServer1.Disconnect; end;
procedure TForm6.FormCreate(Sender: TObject); begin RtcMessageServer1.Listen; RtcTcpServer1.Listen; end;
procedure TForm6.RtcMessageServer1Disconnect(Sender: TRtcConnection); begin TRtcConnection(Sender.Info.asObj['parCon']).Disconnect; Sender.Info.asObj['parCon'] := nil; end;
procedure TForm6.RtcTcpServer1Connect(Sender: TRtcConnection); var Conn: TRtcMessageServer; begin Conn := TRtcMessageServer(RtcMessageServer1.GetConnection); Conn.Info.asObj['parCon'] := Sender; Sender.Info.asObj['con'] := Conn; Sender.Info.asObj['req'] := TMemoryStream.Create; Sender.Info.asObj['res'] := TMemoryStream.Create; Sender.Info.asInteger['reqsize'] := 0; end;
procedure TForm6.RtcTcpServer1DataReceived(Sender: TRtcConnection); const CONTENT_LEN = 'CONTENT-LENGTH:'; CRLF = #13#10;
function GetRequestSize(buf: RtcByteArray): Integer; var st, ss: RtcString; p, ct: Integer; begin Result := 0; p := PosEx(CRLF + CRLF, buf); if p > -1 then begin st := Upper_Case(RtcBytesZeroToString(buf, 0, p + 3)); ct := PosEx(CONTENT_LEN, st); if ct > 0 then ss := RtcBytesZeroToString(buf, ct + Length(CONTENT_LEN), PosEx(CRLF, st, ct) - (ct + Length(CONTENT_LEN)) - 1) else Exit; Result := Str2IntDef(ss, 0) + Length(st); end; end;
var buf: RtcByteArray; aReq, aAns: TMemoryStream; rs: Integer; conn: TRtcMessageServer; begin if not RtcMessageServer1.isListening then begin Sender.Disconnect; Exit; end;
aReq := TMemoryStream(Sender.Info.asObj['req']); buf := Sender.ReadEx; aReq.WriteBuffer(buf[0], Length(buf));
rs := Sender.Info.asInteger['reqsize']; if rs = 0 then begin rs := GetRequestSize(buf); if rs = 0 then begin Sender.Disconnect; Exit; end; Sender.Info.asInteger['reqsize'] := rs; end;
if rs <> aReq.Size then begin if rs < aReq.Size then Sender.Disconnect; Exit; end;
aReq.Position := 0; aAns := TMemoryStream(Sender.Info.asObj['res']); conn := TRtcMessageServer(Sender.Info.asObj['con']); conn.ProcessData(aReq, aAns); if aAns.Size > 0 then begin aAns.Position := 0; SetLength(buf, aAns.Size); aAns.ReadBuffer(buf[0], aAns.Size); if not Application.Terminated then Sender.WriteEx(buf); aAns.Clear; aReq.Clear; Sender.Info.asInteger['reqsize'] := 0; end else begin Sender.Disconnect; Exit; end; end;
procedure TForm6.RtcTcpServer1Disconnect(Sender: TRtcConnection); begin RtcMessageServer1.PutConnection(Sender.Info.asObj['con']); Sender.Info.asObj['con'] := nil; TMemoryStream(Sender.Info.asObj['req']).Free; Sender.Info.asObj['req'] := nil; TMemoryStream(Sender.Info.asObj['res']).Free; Sender.Info.asObj['res'] := nil; end;
end.
Things to know about this example: RTC ProcessData does support partial request , but i kept the code above as an example. It assume the HTTP header (only the header not the whole request ) is not fragmented or will disconnect , this should not be a problem as fragmentation is very unlikely for the first MTU size (around 1500 bytes) when sending, so while the header size is less than MTU we are safe .
|
|
|
Logged
|
|
|
|
D.Tkalcec (RTC)
|
|
« Reply #21 on: July 25, 2018, 07:06:12 PM » |
|
Ignore my question about your example code (question deleted). I've just realized that you are calculating the size of the complete request stream (including request headers) in the "rs" variable and not just the request content body.
|
|
|
Logged
|
|
|
|
Kas
RTC Expired
Posts: 17
|
|
« Reply #22 on: July 25, 2018, 07:11:03 PM » |
|
rs is the request size in full ( header and content length) ,if received length is more than one request then this is not standard HTTP protocol ( 1.0 and 1.1 ) and should disconnect , right ? Because ProcessData is simple procedure and doesn't return if the connection should be dropped or how seek the request stream to the full request been received currently ProcessData will eat the extra data and do nothing.
Try this aReq.WriteBuffer(buf[0], 1); aReq.Position := 0; aAns := TMemoryStream(Sender.Info.asObj['res']); conn := TRtcMessageServer(Sender.Info.asObj['con']); conn.ProcessData(aReq, aAns);
it will work like nothing happen.
|
|
|
Logged
|
|
|
|
D.Tkalcec (RTC)
|
|
« Reply #23 on: July 25, 2018, 07:17:32 PM » |
|
OK, so it's a quick "request format" check.
|
|
|
Logged
|
|
|
|
Kas
RTC Expired
Posts: 17
|
|
« Reply #24 on: July 25, 2018, 07:24:44 PM » |
|
Yes exactly. This will do the job, while waiting for your RTC SDK to support HTTP2 with full (http pipelining)
|
|
|
Logged
|
|
|
|
D.Tkalcec (RTC)
|
|
« Reply #25 on: July 25, 2018, 07:49:26 PM » |
|
I'm not sure I follow. What do you mean by "waiting for your RTC SDK to support HTTP2"? You know that HTTP/2 is only going to be implemented on HTTP connection components (TRtcHttpServer and TRtcHttpClient). Right?
|
|
|
Logged
|
|
|
|
Kas
RTC Expired
Posts: 17
|
|
« Reply #26 on: July 25, 2018, 08:21:11 PM » |
|
I imagined the feeding mechanism for requests as stream and the way delayed call internally will be huge change, and this will affect all components including RtcMessageServer, i imagined you will centralize the request handling code ( HTTP1 and HTTP2 ) and use it cross components components, i thought you will let the TRtcRequest build itself from the data received or a special builder class to help with HTTP2 pipelining, but it seems you will build it only for HTTP server and client. In all cases i wish you good luck.
|
|
|
Logged
|
|
|
|
|