2008-09-30 Michael Hutchinson <mhutchinson@novell.com>
[mono-project.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Http / HttpClientTransportSink.cs
blobbf525c863b959a125c86d3063af2f6571473f763
1 //
2 // HttpClientTransportSink.cs
3 //
4 // Author:
5 // Michael Hutchinson <mhutchinson@novell.com>
6 //
7 // Copyright (C) 2008 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.
29 using System;
30 using System.Collections;
31 using System.IO;
32 using System.Net;
33 using System.Runtime.Remoting.Messaging;
35 namespace System.Runtime.Remoting.Channels.Http
37 class HttpClientTransportSink : IClientChannelSink
39 string url;
40 HttpClientChannel channel;
42 public HttpClientTransportSink (HttpClientChannel channel, string url)
44 this.channel = channel;
45 this.url = url;
48 //always the last sink in the chain
49 public IClientChannelSink NextChannelSink
51 get { return null; }
54 public void AsyncProcessRequest (IClientChannelSinkStack sinkStack, IMessage msg,
55 ITransportHeaders headers, Stream requestStream)
57 bool isOneWay = RemotingServices.IsOneWay (((IMethodMessage)msg).MethodBase);
59 HttpWebRequest request = CreateRequest (headers);
61 Stream targetStream = request.GetRequestStream ();
62 CopyStream (requestStream, targetStream, 1024);
63 targetStream.Close ();
65 if (!isOneWay) {
66 sinkStack.Push (this, request);
67 request.BeginGetResponse (new AsyncCallback (AsyncProcessResponseCallback), sinkStack);
71 void AsyncProcessResponseCallback (IAsyncResult ar)
73 IClientChannelSinkStack sinkStack = (IClientChannelSinkStack)ar.AsyncState;
74 HttpWebRequest request = (HttpWebRequest)sinkStack.Pop(this);
76 WebResponse response;
77 try {
78 response = request.EndGetResponse (ar);
79 } catch (WebException ex) {
80 response = ex.Response;
81 //only error 500 is handled by the remoting stack
82 HttpWebResponse httpResponse = response as HttpWebResponse;
83 if (httpResponse == null || httpResponse.StatusCode != HttpStatusCode.InternalServerError)
84 throw;
87 //this is only valid after the response is fetched
88 SetConnectionLimit (request);
90 Stream responseStream = response.GetResponseStream ();
91 ITransportHeaders responseHeaders = GetHeaders (response);
92 sinkStack.AsyncProcessResponse (responseHeaders, responseStream);
95 public void AsyncProcessResponse (IClientResponseChannelSinkStack sinkStack, object state,
96 ITransportHeaders headers, Stream stream)
98 // Should never be called
99 throw new NotSupportedException ();
102 public Stream GetRequestStream (IMessage msg, ITransportHeaders headers)
104 return null;
107 HttpWebRequest CreateRequest (ITransportHeaders requestHeaders)
109 string url = this.url;
112 //FIXME: requestUri should contain the URL-less URI only when it's a CAO call;
113 // at all other times it should be null. On Mono, whenever it should be null, it contains the full
114 // URL+URI, so we have a broken mixure of path types and we need to hack around it
115 string requestUri = requestHeaders[CommonTransportKeys.RequestUri] as string;
116 string objectURI;
117 if (requestUri != null && HttpChannel.ParseInternal (requestUri, out objectURI) == null) {
118 url = HttpChannel.ParseInternal (url, out objectURI);
119 if (!url.EndsWith ("/"))
120 url = url + "/";
121 url = url + requestUri;
124 HttpWebRequest request = (HttpWebRequest)WebRequest.Create (url);
125 request.UserAgent = string.Format ("Mozilla/4.0+(compatible; Mono Remoting; Mono {0})",
126 System.Environment.Version);
128 //Only set these if they deviate from the defaults, as some map to
129 //properties that throw NotImplementedExceptions
130 if (channel.Timeout != -1)
131 request.Timeout = channel.Timeout;
132 if (channel.AllowAutoRedirect == false)
133 request.AllowAutoRedirect = false;
134 if (channel.Credentials != null)
135 request.Credentials = channel.Credentials;
136 #if NET_2_0
137 else if (channel.UseDefaultCredentials == true)
138 request.UseDefaultCredentials = true;
139 #endif
140 else if (channel.Username != null && channel.Username.Length > 0) {
141 if (channel.Domain != null && channel.Domain.Length > 0) {
142 request.Credentials = new NetworkCredential (channel.Username, channel.Password,
143 channel.Domain);
144 } else {
145 request.Credentials = new NetworkCredential (channel.Username, channel.Password);
149 if (channel.UnsafeAuthenticatedConnectionSharing == true)
150 request.UnsafeAuthenticatedConnectionSharing = true;
151 if (channel.ConnectionGroupName != null)
152 request.ConnectionGroupName = channel.ConnectionGroupName;
155 FIXME: implement these
156 MachineName
157 ProxyName
158 ProxyPort
159 ProxyUri
160 ServicePrincipalName
161 UseAuthenticatedConnectionSharing
164 //build the headers
165 request.ContentType = (string)requestHeaders["Content-Type"];
167 //BUG: Mono formatters/dispatcher don't set this. Something in the MS stack does.
168 string method = (string)requestHeaders["__RequestVerb"];
169 if (method == null)
170 method = "POST";
171 request.Method = method;
173 foreach (DictionaryEntry entry in requestHeaders) {
174 string key = entry.Key.ToString ();
175 if (key != "__RequestVerb" && key != "Content-Type" && key != CommonTransportKeys.RequestUri) {
176 request.Headers.Add (key, entry.Value.ToString ());
179 return request;
182 void SetConnectionLimit (HttpWebRequest request)
184 if (channel.ClientConnectionLimit != 2) {
185 request.ServicePoint.ConnectionLimit = channel.ClientConnectionLimit;
189 static TransportHeaders GetHeaders (WebResponse response)
191 TransportHeaders headers = new TransportHeaders ();
192 foreach (string key in response.Headers) {
193 headers[key] = response.Headers[key];
195 return headers;
198 internal static void CopyStream (Stream source, Stream target, int bufferSize)
200 byte[] buffer = new byte[bufferSize];
201 int readLen = source.Read (buffer, 0, buffer.Length);
202 while (readLen > 0) {
203 target.Write (buffer, 0, readLen);
204 readLen = source.Read (buffer, 0, buffer.Length);
208 public void ProcessMessage (IMessage msg, ITransportHeaders requestHeaders, Stream requestStream,
209 out ITransportHeaders responseHeaders, out Stream responseStream)
211 HttpWebRequest request = CreateRequest (requestHeaders);
212 Stream targetStream = request.GetRequestStream ();
213 CopyStream (requestStream, targetStream, 1024);
214 targetStream.Close ();
216 WebResponse response;
217 try {
218 response = request.GetResponse ();
219 } catch (WebException ex) {
220 response = ex.Response;
221 //only error 500 is handled by the remoting stack
222 HttpWebResponse httpResponse = response as HttpWebResponse;
223 if (httpResponse == null || httpResponse.StatusCode != HttpStatusCode.InternalServerError)
224 throw;
227 //this is only valid after the response is fetched
228 SetConnectionLimit (request);
230 //FIXME: can we assume that the formatters will close the stream? Or do we need to make
231 // a copy and close it ourselves?
232 responseHeaders = GetHeaders (response);
233 responseStream = response.GetResponseStream ();
236 public IDictionary Properties
238 get { return null; }