[asp.net] Better handling of item timeout reset in the in-proc session handler
[mono.git] / mcs / class / System.Web / System.Web.SessionState_2.0 / SessionStateServerHandler.cs
1 //
2 // System.Web.SessionStateServerHandler
3 //
4 // Authors:
5 //   Marek Habersack (grendello@gmail.com)
6 //
7 // (C) 2006 Marek Habersack
8 // (C) 2007-2010 Novell, Inc (http://novell.com/)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31 #if NET_2_0
32 using System.Collections.Specialized;
33 using System.IO;
34 using System.IO.Compression;
35 using System.Web;
36 using System.Web.Configuration;
37 using System.Runtime.Remoting;
38 using System.Diagnostics;
39
40 namespace System.Web.SessionState 
41 {
42         internal class SessionStateServerHandler : SessionStateStoreProviderBase
43         {
44                 const Int32 lockAcquireTimeout = 30000;
45                 
46                 SessionStateSection config;
47                 RemoteStateServer stateServer;
48
49                 public override SessionStateStoreData CreateNewStoreData (HttpContext context, int timeout)
50                 {
51                         return new SessionStateStoreData (new SessionStateItemCollection (),
52                                                           HttpApplicationFactory.ApplicationState.SessionObjects,
53                                                           timeout);
54                 }
55                 
56                 public override void CreateUninitializedItem (HttpContext context, string id, int timeout)
57                 {
58                         EnsureGoodId (id, true);
59                         stateServer.CreateUninitializedItem (id, timeout);
60                 }
61                 
62                 public override void Dispose ()
63                 {
64                 }
65                 
66                 public override void EndRequest (HttpContext context)
67                 {
68                 }
69
70                 SessionStateStoreData GetItemInternal (HttpContext context,
71                                                        string id,
72                                                        out bool locked,
73                                                        out TimeSpan lockAge,
74                                                        out object lockId,
75                                                        out SessionStateActions actions,
76                                                        bool exclusive)
77                 {
78                         locked = false;
79                         lockAge = TimeSpan.MinValue;
80                         lockId = Int32.MinValue;
81                         actions = SessionStateActions.None;
82
83                         if (id == null)
84                                 return null;
85                         
86                         StateServerItem item = stateServer.GetItem (id,
87                                                                     out locked,
88                                                                     out lockAge,
89                                                                     out lockId,
90                                                                     out actions,
91                                                                     exclusive);
92                         
93                         if (item == null)
94                                 return null;
95                         
96                         if (actions == SessionStateActions.InitializeItem)
97                                 return CreateNewStoreData (context, item.Timeout);
98                         
99                         SessionStateItemCollection items = null;
100                         HttpStaticObjectsCollection sobjs = null;
101                         MemoryStream stream = null;
102                         BinaryReader reader = null;
103                         Stream input = null;
104 #if NET_4_0
105                         GZipStream gzip = null;
106 #endif
107                         try {
108                                 if (item.CollectionData != null && item.CollectionData.Length > 0) {
109                                         stream = new MemoryStream (item.CollectionData);
110 #if NET_4_0                                     
111                                         if (config.CompressionEnabled)
112                                                 input = gzip = new GZipStream (stream, CompressionMode.Decompress, true);
113                                         else
114 #endif
115                                                 input = stream;
116                                         reader = new BinaryReader (input);
117                                         items = SessionStateItemCollection.Deserialize (reader);
118 #if NET_4_0
119                                         if (gzip != null)
120                                                 gzip.Close ();
121 #endif
122                                         reader.Close ();
123                                 } else
124                                         items = new SessionStateItemCollection ();
125                                 if (item.StaticObjectsData != null && item.StaticObjectsData.Length > 0)
126                                         sobjs = HttpStaticObjectsCollection.FromByteArray (item.StaticObjectsData);
127                                 else
128                                         sobjs = new HttpStaticObjectsCollection ();
129                         } catch (Exception ex) {
130                                 throw new HttpException ("Failed to retrieve session state.", ex);
131                         } finally {
132                                 if (stream != null)
133                                         stream.Dispose ();
134 #if NET_4_0
135                                 if (reader != null)
136                                         reader.Dispose ();
137                                 if (gzip != null)
138                                         gzip.Dispose ();
139 #else
140                                 if (reader != null)
141                                         ((IDisposable)reader).Dispose ();
142 #endif
143                         }
144                                 
145                         return new SessionStateStoreData (items,
146                                                           sobjs,
147                                                           item.Timeout);
148                 }
149                 
150                 public override SessionStateStoreData GetItem (HttpContext context,
151                                                                string id,
152                                                                out bool locked,
153                                                                out TimeSpan lockAge,
154                                                                out object lockId,
155                                                                out SessionStateActions actions)
156                 {
157                         EnsureGoodId (id, false);
158                         return GetItemInternal (context, id, out locked, out lockAge, out lockId, out actions, false);
159                 }
160                 
161                 public override SessionStateStoreData GetItemExclusive (HttpContext context,
162                                                                         string id,
163                                                                         out bool locked,
164                                                                         out TimeSpan lockAge,
165                                                                         out object lockId,
166                                                                         out SessionStateActions actions)
167                 {
168                         EnsureGoodId (id, false);
169                         return GetItemInternal (context, id, out locked, out lockAge, out lockId, out actions, true);
170                 }
171
172                 public override void Initialize (string name, NameValueCollection config)
173                 {
174                         this.config = (SessionStateSection) WebConfigurationManager.GetSection ("system.web/sessionState");
175                         if (String.IsNullOrEmpty (name))
176                                 name = "Session Server handler";
177                         RemotingConfiguration.Configure (null);
178                         string cons = null, proto = null, server = null, port = null;
179                         GetConData (out proto, out server, out port);
180                         cons = String.Format ("{0}://{1}:{2}/StateServer", proto, server, port);
181                         stateServer = Activator.GetObject (typeof (RemoteStateServer), cons) as RemoteStateServer;
182
183                         base.Initialize (name, config);
184                 }
185                 
186                 public override void InitializeRequest (HttpContext context)
187                 {
188                         // nothing to do here
189                 }
190                 
191                 public override void ReleaseItemExclusive (HttpContext context,
192                                                            string id,
193                                                            object lockId)
194                 {
195                         EnsureGoodId (id, true);
196                         stateServer.ReleaseItemExclusive (id, lockId);
197                 }
198                 
199                 public override void RemoveItem (HttpContext context,
200                                                  string id,
201                                                  object lockId,
202                                                  SessionStateStoreData item)
203                 {
204                         EnsureGoodId (id, true);
205                         stateServer.Remove (id, lockId);
206                 }
207                 
208                 public override void ResetItemTimeout (HttpContext context, string id)
209                 {
210                         EnsureGoodId (id, true);
211                         stateServer.ResetItemTimeout (id);
212                 }
213
214                 public override void SetAndReleaseItemExclusive (HttpContext context,
215                                                                  string id,
216                                                                  SessionStateStoreData item,
217                                                                  object lockId,
218                                                                  bool newItem)
219                 {
220                         EnsureGoodId (id, true);
221                         byte[] collection_data = null;
222                         byte[] sobjs_data = null;
223                         MemoryStream stream = null;
224                         BinaryWriter writer = null;
225                         Stream output = null;
226 #if NET_4_0
227                         GZipStream gzip = null;
228 #endif
229                         
230                         try {
231                                 SessionStateItemCollection items = item.Items as SessionStateItemCollection;
232                                 if (items != null && items.Count > 0) {
233                                         stream = new MemoryStream ();
234 #if NET_4_0
235                                         if (config.CompressionEnabled)
236                                                 output = gzip = new GZipStream (stream, CompressionMode.Compress, true);
237                                         else
238 #endif
239                                                 output = stream;
240                                         writer = new BinaryWriter (output);
241                                         items.Serialize (writer);
242 #if NET_4_0
243                                         if (gzip != null)
244                                                 gzip.Close ();
245 #endif
246                                         writer.Close ();
247                                         collection_data = stream.ToArray ();
248                                 }
249                                 HttpStaticObjectsCollection sobjs = item.StaticObjects;
250                                 if (sobjs != null && sobjs.Count > 0)
251                                         sobjs_data = sobjs.ToByteArray ();
252                         } catch (Exception ex) {
253                                 throw new HttpException ("Failed to store session data.", ex);
254                         } finally {
255
256 #if NET_4_0
257                                 if (writer != null)
258                                         writer.Dispose ();
259                                 if (gzip != null)
260                                         gzip.Dispose ();
261 #else
262                                 if (writer != null)
263                                         ((IDisposable)writer).Dispose ();
264 #endif
265                                 if (stream != null)
266                                         stream.Dispose ();
267                         }
268                         
269                         stateServer.SetAndReleaseItemExclusive (id, collection_data, sobjs_data, lockId, item.Timeout, newItem);
270                 }
271                 
272                 public override bool SetItemExpireCallback (SessionStateItemExpireCallback expireCallback)
273                 {
274                         return false;
275                 }
276
277                 void EnsureGoodId (string id, bool throwOnNull)
278                 {
279                         if (id == null)
280                                 if (throwOnNull)
281                                         throw new HttpException ("Session ID is invalid");
282                                 else
283                                         return;
284                         
285                         if (id.Length > SessionIDManager.SessionIDMaxLength)
286                                 throw new HttpException ("Session ID too long");
287                 }
288
289                 void GetConData (out string proto, out string server, out string port)
290                 {
291                         string cons = config.StateConnectionString;
292                         int ei = cons.IndexOf ('=');
293                         int ci = cons.IndexOf (':');
294
295                         if (ei < 0 || ci < 0)
296                                 throw new HttpException ("Invalid StateConnectionString");
297                         
298                         proto = cons.Substring (0, ei);
299                         server = cons.Substring (ei+1, ci - ei - 1);
300                         port = cons.Substring (ci+1, cons.Length - ci - 1);
301                         
302                         if (proto == "tcpip")
303                                 proto = "tcp";
304                 }
305         }
306 }
307 #endif