5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2006 Novell, Inc. http://www.novell.com
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
29 using System.Collections.Generic;
32 using System.ServiceModel;
34 using System.Threading;
36 namespace System.ServiceModel.Channels
38 internal class HttpSimpleReplyChannel : HttpReplyChannel
40 HttpSimpleChannelListener<IReplyChannel> source;
41 List<HttpListenerContext> waiting = new List<HttpListenerContext> ();
42 EndpointAddress local_address;
43 RequestContext reqctx;
45 public HttpSimpleReplyChannel (HttpSimpleChannelListener<IReplyChannel> listener)
48 this.source = listener;
51 protected override void OnClose (TimeSpan timeout)
57 public override bool TryReceiveRequest (TimeSpan timeout, out RequestContext context)
60 if (waiting.Count == 0 && !WaitForRequest (timeout))
62 HttpListenerContext ctx = null;
64 if (waiting.Count > 0) {
70 // Though as long as this instance is used
71 // synchronously, it should not happen.
74 // FIXME: supply maxSizeOfHeaders.
75 int maxSizeOfHeaders = 0x10000;
79 // FIXME: our HttpConnection (under HttpListener)
80 // somehow breaks when the underlying connection is
81 // reused. Remove it when it gets fixed.
82 ctx.Response.KeepAlive = false;
84 if (ctx.Request.HttpMethod == "POST") {
85 if (!Encoder.IsContentTypeSupported (ctx.Request.ContentType)) {
86 ctx.Response.StatusCode = (int) HttpStatusCode.UnsupportedMediaType;
87 ctx.Response.StatusDescription = String.Format (
88 "Expected content-type '{0}' but got '{1}'", Encoder.ContentType, ctx.Request.ContentType);
89 ctx.Response.Close ();
94 msg = Encoder.ReadMessage (
95 ctx.Request.InputStream, maxSizeOfHeaders);
96 if (source.MessageEncoder.MessageVersion.Envelope == EnvelopeVersion.Soap11 ||
97 source.MessageEncoder.MessageVersion.Addressing == AddressingVersion.None) {
98 string action = GetHeaderItem (ctx.Request.Headers ["SOAPAction"]);
100 msg.Headers.Action = action;
102 } else if (ctx.Request.HttpMethod == "GET") {
103 msg = Message.CreateMessage (source.MessageEncoder.MessageVersion, null);
105 msg.Headers.To = ctx.Request.Url;
107 HttpRequestMessageProperty prop =
108 new HttpRequestMessageProperty ();
109 prop.Method = ctx.Request.HttpMethod;
110 prop.QueryString = ctx.Request.Url.Query;
111 if (prop.QueryString.StartsWith ("?"))
112 prop.QueryString = prop.QueryString.Substring (1);
113 // FIXME: prop.SuppressEntityBody
114 prop.Headers.Add (ctx.Request.Headers);
115 msg.Properties.Add (HttpRequestMessageProperty.Name, prop);
117 MessageBuffer buf = msg.CreateBufferedCopy (0x10000);
118 msg = buf.CreateMessage ();
119 System.Xml.XmlTextWriter w = new System.Xml.XmlTextWriter (Console.Out);
120 w.Formatting = System.Xml.Formatting.Indented;
121 buf.CreateMessage ().WriteMessage (w);
124 context = new HttpRequestContext (this, msg, ctx);
131 public override bool WaitForRequest (TimeSpan timeout)
134 throw new InvalidOperationException ("Another wait operation is in progress");
136 wait = new AutoResetEvent (false);
137 source.Http.BeginGetContext (HttpContextReceived, null);
138 } catch (HttpListenerException e) {
139 if (e.ErrorCode == 0x80004005) // invalid handle. Happens during shutdown.
140 while (true) Thread.Sleep (1000); // thread is about to be terminated.
142 } catch (ObjectDisposedException) { return false; }
143 return wait.WaitOne (timeout, false);
146 void HttpContextReceived (IAsyncResult result)
148 if (State == CommunicationState.Closing || State == CommunicationState.Closed)
151 throw new InvalidOperationException ("WaitForRequest operation has not started");
152 waiting.Add (source.Http.EndGetContext (result));
158 internal abstract class HttpReplyChannel : ReplyChannelBase
160 HttpChannelListenerBase<IReplyChannel> source;
161 List<HttpListenerContext> waiting = new List<HttpListenerContext> ();
162 EndpointAddress local_address;
164 public HttpReplyChannel (HttpChannelListenerBase<IReplyChannel> listener)
167 this.source = listener;
170 public MessageEncoder Encoder {
171 get { return source.MessageEncoder; }
174 // FIXME: where is it set?
175 public override EndpointAddress LocalAddress {
176 get { return local_address; }
179 public override RequestContext ReceiveRequest (TimeSpan timeout)
182 TryReceiveRequest (timeout, out ctx);
186 protected override void OnAbort ()
188 foreach (HttpListenerContext ctx in waiting)
189 ctx.Request.InputStream.Close ();
192 protected override void OnClose (TimeSpan timeout)
194 // FIXME: consider timeout
195 foreach (HttpListenerContext ctx in waiting)
196 ctx.Request.InputStream.Close ();
199 protected override void OnOpen (TimeSpan timeout)
203 protected string GetHeaderItem (string raw)
205 if (raw == null || raw.Length == 0)
210 if (raw [raw.Length - 1] == raw [0])
211 return raw.Substring (1, raw.Length - 2);
212 // FIXME: is it simply an error?