2 // HttpServerTransportSink.cs
5 // Michael Hutchinson <mhutchinson@novell.com>
7 // Copyright (C) 2008 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.
30 using System.Collections;
33 using System.Runtime.Remoting.Messaging;
35 namespace System.Runtime.Remoting.Channels.Http
37 class HttpServerTransportSink : IServerChannelSink
40 IServerChannelSink nextSink;
42 public HttpServerTransportSink (IServerChannelSink nextSink)
44 this.nextSink = nextSink;
47 public IServerChannelSink NextChannelSink
49 get { return nextSink; }
52 public void AsyncProcessResponse (IServerResponseChannelSinkStack sinkStack, object state,
53 IMessage msg, ITransportHeaders headers, Stream responseStream)
55 ContextWithId ctx = (ContextWithId) state;
56 WriteOut (ctx.Context, headers, responseStream);
57 ctx.Context.Response.Close ();
60 public Stream GetResponseStream (IServerResponseChannelSinkStack sinkStack, object state,
61 IMessage msg, ITransportHeaders headers)
66 public ServerProcessing ProcessMessage (IServerChannelSinkStack sinkStack, IMessage requestMsg,
67 ITransportHeaders requestHeaders, Stream requestStream, out IMessage responseMsg,
68 out ITransportHeaders responseHeaders, out Stream responseStream)
70 //we know we never call this
71 throw new NotSupportedException ();
74 public IDictionary Properties
79 return nextSink.Properties;
84 internal void HandleRequest (HttpListenerContext context)
87 ITransportHeaders requestHeaders = new TransportHeaders ();
88 System.Collections.Specialized.NameValueCollection httpHeaders = context.Request.Headers;
89 foreach (string key in httpHeaders.Keys) {
90 requestHeaders[key] = httpHeaders[key];
93 //get an ID for this connection
94 ContextWithId identitiedContext = new ContextWithId (context);
96 requestHeaders[CommonTransportKeys.RequestUri] = context.Request.Url.PathAndQuery;
97 requestHeaders[CommonTransportKeys.IPAddress] = context.Request.RemoteEndPoint.Address;
98 requestHeaders[CommonTransportKeys.ConnectionId] = identitiedContext.ID;
99 requestHeaders["__RequestVerb"] = context.Request.HttpMethod;
100 requestHeaders["__HttpVersion"] = string.Format ("HTTP/{0}.{1}",
101 context.Request.ProtocolVersion.Major, context.Request.ProtocolVersion.Minor);
103 if (RemotingConfiguration.CustomErrorsEnabled (context.Request.IsLocal))
104 requestHeaders["__CustomErrorsEnabled"] = false;
106 IMessage responseMsg;
107 Stream responseStream;
108 ITransportHeaders responseHeaders;
110 // attach the context as state so that our async handler can use it to send the response
111 ServerChannelSinkStack sinkStack = new ServerChannelSinkStack ();
112 sinkStack.Push (this, identitiedContext);
114 // NOTE: if we copy the InputStream before passing it so the sinks, the .NET formatters have
115 // unspecified internal errors. Let's hope they don't need to seek the stream!
116 ServerProcessing proc = nextSink.ProcessMessage (sinkStack, null, requestHeaders, context.Request.InputStream,
117 out responseMsg, out responseHeaders, out responseStream);
120 case ServerProcessing.Complete:
121 WriteOut (context, responseHeaders, responseStream);
122 context.Response.Close ();
125 case ServerProcessing.Async:
128 case ServerProcessing.OneWay:
129 context.Response.Close ();
135 internal ServerProcessing SynchronousDispatch (ITransportHeaders requestHeaders, Stream requestStream,
136 out ITransportHeaders responseHeaders, out Stream responseStream)
138 IMessage responseMsg;
139 ContextWithId identitiedContext = new ContextWithId (null);
141 // attach the context as state so that our async handler can use it to send the response
142 ServerChannelSinkStack sinkStack = new ServerChannelSinkStack ();
143 sinkStack.Push (this, identitiedContext);
145 return nextSink.ProcessMessage (sinkStack, null, requestHeaders, requestStream,
146 out responseMsg, out responseHeaders, out responseStream);
149 static void WriteOut (HttpListenerContext context, ITransportHeaders responseHeaders, Stream responseStream)
151 //header processing taken/modified from HttpRemotingHandler
152 if (responseHeaders != null && responseHeaders["__HttpStatusCode"] != null) {
153 context.Response.StatusCode = Convert.ToInt32 (responseHeaders["__HttpStatusCode"]);
154 context.Response.StatusDescription = (string) responseHeaders["__HttpReasonPhrase"];
157 if (responseHeaders != null) {
158 foreach (DictionaryEntry entry in responseHeaders) {
159 string key = entry.Key.ToString ();
160 if (key != "__HttpStatusCode" && key != "__HttpReasonPhrase") {
161 context.Response.AddHeader ((string)entry.Key, responseHeaders[entry.Key].ToString ());
166 //we need a stream with a length, so if it's not a MemoryStream we copy it
168 if (responseStream is MemoryStream) {
169 ms = (MemoryStream) responseStream;
170 //this seems to be necessary for some third-party formatters
171 //even though my testing suggested .NET doesn't seem to seek incoming streams
174 ms = new MemoryStream ();
175 HttpClientTransportSink.CopyStream (responseStream, ms, 1024);
177 responseStream.Close ();
180 //FIXME: WHY DOES CHUNKING BREAK THE TESTS?
181 //for now, we set the content length so that the server doesn't use chunking
182 context.Response.ContentLength64 = ms.Length;
183 HttpClientTransportSink.CopyStream (ms, context.Response.OutputStream, 1024);
189 static object lockObj = new object ();
190 static int nextId = 0;
192 HttpListenerContext context;
195 public HttpListenerContext Context { get { return context; } }
196 public int ID { get { return id; } }
198 public ContextWithId (HttpListenerContext context)
200 this.context = context;
202 //FIXME: is it really valid to roll arund and reset the ID?