blob: c875a43cb421a731ef73074b6f35173d6790d32f [file] [log] [blame]
Bryan Duxbury62359472010-06-24 20:34:34 +00001/**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 *
20 */
21
22using System;
23using System.Collections.Generic;
24using System.IO;
25using System.Net;
Roger Meier284a9b52011-12-08 13:39:56 +000026using System.Threading;
Bryan Duxbury62359472010-06-24 20:34:34 +000027
28namespace Thrift.Transport
29{
Roger Meierb1ec4cc2012-04-11 21:21:41 +000030 public class THttpClient : TTransport, IDisposable
Bryan Duxbury62359472010-06-24 20:34:34 +000031 {
32 private readonly Uri uri;
33 private Stream inputStream;
34 private MemoryStream outputStream = new MemoryStream();
Jake Farrell5e022aa2012-05-18 00:33:54 +000035
36 // Timeouts in milliseconds
37 private int connectTimeout = 30000;
38
39 private int readTimeout = 30000;
40
Bryan Duxbury62359472010-06-24 20:34:34 +000041 private IDictionary<String, String> customHeaders = new Dictionary<string, string>();
42
Jake Farrell5e022aa2012-05-18 00:33:54 +000043 private HttpWebRequest connection = null;
44 private IWebProxy proxy = WebRequest.DefaultWebProxy;
45
Bryan Duxbury62359472010-06-24 20:34:34 +000046 public THttpClient(Uri u)
47 {
48 uri = u;
Jake Farrell5e022aa2012-05-18 00:33:54 +000049 connection = CreateRequest();
Bryan Duxbury62359472010-06-24 20:34:34 +000050 }
51
52 public int ConnectTimeout
53 {
54 set
55 {
56 connectTimeout = value;
57 }
58 }
59
60 public int ReadTimeout
61 {
62 set
63 {
64 readTimeout = value;
65 }
66 }
67
68 public IDictionary<String, String> CustomHeaders
69 {
70 get
71 {
72 return customHeaders;
73 }
74 }
75
Jake Farrell5e022aa2012-05-18 00:33:54 +000076 public IWebProxy Proxy
77 {
78 set
79 {
80 proxy = value;
81 }
82 }
83
Bryan Duxbury62359472010-06-24 20:34:34 +000084 public override bool IsOpen
85 {
86 get
87 {
88 return true;
89 }
90 }
91
92 public override void Open()
93 {
94 }
95
96 public override void Close()
97 {
98 if (inputStream != null)
99 {
100 inputStream.Close();
101 inputStream = null;
102 }
103 if (outputStream != null)
104 {
105 outputStream.Close();
106 outputStream = null;
107 }
108 }
109
110 public override int Read(byte[] buf, int off, int len)
111 {
112 if (inputStream == null)
113 {
114 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No request has been sent");
115 }
116
117 try
118 {
119 int ret = inputStream.Read(buf, off, len);
120
121 if (ret == -1)
122 {
123 throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "No more data available");
124 }
125
126 return ret;
127 }
128 catch (IOException iox)
129 {
130 throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString());
131 }
132 }
133
134 public override void Write(byte[] buf, int off, int len)
135 {
136 outputStream.Write(buf, off, len);
137 }
138
Roger Meier284a9b52011-12-08 13:39:56 +0000139#if !SILVERLIGHT
Bryan Duxbury62359472010-06-24 20:34:34 +0000140 public override void Flush()
141 {
Bryan Duxburyea67a782010-08-06 17:50:51 +0000142 try
143 {
144 SendRequest();
145 }
146 finally
147 {
148 outputStream = new MemoryStream();
149 }
Bryan Duxbury62359472010-06-24 20:34:34 +0000150 }
151
152 private void SendRequest()
153 {
154 try
155 {
156 HttpWebRequest connection = CreateRequest();
157
158 byte[] data = outputStream.ToArray();
159 connection.ContentLength = data.Length;
160
Roger Meierb1ec4cc2012-04-11 21:21:41 +0000161 using (Stream requestStream = connection.GetRequestStream())
162 {
163 requestStream.Write(data, 0, data.Length);
164 inputStream = connection.GetResponse().GetResponseStream();
165 }
Bryan Duxbury62359472010-06-24 20:34:34 +0000166 }
167 catch (IOException iox)
168 {
169 throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString());
170 }
171 catch (WebException wx)
172 {
173 throw new TTransportException(TTransportException.ExceptionType.Unknown, "Couldn't connect to server: " + wx);
174 }
175 }
Roger Meier284a9b52011-12-08 13:39:56 +0000176#endif
Roger Meierb1ec4cc2012-04-11 21:21:41 +0000177 private HttpWebRequest CreateRequest()
Bryan Duxbury62359472010-06-24 20:34:34 +0000178 {
179 HttpWebRequest connection = (HttpWebRequest)WebRequest.Create(uri);
180
Roger Meier284a9b52011-12-08 13:39:56 +0000181#if !SILVERLIGHT
Bryan Duxbury62359472010-06-24 20:34:34 +0000182 if (connectTimeout > 0)
183 {
184 connection.Timeout = connectTimeout;
185 }
186 if (readTimeout > 0)
187 {
188 connection.ReadWriteTimeout = readTimeout;
189 }
Roger Meier284a9b52011-12-08 13:39:56 +0000190#endif
Bryan Duxbury62359472010-06-24 20:34:34 +0000191 // Make the request
192 connection.ContentType = "application/x-thrift";
193 connection.Accept = "application/x-thrift";
194 connection.UserAgent = "C#/THttpClient";
195 connection.Method = "POST";
Roger Meier284a9b52011-12-08 13:39:56 +0000196#if !SILVERLIGHT
Bryan Duxbury62359472010-06-24 20:34:34 +0000197 connection.ProtocolVersion = HttpVersion.Version10;
Roger Meier284a9b52011-12-08 13:39:56 +0000198#endif
Bryan Duxbury62359472010-06-24 20:34:34 +0000199
Roger Meier284a9b52011-12-08 13:39:56 +0000200 //add custom headers here
Bryan Duxbury62359472010-06-24 20:34:34 +0000201 foreach (KeyValuePair<string, string> item in customHeaders)
202 {
Roger Meier284a9b52011-12-08 13:39:56 +0000203#if !SILVERLIGHT
Bryan Duxbury62359472010-06-24 20:34:34 +0000204 connection.Headers.Add(item.Key, item.Value);
Roger Meier284a9b52011-12-08 13:39:56 +0000205#else
206 connection.Headers[item.Key] = item.Value;
207#endif
Bryan Duxbury62359472010-06-24 20:34:34 +0000208 }
209
Jake Farrell12ac2ac2011-12-09 02:21:37 +0000210#if !SILVERLIGHT
Jake Farrell5e022aa2012-05-18 00:33:54 +0000211 connection.Proxy = proxy;
Jake Farrell12ac2ac2011-12-09 02:21:37 +0000212#endif
Bryan Duxbury62359472010-06-24 20:34:34 +0000213
Roger Meier284a9b52011-12-08 13:39:56 +0000214 return connection;
Bryan Duxbury62359472010-06-24 20:34:34 +0000215 }
Roger Meier284a9b52011-12-08 13:39:56 +0000216
217#if SILVERLIGHT
218 public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
219 {
220 // Extract request and reset buffer
221 var data = outputStream.ToArray();
222
223 //requestBuffer_ = new MemoryStream();
224
225 try
226 {
227 // Create connection object
228 var flushAsyncResult = new FlushAsyncResult(callback, state);
229 flushAsyncResult.Connection = CreateRequest();
230
231 flushAsyncResult.Data = data;
232
233
234 flushAsyncResult.Connection.BeginGetRequestStream(GetRequestStreamCallback, flushAsyncResult);
235 return flushAsyncResult;
236
237 }
238 catch (IOException iox)
239 {
240 throw new TTransportException(iox.ToString());
241 }
242 }
243
244 public override void EndFlush(IAsyncResult asyncResult)
245 {
246 try
247 {
248 var flushAsyncResult = (FlushAsyncResult) asyncResult;
249
250 if (!flushAsyncResult.IsCompleted)
251 {
252 var waitHandle = flushAsyncResult.AsyncWaitHandle;
253 waitHandle.WaitOne(); // blocking INFINITEly
254 waitHandle.Close();
255 }
256
257 if (flushAsyncResult.AsyncException != null)
258 {
259 throw flushAsyncResult.AsyncException;
260 }
261 } finally
262 {
263 outputStream = new MemoryStream();
264 }
265
266 }
267
268
269 private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
270 {
271 var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
272 try
273 {
274 var reqStream = flushAsyncResult.Connection.EndGetRequestStream(asynchronousResult);
275 reqStream.Write(flushAsyncResult.Data, 0, flushAsyncResult.Data.Length);
276 reqStream.Flush();
277 reqStream.Close();
278
279 // Start the asynchronous operation to get the response
280 flushAsyncResult.Connection.BeginGetResponse(GetResponseCallback, flushAsyncResult);
281 }
282 catch (Exception exception)
283 {
284 flushAsyncResult.AsyncException = new TTransportException(exception.ToString());
285 flushAsyncResult.UpdateStatusToComplete();
286 flushAsyncResult.NotifyCallbackWhenAvailable();
287 }
288 }
289
290 private void GetResponseCallback(IAsyncResult asynchronousResult)
291 {
292 var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
293 try
294 {
295 inputStream = flushAsyncResult.Connection.EndGetResponse(asynchronousResult).GetResponseStream();
296 }
297 catch (Exception exception)
298 {
299 flushAsyncResult.AsyncException = new TTransportException(exception.ToString());
300 }
301 flushAsyncResult.UpdateStatusToComplete();
302 flushAsyncResult.NotifyCallbackWhenAvailable();
303 }
304
305 // Based on http://msmvps.com/blogs/luisabreu/archive/2009/06/15/multithreading-implementing-the-iasyncresult-interface.aspx
306 class FlushAsyncResult : IAsyncResult
307 {
308 private volatile Boolean _isCompleted;
309 private ManualResetEvent _evt;
310 private readonly AsyncCallback _cbMethod;
311 private readonly Object _state;
312
313 public FlushAsyncResult(AsyncCallback cbMethod, Object state)
314 {
315 _cbMethod = cbMethod;
316 _state = state;
317 }
318
319 internal byte[] Data { get; set; }
320 internal HttpWebRequest Connection { get; set; }
321 internal TTransportException AsyncException { get; set; }
322
323 public object AsyncState
324 {
325 get { return _state; }
326 }
327 public WaitHandle AsyncWaitHandle
328 {
329 get { return GetEvtHandle(); }
330 }
331 public bool CompletedSynchronously
332 {
333 get { return false; }
334 }
335 public bool IsCompleted
336 {
337 get { return _isCompleted; }
338 }
339 private readonly Object _locker = new Object();
340 private ManualResetEvent GetEvtHandle()
341 {
342 lock (_locker)
343 {
344 if (_evt == null)
345 {
346 _evt = new ManualResetEvent(false);
347 }
348 if (_isCompleted)
349 {
350 _evt.Set();
351 }
352 }
353 return _evt;
354 }
355 internal void UpdateStatusToComplete()
356 {
357 _isCompleted = true; //1. set _iscompleted to true
358 lock (_locker)
359 {
360 if (_evt != null)
361 {
362 _evt.Set(); //2. set the event, when it exists
363 }
364 }
365 }
366
367 internal void NotifyCallbackWhenAvailable()
368 {
369 if (_cbMethod != null)
370 {
371 _cbMethod(this);
372 }
373 }
374 }
375
376#endif
Roger Meierb1ec4cc2012-04-11 21:21:41 +0000377#region " IDisposable Support "
378 private bool _IsDisposed;
379
380 // IDisposable
381 protected override void Dispose(bool disposing)
382 {
383 if (!_IsDisposed)
384 {
385 if (disposing)
386 {
387 if (inputStream != null)
388 inputStream.Dispose();
389 if (outputStream != null)
390 outputStream.Dispose();
391 }
392 }
393 _IsDisposed = true;
394 }
395#endregion
396 }
Bryan Duxbury62359472010-06-24 20:34:34 +0000397}