[Rt-commit] rt branch 5.0/non-blocking-sessions created. rt-5.0.3-82-g51a7b9f24f

BPS Git Server git at git.bestpractical.com
Fri Aug 26 20:48:21 UTC 2022


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt".

The branch, 5.0/non-blocking-sessions has been created
        at  51a7b9f24f19ad49086f5582940b2c502102b41f (commit)

- Log -----------------------------------------------------------------
commit 51a7b9f24f19ad49086f5582940b2c502102b41f
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Fri Aug 19 16:56:21 2022 -0400

    Make the RT session non-blocking in a request
    
    Isolate the tied session interaction with Apache::Session
    from the full RT web request processing to avoid blocking
    requests on the session. This allows multiple requests
    to access the session at once. This should only be done
    where most requests are reads. If multiple requests
    write to the session, this will cause unpredictable
    results.

diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 6e827ffe28..6dfe23a843 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -889,7 +889,12 @@ sub AttemptPasswordAuthentication {
            $next = $next->{'url'} if ref $next;
 
         InstantiateNewSession();
-        $HTML::Mason::Commands::session{'CurrentUser'} = $user_obj;
+
+        RT::Interface::Web::Session::Set(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => 'CurrentUser',
+            SessionValue => $user_obj,
+        );
 
         $m->callback( %$ARGS, CallbackName => 'SuccessfulLogin', CallbackPage => '/autohandler', RedirectTo => \$next );
 
@@ -957,7 +962,12 @@ sub LoadSessionFromCookie {
     my %cookies       = CGI::Cookie->parse(RequestENV('HTTP_COOKIE'));
     my $cookiename    = _SessionCookieName();
     my $SessionCookie = ( $cookies{$cookiename} ? $cookies{$cookiename}->value : undef );
-    tie %HTML::Mason::Commands::session, 'RT::Interface::Web::Session', $SessionCookie;
+
+    RT::Interface::Web::Session::Load(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionId => $SessionCookie,
+    );
+
     unless ( $SessionCookie && $HTML::Mason::Commands::session{'_session_id'} eq $SessionCookie ) {
         InstantiateNewSession();
     }
@@ -975,8 +985,14 @@ sub LoadSessionFromCookie {
 }
 
 sub InstantiateNewSession {
-    tied(%HTML::Mason::Commands::session)->delete if tied(%HTML::Mason::Commands::session);
-    tie %HTML::Mason::Commands::session, 'RT::Interface::Web::Session', undef;
+    # Starting a new session, so clear out any existing one
+    RT::Interface::Web::Session::Delete( SessionRef => \%HTML::Mason::Commands::session );
+
+    RT::Interface::Web::Session::Load(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionId => undef,
+    );
+
     SendSessionCookie();
 }
 
@@ -1023,7 +1039,6 @@ a cached DBI statement handle twice at the same time.
 
 sub Redirect {
     my $redir_to = shift;
-    untie $HTML::Mason::Commands::session;
     my $uri        = URI->new($redir_to);
     my $server_uri = URI->new( RT->Config->Get('WebURL') );
     
@@ -2198,8 +2213,19 @@ sub MaybeRedirectForResults {
 
     if ( $has_actions ) {
         my $key = Digest::MD5::md5_hex( rand(1024) );
-        push @{ $session{"Actions"}{ $key } ||= [] }, @{ $args{'Actions'} };
-        $session{'i'}++;
+        my $actions_ref = [];
+        if ( $session{"Actions"}{ $key } ) {
+            $actions_ref = $session{"Actions"}{ $key };
+        }
+        push @{$actions_ref}, @{ $args{'Actions'} };
+
+        RT::Interface::Web::Session::Set(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => 'Actions',
+            SessionSubKey => $key,
+            SessionValue => $actions_ref,
+        );
+
         $arguments{'results'} = $key;
     }
 
@@ -2316,10 +2342,13 @@ sub CreateTicket {
     if ( my $tmp = $session{'Attachments'}{ $ARGS{'Token'} || '' } ) {
         push @attachments, grep $_, map $tmp->{$_}, sort keys %$tmp;
 
-        delete $session{'Attachments'}{ $ARGS{'Token'} || '' }
-            unless $ARGS{'KeepAttachments'} or $Ticket->{DryRun};
-        $session{'Attachments'} = $session{'Attachments'}
-            if @attachments;
+        unless ( $ARGS{'KeepAttachments'} or $Ticket->{DryRun} ) {
+            RT::Interface::Web::Session::Delete(
+                SessionRef => \%HTML::Mason::Commands::session,
+                SessionKey => 'Attachments',
+                SessionSubKey => $ARGS{'Token'} || '',
+            );
+        }
     }
     if ( $ARGS{'Attachments'} ) {
         push @attachments, grep $_, map $ARGS{Attachments}->{$_}, sort keys %{ $ARGS{'Attachments'} };
@@ -2452,11 +2481,14 @@ sub ProcessUpdateMessage {
     if ( my $tmp = $session{'Attachments'}{ $args{'ARGSRef'}{'Token'} || '' } ) {
         push @attachments, grep $_, map $tmp->{$_}, sort keys %$tmp;
 
-        delete $session{'Attachments'}{ $args{'ARGSRef'}{'Token'} || '' }
-            unless $args{'KeepAttachments'}
-            or ($args{TicketObj} and $args{TicketObj}{DryRun});
-        $session{'Attachments'} = $session{'Attachments'}
-            if @attachments;
+        unless ( $args{'KeepAttachments'}
+                 or ( $args{TicketObj} and $args{TicketObj}{DryRun} )) {
+            RT::Interface::Web::Session::Delete(
+                SessionRef => \%HTML::Mason::Commands::session,
+                SessionKey => 'Attachments',
+                SessionSubKey => $args{'ARGSRef'}{'Token'} || '',
+            );
+        }
     }
     if ( $args{ARGSRef}{'UpdateAttachments'} ) {
         push @attachments, grep $_, map $args{ARGSRef}->{UpdateAttachments}{$_},
@@ -2624,14 +2656,16 @@ sub ProcessAttachments {
     my $token = $args{'ARGSRef'}{'Token'}
         ||= $args{'Token'} ||= Digest::MD5::md5_hex( rand(1024) );
 
-    my $update_session = 0;
-
     # deal with deleting uploaded attachments
     if ( my $del = $args{'ARGSRef'}{'DeleteAttach'} ) {
-        delete $session{'Attachments'}{ $token }{ $_ }
-            foreach ref $del? @$del : ($del);
-
-        $update_session = 1;
+        foreach my $delete ( ref $del ? @$del : ($del) ) {
+            RT::Interface::Web::Session::Delete(
+                SessionRef => \%HTML::Mason::Commands::session,
+                SessionKey => 'Attachments',
+                SessionSubKey => $token,
+                SessionSubSubKey => $delete,
+            );
+        }
     }
 
     # store the uploaded attachment in session
@@ -2663,11 +2697,15 @@ sub ProcessAttachments {
             }
         }
 
-        $session{'Attachments'}{ $token }{ $file_path } = $attachment;
-
-        $update_session = 1;
+        RT::Interface::Web::Session::Set(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => 'Attachments',
+            SessionSubKey => $token,
+            SessionSubSubKey => $file_path,
+            SessionValue => $attachment,
+        );
     }
-    $session{'Attachments'} = $session{'Attachments'} if $update_session;
+
     return 1;
 }
 
@@ -4192,7 +4230,13 @@ sub ProcessQuickCreate {
             );
         }
 
-        $session{QuickCreate} = \%ARGS unless $created;
+        unless ( $created ) {
+            RT::Interface::Web::Session::Set(
+                SessionRef => \%HTML::Mason::Commands::session,
+                SessionKey => 'QuickCreate',
+                SessionValue => \%ARGS,
+            );
+        }
 
         MaybeRedirectForResults(
             Actions   => \@results,
@@ -4608,16 +4652,25 @@ sub SetObjectSessionCache {
         CheckRight => $CheckRight, ShowAll => $ShowAll );
 
     if ( defined $session{$cache_key} && !$session{$cache_key}{id} ) {
-        delete $session{$cache_key};
+        RT::Interface::Web::Session::Delete(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => $cache_key,
+        );
     }
 
     if ( defined $session{$cache_key}
          && ref $session{$cache_key} eq 'ARRAY') {
-        delete $session{$cache_key};
+         RT::Interface::Web::Session::Delete(
+             SessionRef => \%HTML::Mason::Commands::session,
+             SessionKey => $cache_key,
+         );
     }
     if ( defined $session{$cache_key} && defined $CacheNeedsUpdate &&
         $session{$cache_key}{lastupdated} <= $CacheNeedsUpdate ) {
-        delete $session{$cache_key};
+        RT::Interface::Web::Session::Delete(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => $cache_key,
+        );
     }
 
     if ( not defined $session{$cache_key} ) {
@@ -4628,24 +4681,36 @@ sub SetObjectSessionCache {
             CallbackPage => '/Elements/Quicksearch',
             ARGSRef => \%args, Collection => $collection, ObjectType => $ObjectType );
 
-        $session{$cache_key}{id} = {};
+        RT::Interface::Web::Session::Delete(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => $cache_key,
+        );
 
+        my %ids;
         while (my $object = $collection->Next) {
             if ($ShowAll
                 or not $CheckRight
                 or $session{CurrentUser}->HasRight( Object => $object, Right => $CheckRight ))
             {
                 next if $args{'Exclude'} and exists $args{'Exclude'}->{$object->Name};
-                push @{$session{$cache_key}{objects}}, {
+                push @{$ids{objects}}, {
                     Id          => $object->Id,
                     Name        => $object->Name,
                     Description => $object->_Accessible("Description" => "read") ? $object->Description : undef,
                     Lifecycle   => $object->_Accessible("Lifecycle" => "read") ? $object->Lifecycle : undef,
                 };
-                $session{$cache_key}{id}{ $object->id } = 1;
+                $ids{id}{ $object->id } = 1;
             }
         }
-        $session{$cache_key}{lastupdated} = time();
+
+        $ids{'lastupdated'} = time();
+
+        RT::Interface::Web::Session::Set(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => $cache_key,
+            SessionValue => \%ids,
+        );
+
     }
 
     return $cache_key;
diff --git a/lib/RT/Interface/Web/Session.pm b/lib/RT/Interface/Web/Session.pm
index a6ddccad8e..45d86b292b 100644
--- a/lib/RT/Interface/Web/Session.pm
+++ b/lib/RT/Interface/Web/Session.pm
@@ -51,6 +51,7 @@ use warnings;
 use strict;
 
 use RT::CurrentUser;
+use Clone;
 
 =head1 NAME
 
@@ -316,6 +317,95 @@ sub ClearByUser {
     $self->ClearOrphanLockFiles if $deleted;
 }
 
+sub Load {
+    my %args = (
+        SessionRef => undef,
+        SessionId  => undef,
+        @_
+    );
+
+    my %local_session;
+    tie %local_session, 'RT::Interface::Web::Session', $args{'SessionId'};
+
+    %{$args{'SessionRef'}} = %{Clone::clone( \%local_session )};
+
+    untie %local_session;
+
+    return 1;
+}
+
+sub Set {
+    my %args = (
+        SessionRef   => undef,
+        SessionKey   => undef,
+        SessionSubKey => undef,
+        SessionSubSubKey => undef,
+        SessionValue => undef,
+        @_
+    );
+
+    my $session_id = $args{'SessionRef'}->{'_session_id'};
+
+    my %local_session;
+    tie %local_session, 'RT::Interface::Web::Session', $session_id;
+
+    # Set the value, which will automagically set it in the back-end session storage
+    if ( $args{'SessionSubSubKey'} ) {
+        $local_session{$args{'SessionKey'}}{$args{'SessionSubKey'}}{$args{'SessionSubSubKey'}} = $args{'SessionValue'};
+    }
+    elsif ( $args{'SessionSubKey'} ) {
+        $local_session{$args{'SessionKey'}}{$args{'SessionSubKey'}} = $args{'SessionValue'};
+    }
+    else {
+        $local_session{$args{'SessionKey'}} = $args{'SessionValue'};
+    }
+
+    # Clone it back so we update the copy of the session with the latest values
+    %{$args{'SessionRef'}} = %{Clone::clone( \%local_session )};
+
+    untie %local_session;
+
+    return 1;
+}
+
+sub Delete {
+    my %args = (
+        SessionRef => undef,
+        SessionKey   => undef,
+        SessionSubKey => undef,
+        SessionSubSubKey => undef,
+        @_
+    );
+
+    my $session_id = $args{'SessionRef'}->{'_session_id'};
+
+    my %local_session;
+    tie %local_session, 'RT::Interface::Web::Session', $session_id;
+
+    if ( $args{'SessionKey'} ) {
+        # Delete requested item from the session
+        if ( defined $args{'SessionSubSubKey'} ) {
+            delete $local_session{$args{'SessionKey'}}{$args{'SessionSubKey'}}{$args{'SessionSubSubKey'}};
+        }
+        elsif ( defined $args{'SessionSubKey'} ) {
+            delete $local_session{$args{'SessionKey'}}{$args{'SessionSubKey'}};
+        }
+        else {
+            delete $local_session{$args{'SessionKey'}};
+        }
+        %{$args{'SessionRef'}} = %{Clone::clone( \%local_session )};
+    }
+    else {
+        # No key provided, delete the whole session
+        tied(%local_session)->delete if tied(%local_session);
+        %{$args{'SessionRef'}} = ();
+    }
+
+    untie %local_session;
+
+    return 1;
+}
+
 sub TIEHASH {
     my $self = shift;
     my $id = shift;
diff --git a/share/html/Admin/Global/DashboardsInMenu.html b/share/html/Admin/Global/DashboardsInMenu.html
index f72d8de206..407bd3024f 100644
--- a/share/html/Admin/Global/DashboardsInMenu.html
+++ b/share/html/Admin/Global/DashboardsInMenu.html
@@ -132,7 +132,10 @@ if ($ARGS{UpdateSearches}) {
                 );
             }
             push @actions, $ok ? loc('Global dashboards in menu saved.') : $msg;
-            delete $session{'dashboards_in_menu'};
+            RT::Interface::Web::Session::Delete(
+                SessionRef => \%HTML::Mason::Commands::session,
+                SessionKey => 'dashboards_in_menu',
+            );
         }
         else {
           my $report_names = ref $ARGS{'report'} eq 'ARRAY' ? $ARGS{'report'} : [$ARGS{'report'}];
@@ -155,7 +158,10 @@ if ($ARGS{UpdateSearches}) {
               );
           }
           push @actions, $ok ? loc('Preferences saved for reports in menu.') : $msg;
-          delete $session{'reports_in_menu'};
+          RT::Interface::Web::Session::Delete(
+              SessionRef => \%HTML::Mason::Commands::session,
+              SessionKey => 'reports_in_menu',
+          );
         }
     }
 }
diff --git a/share/html/Admin/Users/DashboardsInMenu.html b/share/html/Admin/Users/DashboardsInMenu.html
index fa4f0e248e..44617f60b7 100644
--- a/share/html/Admin/Users/DashboardsInMenu.html
+++ b/share/html/Admin/Users/DashboardsInMenu.html
@@ -150,7 +150,10 @@ if ($ARGS{UpdateSearches}) {
 
       my ( $ok, $msg ) = $UserObj->SetPreferences( $ARGS{'dashboard_id'}, { 'dashboards' => \@dashboard_ids } );
       push @actions, $ok ? loc('Preferences saved for dashboards in menu.') : $msg;
-      delete $session{'dashboards_in_menu'};
+      RT::Interface::Web::Session::Delete(
+          SessionRef => \%HTML::Mason::Commands::session,
+          SessionKey => 'dashboards_in_menu',
+      );
   }
   else {
     my $report_names = ref $ARGS{'report'} eq 'ARRAY' ? $ARGS{'report'} : [$ARGS{'report'}];
@@ -163,7 +166,10 @@ if ($ARGS{UpdateSearches}) {
 
     my ( $ok, $msg ) = $UserObj->SetPreferences( $ARGS{'dashboard_id'}, \@ret );
     push @actions, $ok ? loc('Preferences saved for reports in menu.') : $msg;
-    delete $session{'reports_in_menu'};
+    RT::Interface::Web::Session::Delete(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'reports_in_menu',
+    );
   }
 }
 
diff --git a/share/html/Asset/Create.html b/share/html/Asset/Create.html
index f5e91da737..b48501dfe8 100644
--- a/share/html/Asset/Create.html
+++ b/share/html/Asset/Create.html
@@ -118,7 +118,11 @@ Abort(loc("You don't have permission to create assets in catalog [_1].",
     unless $catalog->CurrentUserHasRight("CreateAsset");
 
 # Update the current default with the latest selection
-$session{'DefaultCatalog'} = $catalog->Id;
+RT::Interface::Web::Session::Set(
+    SessionRef => \%HTML::Mason::Commands::session,
+    SessionKey => 'DefaultCatalog',
+    SessionValue => $catalog->Id,
+);
 
 my @results;
 
diff --git a/share/html/Asset/Elements/SelectCatalog b/share/html/Asset/Elements/SelectCatalog
index 0f003909c6..d836d1c077 100644
--- a/share/html/Asset/Elements/SelectCatalog
+++ b/share/html/Asset/Elements/SelectCatalog
@@ -65,7 +65,11 @@ $AutoSubmit     => 0
 <%init>
 my $catalog_obj = LoadDefaultCatalog($Default || '');
 if ( $UpdateSession && $catalog_obj->Id ){
-    $session{'DefaultCatalog'} = $catalog_obj->Id;
+    RT::Interface::Web::Session::Set(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'DefaultCatalog',
+        SessionValue => $catalog_obj->Id,
+    );
     $Default = $catalog_obj->Id;
 }
 $ARGS{OnChange} = "jQuery(this).closest('form').find('input[name=CatalogChanged]').val(1);";
diff --git a/share/html/Elements/ListActions b/share/html/Elements/ListActions
index 0fd05fda49..a06ed123c4 100644
--- a/share/html/Elements/ListActions
+++ b/share/html/Elements/ListActions
@@ -61,20 +61,30 @@
 
 # backward compatibility, don't use array in new code, but use keyed hash
 if ( ref( $session{'Actions'} ) eq 'ARRAY' ) {
-    unshift @actions, @{ delete $session{'Actions'} };
-    $session{'i'}++;
+    unshift @actions, @{ $session{'Actions'} };
+    RT::Interface::Web::Session::Delete(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'Actions',
+    );
 }
 
 if ( ref( $session{'Actions'}{''} ) eq 'ARRAY' ) {
-    unshift @actions, @{ delete $session{'Actions'}{''} };
-    $session{'i'}++;
+    unshift @actions, @{ $session{'Actions'}{''} };
+    RT::Interface::Web::Session::Delete(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'Actions',
+        SessionSubKey => '',
+    );
 }
 
 my $actions_pointer = $DECODED_ARGS->{'results'};
 
 if ($actions_pointer &&  ref( $session{'Actions'}->{$actions_pointer} ) eq 'ARRAY' ) {
-    unshift @actions, @{ delete $session{'Actions'}->{$actions_pointer} };
-    $session{'i'}++;
+    unshift @actions, @{ $session{'Actions'}->{$actions_pointer} };
+    RT::Interface::Web::Session::Delete(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'Actions',
+    );
 }
 
 # XXX: run callbacks per row really crazy idea
diff --git a/share/html/Elements/QuickCreate b/share/html/Elements/QuickCreate
index 00ee3bd593..ce8f089c7f 100644
--- a/share/html/Elements/QuickCreate
+++ b/share/html/Elements/QuickCreate
@@ -94,5 +94,9 @@
 </div>
 
 <%INIT>
-my $args = delete $session{QuickCreate} || {};
+my $args = $session{QuickCreate} || {};
+RT::Interface::Web::Session::Delete(
+    SessionRef => \%HTML::Mason::Commands::session,
+    SessionKey => 'QuickCreate',
+);
 </%INIT>
diff --git a/share/html/Helpers/Upload/Delete b/share/html/Helpers/Upload/Delete
index 50fe73b23e..013c3c9d82 100644
--- a/share/html/Helpers/Upload/Delete
+++ b/share/html/Helpers/Upload/Delete
@@ -50,8 +50,12 @@ $Name => ''
 $Token => ''
 </%args>
 <%init>
-delete $session{'Attachments'}{ $Token }{ $Name };
-$session{'Attachments'} = $session{'Attachments'};
+RT::Interface::Web::Session::Delete(
+    SessionRef => \%HTML::Mason::Commands::session,
+    SessionKey => 'Attachments',
+    SessionSubKey => $Token,
+    SessionSubSubKey => $Name,
+);
 $r->content_type('application/json; charset=utf-8');
 $m->out( JSON({status => 'success'}) );
 $m->abort;
diff --git a/share/html/Install/index.html b/share/html/Install/index.html
index 9fb50a7da4..3eaf260e79 100644
--- a/share/html/Install/index.html
+++ b/share/html/Install/index.html
@@ -114,6 +114,7 @@ elsif ( ! -w $file ) {
     $locked = 1;
 }
 
+my $user_obj;
 if ( $locked ) {
     push @errors, loc("Config file [_1] is locked", $file);
 }
@@ -136,15 +137,21 @@ elsif ( $Run ) {
     RT::Interface::Web::Redirect(RT->Config->Get('WebURL') . 'Install/DatabaseType.html');
 } elsif ( $ChangeLang && $Lang ) {
     # hackish, but works
-    $session{'CurrentUser'} = RT::CurrentUser->new;
-    $session{'CurrentUser'}->LanguageHandle( $Lang );
+    $user_obj = RT::CurrentUser->new;
+    $user_obj->LanguageHandle( $Lang );
 }
 my $lang_handle = do { local $@;
     eval {
-        ($session{'CurrentUser'} || RT::CurrentUser->new(RT->SystemUser->Id))
+        ($user_obj || RT::CurrentUser->new(RT->SystemUser->Id))
             ->LanguageHandle
     }
 };
+
+RT::Interface::Web::Session::Set(
+    SessionRef => \%HTML::Mason::Commands::session,
+    SessionKey => 'CurrentUser',
+    SessionValue => $user_obj,
+);
 </%init>
 
 <%args>
diff --git a/share/html/NoAuth/Logout.html b/share/html/NoAuth/Logout.html
index 4587533211..47540a8867 100644
--- a/share/html/NoAuth/Logout.html
+++ b/share/html/NoAuth/Logout.html
@@ -79,7 +79,11 @@ $m->callback( %ARGS, CallbackName => 'BeforeSessionDelete' );
 
 if (keys %session) {
     RT::Interface::Web::InstantiateNewSession();
-    $session{'CurrentUser'} = RT::CurrentUser->new;
+    RT::Interface::Web::Session::Set(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'CurrentUser',
+        SessionValue => RT::CurrentUser->new,
+    );
 }
 
 $m->callback( %ARGS, CallbackName => 'AfterSessionDelete' );
diff --git a/share/html/Prefs/DashboardsInMenu.html b/share/html/Prefs/DashboardsInMenu.html
index 26a9b72caf..cc94a338a1 100644
--- a/share/html/Prefs/DashboardsInMenu.html
+++ b/share/html/Prefs/DashboardsInMenu.html
@@ -107,7 +107,10 @@ if ( $ARGS{ResetDashboards} ) {
     # Empty DashboardsInMenu pref means to use system default.
     my ($ok, $msg) = $user->SetPreferences('DashboardsInMenu', {});
     push @results, $ok ? loc('Preferences saved.') : $msg;
-    delete $session{'dashboards_in_menu'};
+    RT::Interface::Web::Session::Delete(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'dashboards_in_menu',
+    );
 }
 
 if ( $ARGS{ResetReports} ) {
@@ -116,7 +119,10 @@ if ( $ARGS{ResetReports} ) {
         # thus we need to delete preference instead.
         my ( $ok, $msg ) = $user->DeletePreferences('ReportsInMenu');
         push @results, $ok ? loc('Preferences saved.') : $msg;
-        delete $session{'reports_in_menu'};
+        RT::Interface::Web::Session::Delete(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => 'reports_in_menu',
+        );
     }
 }
 
@@ -146,7 +152,10 @@ if ($ARGS{UpdateSearches}) {
 
         my ( $ok, $msg ) = $user->SetPreferences( $ARGS{'dashboard_id'}, { 'dashboards' => \@dashboard_ids } );
         push @results, $ok ? loc('Preferences saved for dashboards in menu.') : $msg;
-        delete $session{'dashboards_in_menu'};
+        RT::Interface::Web::Session::Delete(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => 'dashboards_in_menu',
+        );
     }
     else {
       my $report_names = ref $ARGS{'report'} eq 'ARRAY' ? $ARGS{'report'} : [$ARGS{'report'}];
@@ -159,7 +168,10 @@ if ($ARGS{UpdateSearches}) {
 
       my ( $ok, $msg ) = $user->SetPreferences( $ARGS{'dashboard_id'}, \@ret );
       push @results, $ok ? loc('Preferences saved for reports in menu.') : $msg;
-      delete $session{'reports_in_menu'};
+      RT::Interface::Web::Session::Delete(
+          SessionRef => \%HTML::Mason::Commands::session,
+          SessionKey => 'reports_in_menu',
+      );
     }
 }
 
diff --git a/share/html/Prefs/QueueList.html b/share/html/Prefs/QueueList.html
index 5dca1630e7..63c20bd9cb 100644
--- a/share/html/Prefs/QueueList.html
+++ b/share/html/Prefs/QueueList.html
@@ -112,12 +112,18 @@ if ($ARGS{'Save'}) {
         # Clear for 'CreateTicket'
         my $cache_key = GetObjectSessionCacheKey( ObjectType => 'RT::Queue',
             CheckRight => 'CreateTicket', ShowAll => 0 );
-        delete $session{$cache_key};
+        RT::Interface::Web::Session::Delete(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => $cache_key,
+        );
 
         # Clear for 'ShowTicket'
         $cache_key = GetObjectSessionCacheKey( ObjectType => 'RT::Queue',
             CheckRight => 'ShowTicket', ShowAll => 0 );
-        delete $session{$cache_key};
+        RT::Interface::Web::Session::Delete(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => $cache_key,
+        );
     }
 }
 
diff --git a/share/html/REST/1.0/logout b/share/html/REST/1.0/logout
index fb2fe11b51..c310b060df 100644
--- a/share/html/REST/1.0/logout
+++ b/share/html/REST/1.0/logout
@@ -48,7 +48,11 @@
 <%PERL>
 if (keys %session) {
     RT::Interface::Web::InstantiateNewSession();
-    $session{CurrentUser} = RT::CurrentUser->new();
+    RT::Interface::Web::Session::Set(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'CurrentUser',
+        SessionValue => RT::CurrentUser->new,
+    );
 }
 </%PERL>
 RT/<% $RT::VERSION %> 200 Ok
diff --git a/share/html/Search/Build.html b/share/html/Search/Build.html
index 2f3be5f914..7e7aaf2e24 100644
--- a/share/html/Search/Build.html
+++ b/share/html/Search/Build.html
@@ -172,7 +172,10 @@ if ( $NewQuery ) {
     %saved_search = ( Id => 'new' );
 
     # ..then wipe the session out..
-    delete $session{$hash_name};
+    RT::Interface::Web::Session::Delete(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => $hash_name,
+    );
 
     # ..and the search results.
     $session{$session_name}->CleanSlate if defined $session{$session_name};
@@ -351,12 +354,16 @@ if ($ARGS{SavedSearchSave}) {
 
 # Push the updates into the session so we don't lose 'em
 
-$session{$hash_name} = {
-    %query,
-    SearchId    => $saved_search{'Id'},
-    Object      => $saved_search{'Object'},
-    Description => $saved_search{'Description'},
-};
+RT::Interface::Web::Session::Set(
+    SessionRef => \%HTML::Mason::Commands::session,
+    SessionKey => $hash_name,
+    SessionValue => {
+        %query,
+        SearchId    => $saved_search{'Id'},
+        Object      => $saved_search{'Object'},
+        Description => $saved_search{'Description'},
+    },
+);
 
 
 # Show the results, if we were asked.
diff --git a/share/html/Search/Bulk.html b/share/html/Search/Bulk.html
index 68767c4f31..72fcd580f3 100644
--- a/share/html/Search/Bulk.html
+++ b/share/html/Search/Bulk.html
@@ -557,7 +557,11 @@ unless ( $ARGS{'AddMoreAttach'} ) {
     }
     $RT::Handle->Commit;
 
-    delete $session{'Attachments'}{ $ARGS{'Token'} };
+    RT::Interface::Web::Session::Delete(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'Attachments',
+        SessionSubKey => $ARGS{'Token'},
+    );
 
     $Tickets->RedoSearch();
 }
diff --git a/share/html/Search/Chart b/share/html/Search/Chart
index acc121d5c8..957177c600 100644
--- a/share/html/Search/Chart
+++ b/share/html/Search/Chart
@@ -105,10 +105,18 @@ use RT::Report::Tickets;
 my $report = RT::Report::Tickets->new( $session{'CurrentUser'} );
 
 my %columns;
-if ( $Cache and my $data = delete $session{'charts_cache'}{ $Cache } ) {
-    %columns = %{ $data->{'columns'} };
-    $report->Deserialize( $data->{'report'} );
-    $session{'i'}++;
+if ( $Cache ) {
+    my $data;
+    if ( $session{'charts_cache'}{ $Cache } ) {
+        $data = $session{'charts_cache'}{ $Cache };
+        RT::Interface::Web::Session::Delete(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => 'charts_cache',
+            SessionSubKey => $Cache,
+        );
+        %columns = %{ $data->{'columns'} };
+        $report->Deserialize( $data->{'report'} );
+    }
 } else {
     %columns = $report->SetupGroupings(
         Query => $Query,
diff --git a/share/html/Search/Elements/Chart b/share/html/Search/Elements/Chart
index 021a07cfb7..c8c526ae5f 100644
--- a/share/html/Search/Elements/Chart
+++ b/share/html/Search/Elements/Chart
@@ -69,8 +69,13 @@ my $query_string = $m->comp('/Elements/QueryString', %ARGS, GroupBy => \@GroupBy
 my $key;
 if ( RT->Config->Get('EnableJSChart') || !RT->Config->Get('DisableGD') ) {
     $key = Digest::MD5::md5_hex( rand(1024) );
-    $session{'charts_cache'}{$key} = { columns => \%columns, report => $report->Serialize };
-    $session{'i'}++;
+    RT::Interface::Web::Session::Set(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'charts_cache',
+        SessionSubKey => $key,
+        SessionValue => { columns => \%columns, report => $report->Serialize },
+    );
+
 }
 
 </%init>
diff --git a/share/html/Search/JSChart b/share/html/Search/JSChart
index bc3f9d3ac2..29a9d083d8 100644
--- a/share/html/Search/JSChart
+++ b/share/html/Search/JSChart
@@ -199,10 +199,18 @@ my $report = RT::Report::Tickets->new( $session{'CurrentUser'} );
 @GroupBy = 'Status' unless @GroupBy;
 
 my %columns;
-if ( $Cache and my $data = delete $session{'charts_cache'}{ $Cache } ) {
-    %columns = %{ $data->{'columns'} };
-    $report->Deserialize( $data->{'report'} );
-    $session{'i'}++;
+if ( $Cache ) {
+    my $data;
+    if ( $session{'charts_cache'}{ $Cache } ) {
+        $data = $session{'charts_cache'}{ $Cache };
+        RT::Interface::Web::Session::Delete(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => 'charts_cache',
+            SessionSubKey => $Cache,
+        );
+        %columns = %{ $data->{'columns'} };
+        $report->Deserialize( $data->{'report'} );
+    }
 } else {
     %columns = $report->SetupGroupings(
         Query => $Query,
diff --git a/share/html/Search/Results.html b/share/html/Search/Results.html
index e0de84d99f..9588717cca 100644
--- a/share/html/Search/Results.html
+++ b/share/html/Search/Results.html
@@ -59,7 +59,6 @@
 <&|/l_unsafe, "<i>".$m->interp->apply_escapes($msg, "h")."</i>" &>There was an error parsing your search query: [_1].  Your RT admin can find more information in the error logs.</&>
 </&>
 % } else {
-
 <& /Elements/CollectionList, 
     Query => $Query,
     TotalFound => $count,
@@ -164,8 +163,11 @@ $Page = 1 unless $Page && $Page > 0;
 my $hash_name = join '-', 'CurrentSearchHash', $Class, $ObjectType || ();
 my $session_name = join '-', 'collection', $Class, $ObjectType || ();
 
-$session{'i'}++;
-$session{$session_name} = $Class->new($session{'CurrentUser'}) ;
+RT::Interface::Web::Session::Set(
+    SessionRef => \%HTML::Mason::Commands::session,
+    SessionKey => $session_name,
+    SessionValue => $Class->new($session{'CurrentUser'}),
+);
 
 my ( $ok, $msg );
 if ( $Query ) {
@@ -197,16 +199,28 @@ if ($OrderBy =~ /\|/) {
 $session{$session_name}->RowsPerPage( $Rows ) if $Rows;
 $session{$session_name}->GotoPage( $Page - 1 );
 
-$session{$hash_name} = {
-    Format      => $Format,
-    Query       => $Query,
-    Page        => $Page,
-    Order       => $Order,
-    OrderBy     => $OrderBy,
-    RowsPerPage => $Rows,
-    ObjectType  => $ObjectType,
-};
+# Save the session again because we made changes
+# Otherwise the set below will restore it back to
+# the last set.
+RT::Interface::Web::Session::Set(
+    SessionRef => \%HTML::Mason::Commands::session,
+    SessionKey => $session_name,
+    SessionValue => $session{$session_name},
+);
 
+RT::Interface::Web::Session::Set(
+    SessionRef => \%HTML::Mason::Commands::session,
+    SessionKey => $hash_name,
+    SessionValue => {
+        Format      => $Format,
+        Query       => $Query,
+        Page        => $Page,
+        Order       => $Order,
+        OrderBy     => $OrderBy,
+        RowsPerPage => $Rows,
+        ObjectType  => $ObjectType,
+    },
+);
 
 my $count = $session{$session_name}->Query() ? $session{$session_name}->CountAll() : 0;
 
@@ -247,7 +261,11 @@ my $ShortQueryString = "?".$m->comp('/Elements/QueryString', Query => $Query);
 
 my $interval_name = join '_', $Class, $ObjectType || (), 'refresh_interval';
 if ($ARGS{'SearchResultsRefreshInterval'}) {
-    $session{$interval_name} = $ARGS{'SearchResultsRefreshInterval'};
+    RT::Interface::Web::Session::Set(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => $interval_name,
+        SessionValue => $ARGS{'SearchResultsRefreshInterval'},
+    );
 }
 my $refresh = $session{$interval_name} || RT->Config->Get('SearchResultsRefreshInterval', $session{'CurrentUser'} );
 
diff --git a/share/html/SelfService/Elements/RequestUpdate b/share/html/SelfService/Elements/RequestUpdate
index fbb1972263..a0afc33eaa 100644
--- a/share/html/SelfService/Elements/RequestUpdate
+++ b/share/html/SelfService/Elements/RequestUpdate
@@ -82,7 +82,11 @@ action="<%RT->Config->Get('WebPath')%><% $r->path_info %>"
 </div>
 
 <%INIT>
-my $args = delete $session{QuickCreate} || {};
+my $args = $session{QuickCreate} || {};
+RT::Interface::Web::Session::Delete(
+    SessionRef => \%HTML::Mason::Commands::session,
+    SessionKey => 'QuickCreate',
+);
 </%INIT>
 
 <%ARGS>
diff --git a/share/html/SelfService/Helpers/Upload/Delete b/share/html/SelfService/Helpers/Upload/Delete
index 50fe73b23e..013c3c9d82 100644
--- a/share/html/SelfService/Helpers/Upload/Delete
+++ b/share/html/SelfService/Helpers/Upload/Delete
@@ -50,8 +50,12 @@ $Name => ''
 $Token => ''
 </%args>
 <%init>
-delete $session{'Attachments'}{ $Token }{ $Name };
-$session{'Attachments'} = $session{'Attachments'};
+RT::Interface::Web::Session::Delete(
+    SessionRef => \%HTML::Mason::Commands::session,
+    SessionKey => 'Attachments',
+    SessionSubKey => $Token,
+    SessionSubSubKey => $Name,
+);
 $r->content_type('application/json; charset=utf-8');
 $m->out( JSON({status => 'success'}) );
 $m->abort;
diff --git a/share/html/SelfService/Prefs.html b/share/html/SelfService/Prefs.html
index 128d534847..3ba496a04f 100644
--- a/share/html/SelfService/Prefs.html
+++ b/share/html/SelfService/Prefs.html
@@ -169,7 +169,11 @@ if ( $pref eq 'edit-prefs' || $pref eq 'edit-prefs-view-info' || $pref eq 'full-
 
     if ( $Lang ) {
         $session{'CurrentUser'}->LanguageHandle($Lang);
-        $session{'CurrentUser'} = $session{'CurrentUser'}; # force writeback
+        RT::Interface::Web::Session::Set(
+            SessionRef => \%HTML::Mason::Commands::session,
+            SessionKey => 'CurrentUser',
+            SessionValue => $session{'CurrentUser'},
+        );
     }
 }
 
diff --git a/share/html/Ticket/Create.html b/share/html/Ticket/Create.html
index 6af9f0bee7..b9583a4090 100644
--- a/share/html/Ticket/Create.html
+++ b/share/html/Ticket/Create.html
@@ -368,7 +368,11 @@ unless ($Queue) {
 
 Abort( loc( "Permission Denied" ) ) unless $Queue;
 
-$session{DefaultQueue} = $Queue;
+RT::Interface::Web::Session::Set(
+    SessionRef => \%HTML::Mason::Commands::session,
+    SessionKey => 'DefaultQueue',
+    SessionValue => $Queue,
+);
 
 my $current_user = $session{'CurrentUser'};
 
diff --git a/share/html/Widgets/SavedSearch b/share/html/Widgets/SavedSearch
index 0359ae752d..c42452f302 100644
--- a/share/html/Widgets/SavedSearch
+++ b/share/html/Widgets/SavedSearch
@@ -82,7 +82,10 @@ if ( my ( $container_object, $search_id ) = _parse_saved_search(
 # need to delete $session{CurrentSearchHash} to let it not show the old one.
 # of course, the new one should not be shown there either because it's of
 # different type
-    delete $session{'CurrentSearchHash'};
+    RT::Interface::Web::Session::Delete(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'CurrentSearchHash',
+    );
 }
 
 # look for the current one in the available saved searches
diff --git a/share/html/m/logout b/share/html/m/logout
index bd27b06614..4d1b840a39 100644
--- a/share/html/m/logout
+++ b/share/html/m/logout
@@ -48,7 +48,11 @@
 <%init>
 if (keys %session) {
     RT::Interface::Web::InstantiateNewSession();
-    $session{'CurrentUser'} = RT::CurrentUser->new;
+    RT::Interface::Web::Session::Set(
+        SessionRef => \%HTML::Mason::Commands::session,
+        SessionKey => 'CurrentUser',
+        SessionValue => RT::CurrentUser->new,
+    );
 }
 RT::Interface::Web::Redirect(RT->Config->Get('WebURL')."m/");
 </%init>
diff --git a/t/web/attachments.t b/t/web/attachments.t
index b6c1b06db3..d0ebf85bbd 100644
--- a/t/web/attachments.t
+++ b/t/web/attachments.t
@@ -8,6 +8,7 @@ use constant FaviconFile => $RT::StaticPath .'/images/favicon.png';
 use constant TextFile => $RT::StaticPath .'/css/mobile.css';
 
 my ($url, $m) = RT::Test->started_ok;
+diag $url;
 ok $m->login, 'logged in';
 
 my $queue = RT::Test->load_or_create_queue( Name => 'General' );
diff --git a/t/web/session.t b/t/web/session.t
index b7b7d1dfdc..d40f0e4a0e 100644
--- a/t/web/session.t
+++ b/t/web/session.t
@@ -36,7 +36,11 @@ my ($session_id) = $agent->cookie_jar->as_string =~ /RT_SID_[^=]+=(\w+);/;
 
 diag 'Load session for root user';
 my %session;
-tie %session, 'RT::Interface::Web::Session', $session_id;
+RT::Interface::Web::Session::Load(
+    SessionRef => \%session,
+    SessionId => $session_id,
+);
+
 is ( $session{'_session_id'}, $session_id, 'Got session id ' . $session_id );
 is ( $session{'CurrentUser'}->Name, 'root', 'Session is for root user' );
 
@@ -49,16 +53,60 @@ is ( $session{'SelectObject---RT::Queue---' . $user_id . '---CreateTicket---0'}{
 my $last_updated = $session{'SelectObject---RT::Queue---' . $user_id . '---CreateTicket---0'}{'lastupdated'};
 ok( $last_updated, "Got a lastupdated timestamp of $last_updated");
 
-untie(%session);
 # Wait for 1 sec so we can confirm lastupdated doesn't change
 sleep 1;
 $agent->get($url);
 is ($agent->status, 200, "Loaded a page");
 
-tie %session, 'RT::Interface::Web::Session', $session_id;
+RT::Interface::Web::Session::Load(
+    SessionRef => \%session,
+    SessionId => $session_id,
+);
+
 is ( $session{'_session_id'}, $session_id, 'Got session id ' . $session_id );
 is ( $session{'CurrentUser'}->Name, 'root', 'Session is for root user' );
 is ($last_updated, $session{'SelectObject---RT::Queue---' . $user_id . '---CreateTicket---0'}{'lastupdated'},
     "lastupdated is still $last_updated");
 
+RT::Interface::Web::Session::Set(
+    SessionRef => \%session,
+    SessionKey => 'Testing',
+    SessionValue => 'TestValue',
+);
+
+is ( $session{'Testing'}, 'TestValue', 'Set a test value' );
+
+RT::Interface::Web::Session::Load(
+    SessionRef => \%session,
+    SessionId => $session_id,
+);
+
+is ( $session{'Testing'}, 'TestValue', 'Test value still set after Load' );
+
+RT::Interface::Web::Session::Delete(
+    SessionRef => \%session,
+    SessionKey => 'Testing',
+);
+
+ok ( !(exists $session{'Testing'}), 'Test value deleted' );
+
+RT::Interface::Web::Session::Load(
+    SessionRef => \%session,
+    SessionId => $session_id,
+);
+
+ok ( !(exists $session{'Testing'}), 'Test value still deleted after Load' );
+
+diag 'Test logging out';
+ok ( $agent->logout(), 'Logged out' );
+
+RT::Interface::Web::Session::Load(
+    SessionRef => \%session,
+    SessionId => $session_id,
+);
+
+isnt ( $session{'_session_id'}, $session_id, 'Got a new session id' );
+ok ( !( exists $session{'CurrentUser'} ), 'New session is empty' );
+
+
 done_testing;
diff --git a/t/web/simple_search.t b/t/web/simple_search.t
index 0a9c2c0c7f..7b74d80344 100644
--- a/t/web/simple_search.t
+++ b/t/web/simple_search.t
@@ -5,7 +5,7 @@ use RT::Test tests => undef,
     config => 'Set( %FullTextSearch, Enable => 1, Indexed => 0 );';
 my ($baseurl, $m) = RT::Test->started_ok;
 my $url = $m->rt_base_url;
-
+diag "running: $url";
 my $queue = RT::Queue->new($RT::SystemUser);
 $queue->Create( Name => 'other' );
 ok( $queue->id, 'created queue other');
diff --git a/t/web/unlimited_search.t b/t/web/unlimited_search.t
index 6a5f69724d..cbb3f64587 100644
--- a/t/web/unlimited_search.t
+++ b/t/web/unlimited_search.t
@@ -4,6 +4,10 @@ use warnings;
 use RT::Test tests => 85;
 my ($baseurl, $agent) = RT::Test->started_ok;
 
+# Call login first to avoid losing mysql DB connection
+# Todo: figure out why
+ok $agent->login('root', 'password'), 'logged in as root';
+
 my $ticket = RT::Ticket->new(RT->SystemUser);
 for ( 1 .. 75 ) {
     ok $ticket->Create(
@@ -14,8 +18,6 @@ for ( 1 .. 75 ) {
     );
 }
 
-ok $agent->login('root', 'password'), 'logged in as root';
-
 $agent->get_ok('/Search/Build.html');
 $agent->form_name('BuildQuery');
 $agent->field('idOp', '>');

-----------------------------------------------------------------------


hooks/post-receive
-- 
rt


More information about the rt-commit mailing list