Indy HTTPserver TIdHTTPServer Session problem lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- epneeh · 2018-08-27 04:57 Hi, I'm creating Http Server using TidHTTPServer which properties : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i http.AutoSt artSession:=True;http.SessionState:=True;http.KeepAlive:=true;  i'm using webpage as a frontend to the client and fully using AJAX for transaction.. and authentication using IndyHttp server built in session management. my question is, why if client request to server using diferent Internet Provider or my internet provider request using different IP than the first log-in, indy attempt to change Session Cookie ID? and how to prevent that? sorry for my bad english. ---------------------------------------------------------------------- Remy Lebeau · 2018-08-30 19:29 Quote from: epneeh on August 27, 2018, 06:57:11 amwhy if client request to server using diferent Internet Provider or my internet provider request using different IP than the first log-in, indy attempt to change Session Cookie ID? By default, TIdHTTPServer tracks sessions by a combination of cookie value and IP address.  So, even if the client has an existing cookie from an earlier request, if the client's IP changes afterwards, a subsequent request from the new IP will create a new session, and the old session will eventually timeout. Quote from: epneeh on August 27, 2018, 06:57:11 amhow to prevent that? If you don't want to track sessions by IP, you can derive a class from TIdHTTPCustomSessionList and implement its abstract methods to track cookies however you want, and then you can assign an instance of that class to the TIdHTTPServer.SessionList property before activating the server. ---------------------------------------------------------------------- epneeh · 2018-09-05 13:48 Quote from: Remy Lebeau on August 30, 2018, 09:29:30 pmBy default, TIdHTTPServer tracks sessions by a combination of cookie value and IP address.  So, even if the client has an existing cookie from an earlier request, if the client's IP changes afterwards, a subsequent request from the new IP will create a new session, and the old session will eventually timeout. If you don't want to track sessions by IP, you can derive a class from TIdHTTPCustomSessionList and implement its abstract methods to track cookies however you want, and then you can assign an instance of that class to the TIdHTTPServer.SessionList property before activating the server. Hi remy, i was expecting your answer... so i managed to disabled track session by IP by editing idCustomHTTPServer : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function TIdHTTPDefaultSessionList.GetSession(const SessionID, RemoteIP: string): TIdHTTPSession;var  LSessionList: TIdHTTPSessionList;  LSession: TIdHTTPSession;  i: Integer;begin  Result := nil;  LSessionList := SessionList.LockList;  try    // get current time stamp    for i := 0 to LSessionList.Count - 1 do    begin      LSession := TIdHTTPSession(LSessionList[i]);      // the stale sessions check has been removed... the cleanup thread should suffice plenty      if Assigned(LSession) and TextIsSame(LSession.FSessionID, SessionID) then {edited from here} //and ((Length(RemoteIP) = 0) or TextIsSame(LSession.RemoteHost, RemoteIP)) then      begin        // Session found        // TODO: use a timer to signal when the session becomes stale, instead of        // pooling for stale sessions every second...        LSession.FLastTimeStamp := Now;        Result := LSession;        Break;      end;    end;  finally    SessionList.UnlockList;  end;end;  i know its not the best practice, but i need fast solution back there.. maybe going to try your solution by derive TIdHTTPCustomSessionList class later... but there's another problem, when a GET request attempt from client the httpCommandGet method firing repeatedly like 4 or 5 times, so in what scenario did this happen? UPDATE ok so i did a little research this is my code : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i aresponsein fo.ContentType:='application/pdf';aresponseinfo.ContentDisposition:='a ttachment; filename="' + get_nip_from_id(arequestinfo.Params.Values['p']) + '.pdf"';aresponsein fo.ContentStream:=generate_report(arequestinfo.Params.Values['p']);are sponseinfo.WriteContent;aresponseinfo.ContentStream:=nil;  if i comment "//" contentdisposition it went okay... but my response to client will be different from what i expected. ---------------------------------------------------------------------- Remy Lebeau · 2018-09-05 17:03 Quote from: epneeh on September 05, 2018, 03:48:37 pmso i managed to disabled track session by IP by editing idCustomHTTPServer You edited the implementation of the TIdHTTPDefaultSessionList class, which is used only when you DON'T provide a custom class to replace it.  So why didn't you simply derive a new class from TIdHTTPCustomSessionList, like I suggested earlier?  Then you could have overridden the GetSession() to ignore the RemoteIP parameter, and not have to edit Indy itself at all.  For example: Code: [Select]type   TMyHTTPSessionList = class(TIdHTTPDefaultSessionList)   public     function GetSession(const SessionID, RemoteIP: string): TIdHTTPSession; override;   end; function TMyHTTPSessionList.GetSession(const SessionID, RemoteIP: string): TIdHTTPSession; begin   Result := inherited GetSession(SessionID, ''); end; ... IdHTTPServer1.SessionList := TMyHTTPSessionList.Create(IdHTTPServer1); IdHTTPServer1.Active := True; Quote from: epneeh on September 05, 2018, 03:48:37 pmi know its not the best practice, but i need fast solution back there.. It would have been faster and easier to write a custom class directly in your project code and assign it to TIdHTTPServer, rather than editing, recompiling, and reinstalling Indy itself. Quote from: epneeh on September 05, 2018, 03:48:37 pmbut there's another problem, when a GET request attempt from client the httpCommandGet method firing repeatedly like 4 or 5 times, so in what scenario did this happen? I can't answer that without seeing the client's traffic.  Obviously it is making multiple requests to your server.  You will have to look at the details of each request to see exactly what it is really requesting. Quote from: epneeh on September 05, 2018, 03:48:37 pmok so i did a little research this is my code : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i aresponsein fo.ContentType:='application/pdf';aresponseinfo.ContentDisposition:='a ttachment; filename="' + get_nip_from_id(arequestinfo.Params.Values['p']) + '.pdf"';aresponsein fo.ContentStream:=generate_report(arequestinfo.Params.Values['p']);are sponseinfo.WriteContent;aresponseinfo.ContentStream:=nil; You don't need the last 2 lines (especially since the last one is causing a memory leak).  Simply assign the ContentStream and then exit the OnCommandGet event handler.  TIdHTTPServer will then transmit the data for you, and free the stream when finished. Quote from: epneeh on September 05, 2018, 03:48:37 pmif i comment "//" contentdisposition it went okay... but my response to client will be different from what i expected. The Content-Disposition header (or lack of one) will not cause the client to send multiple requests.  Something else is going on. ---------------------------------------------------------------------- epneeh · 2018-09-07 02:46 this is my code now: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i add_log('is firing ' + arequestinfo.Command + ' with param ' + arequestinfo.Params.Text ,true);aresponseinfo.ContentType:='applicatio n/pdf';aresponseinfo.ContentDisposition:='attachment; filename="' + get_nip_from_id(arequestinfo.Params.Values['p']) + '.pdf"';aresponsein fo.ContentStream:=generate_report(arequestinfo.Params.Values['p']);  basically add_log write a log to TMemo, this is my html code : Code: HTML5  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i download pdf  attachment image is for traffic captured from firefox browser when i click "download pdf" button. this is log from TMemo : Code: [Select]09:26:25 | 07/09/2018 : is firing GET with param p=2924 09:26:25 | 07/09/2018 : is firing GET with param p=2924 09:26:25 | 07/09/2018 : is firing GET with param p=2924 09:26:25 | 07/09/2018 : is firing GET with param p=2924 you can see, under 1 second there is 4 same requests firing, any ideas? ---------------------------------------------------------------------- Remy Lebeau · 2018-09-07 18:15 Quote from: epneeh on September 07, 2018, 04:46:51 ambasically add_log write a log to TMemo, But, is it doing that safely?  Remember, TIdHTTPServer is a multi- threaded component.  Does add_log() access the TMemo directly, or does it synchronize with the main UI thread? Quote from: epneeh on September 07, 2018, 04:46:51 amattachment image is for traffic captured from firefox browser when i click "download pdf" button. As you can see, there is only 1 HTTP request being made (as it should be). Quote from: epneeh on September 07, 2018, 04:46:51 amthis is log from TMemo : Code: [Select]09:26:25 | 07/09/2018 : is firing GET with param p=2924 09:26:25 | 07/09/2018 : is firing GET with param p=2924 09:26:25 | 07/09/2018 : is firing GET with param p=2924 09:26:25 | 07/09/2018 : is firing GET with param p=2924 Then there has to be a problem with how you are managing the logging. ---------------------------------------------------------------------- epneeh · 2018-09-07 19:46 this is add_log function Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  procedure add_log(log : string;isSystem: boolean = False);  begin    if form1.chkShowlogs.Checked and isSystem then    begin              TThread.Queue(nil,          procedure          begin            form1.cLog.Lines.Add(timetostr(now) + ' | ' +  datetostr(now) + ' : ' + log);          end        );              end;   end;  is it syncronize? again, if i disabled the contentDisposisition and with or without logging, it is fine. ---------------------------------------------------------------------- Remy Lebeau · 2018-09-07 22:12 Quote from: epneeh on September 07, 2018, 09:46:03 pmCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if form1.chkShowlogs.Checked and isSystem then  Accessing *any* UI control is not thread-safe and MUST be synchronized with the UI thread.  That includes reading properties of UI controls. Quote from: epneeh on September 07, 2018, 09:46:03 pmCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i         TThread.Queue(nil,          procedure          begin            form1.cLog.Lines.Add(timetostr(now) + ' | ' +  datetostr(now) + ' : ' + log);          end        );  That is fine in of itself, as far as threading is concerned.  Though, I would suggest using FormatDateTime() instead of TimeToStr() and DateToStr() separately, and to grab the timestamp before calling TThread.Queue(), eg: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure add_log(log : string;isSystem: boolean = False);var  msg: string;begin  if isSystem then  begin    msg := FormatDateTime('t"|"ddddd', now) + ' : ' + log;    TThread.Queue(nil,      procedure      begin        if form1.chkShowlogs.Checked then        form1.cLog.Lines.Add(msg);      end    );  end;end;  Quote from: epneeh on September 07, 2018, 09:46:03 pmagain, if i disabled the contentDisposisition and with or without logging, it is fine. That has absolutely nothing to do with your logging issue, and besides the lack of a ContentDisposition value CANNOT trigger a browser to issue multiple requests.  Something else is going on. ---------------------------------------------------------------------- epneeh · 2018-09-08 01:08 Hi remy i think IDM is the culprit, as the server send response to client IDM dialog is launching and make a request to the server in milisecond manner, even worst it makes up to 5 request per dialog, do you have suggestion how to queue a request that has the same document and the same parameter? ---------------------------------------------------------------------- Remy Lebeau · 2018-09-10 17:05 Quote from: epneeh on September 08, 2018, 03:08:46 amHi remy i think IDM is the culprit I don't know what IDM is. Quote from: epneeh on September 08, 2018, 03:08:46 amas the server send response to client IDM dialog is launching and make a request to the server in milisecond manner, even worst it makes up to 5 request per dialog Please show the complete HTTP requests that are actually being made.  Maybe IDM is sending a request for different ranges of the file?  Who knows.  We can't see what you see. Quote from: epneeh on September 08, 2018, 03:08:46 amdo you have suggestion how to queue a request that has the same document and the same parameter? You can't.  You must process each request individually and fully, you can't consolidate them.