2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / AspNetReplyChannel.cs
1 //
2 // AspNetReplyChannel.cs
3 //
4 // Author:
5 //      Ankit Jain  <jankit@novell.com>
6 //
7 // Copyright (C) 2006 Novell, Inc.  http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.Net;
32 using System.ServiceModel;
33 using System.Text;
34 using System.Threading;
35 using System.Web;
36
37 namespace System.ServiceModel.Channels
38 {
39         internal class AspNetReplyChannel : HttpReplyChannel
40         {
41                 AspNetChannelListener<IReplyChannel> listener;
42                 List<HttpContext> waiting = new List<HttpContext> ();
43                 HttpContext http_context;
44                 AutoResetEvent wait;
45
46                 public AspNetReplyChannel (AspNetChannelListener<IReplyChannel> listener)
47                         : base (listener)
48                 {
49                         this.listener = listener;
50                 }
51
52                 internal void CloseContext ()
53                 {
54                         if (http_context == null)
55                                 return;
56                         try {
57                                 listener.HttpHandler.EndRequest (listener, http_context);
58                         } finally {
59                                 http_context = null;
60                         }
61                 }
62
63                 void ShutdownPendingRequests ()
64                 {
65                         lock (waiting)
66                                 foreach (HttpContext ctx in waiting)
67                                         try {
68                                                 listener.HttpHandler.EndRequest (listener, ctx);
69                                         } catch {
70                                         }
71                 }
72
73                 protected override void OnAbort ()
74                 {
75                         ShutdownPendingRequests ();
76                         CloseContext ();
77                 }
78
79                 protected override void OnClose (TimeSpan timeout)
80                 {
81                         base.OnClose (timeout);
82
83                         ShutdownPendingRequests ();
84                         CloseContext ();
85                 }
86
87                 public override bool TryReceiveRequest (TimeSpan timeout, out RequestContext context)
88                 {
89                         try {
90                                 return TryReceiveRequestCore (timeout, out context);
91                         } catch (Exception ex) {
92                                 // FIXME: log it
93                                 Console.WriteLine ("AspNetReplyChannel caught an error: " + ex);
94                                 throw;
95                         }
96                 }
97
98                 bool TryReceiveRequestCore (TimeSpan timeout, out RequestContext context)
99                 {
100                         context = null;
101                         if (waiting.Count == 0 && !WaitForRequest (timeout))
102                                 return false;
103                         lock (waiting) {
104                                 if (waiting.Count > 0) {
105                                         http_context = waiting [0];
106                                         waiting.RemoveAt (0);
107                                 }
108                         }
109                         if (http_context == null) 
110                                 // Though as long as this instance is used
111                                 // synchronously, it should not happen.
112                                 return false;
113                         if (http_context.Response.StatusCode != 200) {
114                                 http_context.Response.Close ();
115                                 return false;
116                         }
117
118                         Message msg;
119                         var req = http_context.Request;
120                         if (req.HttpMethod == "GET") {
121                                 msg = Message.CreateMessage (Encoder.MessageVersion, null);
122                         } else {
123                                 //FIXME: Do above stuff for HttpContext ?
124                                 int maxSizeOfHeaders = 0x10000;
125
126                                 msg = Encoder.ReadMessage (
127                                         req.InputStream, maxSizeOfHeaders);
128
129                                 if (Encoder.MessageVersion.Envelope == EnvelopeVersion.Soap11) {
130                                         string action = GetHeaderItem (req.Headers ["SOAPAction"]);
131                                         if (action != null)
132                                                 msg.Headers.Action = action;
133                                 }
134                         }
135
136                         // FIXME: prop.SuppressEntityBody
137                         msg.Headers.To = req.Url;
138                         msg.Properties.Add ("Via", LocalAddress.Uri);
139                         msg.Properties.Add (HttpRequestMessageProperty.Name, CreateRequestProperty (req.HttpMethod, req.Url.Query, req.Headers));
140
141                         context = new AspNetRequestContext (this, msg, http_context);
142
143                         return true;
144                 }
145
146                 /*
147                 public override bool WaitForRequest (TimeSpan timeout)
148                 {
149                         if (http_context == null)
150                                 http_context = listener.HttpHandler.WaitForRequest (listener, timeout);
151                         return http_context != null;
152                 }
153                 */
154
155                 public override bool WaitForRequest (TimeSpan timeout)
156                 {
157                         if (wait != null)
158                                 throw new InvalidOperationException ("Another wait operation is in progress");
159                         try {
160                                 wait = new AutoResetEvent (false);
161                                 listener.ListenerManager.GetHttpContextAsync (timeout, HttpContextAcquired);
162                                 if (wait != null) // in case callback is done before WaitOne() here.
163                                         return wait.WaitOne (timeout, false);
164                                 return waiting.Count > 0;
165                         } finally {
166                                 wait = null;
167                         }
168                 }
169
170                 void HttpContextAcquired (HttpContextInfo ctx)
171                 {
172                         if (wait == null)
173                                 throw new InvalidOperationException ("WaitForRequest operation has not started");
174                         var sctx = (AspNetHttpContextInfo) ctx;
175                         if (State == CommunicationState.Opened && ctx != null)
176                                 waiting.Add (sctx.Source);
177                         var wait_ = wait;
178                         wait = null;
179                         wait_.Set ();
180                 }
181         }
182 }