blob: 78653f3b680b5d42052caf8115f92913dca5d039 [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 Farrell86d2a4a2012-05-19 14:29:15 +000043#if !SILVERLIGHT
Jake Farrell5e022aa2012-05-18 00:33:54 +000044 private IWebProxy proxy = WebRequest.DefaultWebProxy;
Jens Geyerb080f682014-02-22 21:10:45 +010045#endif
46
Jake Farrell86d2a4a2012-05-19 14:29:15 +000047 public THttpClient(Uri u)
Bryan Duxbury62359472010-06-24 20:34:34 +000048 {
49 uri = u;
50 }
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 Farrell86d2a4a2012-05-19 14:29:15 +000076#if !SILVERLIGHT
Jake Farrell5e022aa2012-05-18 00:33:54 +000077 public IWebProxy Proxy
78 {
79 set
80 {
81 proxy = value;
82 }
83 }
Jake Farrell86d2a4a2012-05-19 14:29:15 +000084#endif
Jake Farrell5e022aa2012-05-18 00:33:54 +000085
Bryan Duxbury62359472010-06-24 20:34:34 +000086 public override bool IsOpen
87 {
88 get
89 {
90 return true;
91 }
92 }
93
94 public override void Open()
95 {
96 }
97
98 public override void Close()
99 {
100 if (inputStream != null)
101 {
102 inputStream.Close();
103 inputStream = null;
104 }
105 if (outputStream != null)
106 {
107 outputStream.Close();
108 outputStream = null;
109 }
110 }
111
112 public override int Read(byte[] buf, int off, int len)
113 {
114 if (inputStream == null)
115 {
116 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No request has been sent");
117 }
118
119 try
120 {
121 int ret = inputStream.Read(buf, off, len);
122
123 if (ret == -1)
124 {
125 throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "No more data available");
126 }
127
128 return ret;
129 }
130 catch (IOException iox)
131 {
132 throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString());
133 }
134 }
135
136 public override void Write(byte[] buf, int off, int len)
137 {
138 outputStream.Write(buf, off, len);
139 }
140
Roger Meier284a9b52011-12-08 13:39:56 +0000141#if !SILVERLIGHT
Bryan Duxbury62359472010-06-24 20:34:34 +0000142 public override void Flush()
143 {
Bryan Duxburyea67a782010-08-06 17:50:51 +0000144 try
145 {
146 SendRequest();
147 }
148 finally
149 {
150 outputStream = new MemoryStream();
151 }
Bryan Duxbury62359472010-06-24 20:34:34 +0000152 }
153
154 private void SendRequest()
155 {
156 try
157 {
158 HttpWebRequest connection = CreateRequest();
159
160 byte[] data = outputStream.ToArray();
161 connection.ContentLength = data.Length;
162
Roger Meierb1ec4cc2012-04-11 21:21:41 +0000163 using (Stream requestStream = connection.GetRequestStream())
164 {
165 requestStream.Write(data, 0, data.Length);
166 inputStream = connection.GetResponse().GetResponseStream();
167 }
Bryan Duxbury62359472010-06-24 20:34:34 +0000168 }
169 catch (IOException iox)
170 {
171 throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString());
172 }
173 catch (WebException wx)
174 {
175 throw new TTransportException(TTransportException.ExceptionType.Unknown, "Couldn't connect to server: " + wx);
176 }
177 }
Roger Meier284a9b52011-12-08 13:39:56 +0000178#endif
Jens Geyerb080f682014-02-22 21:10:45 +0100179 private HttpWebRequest CreateRequest()
Bryan Duxbury62359472010-06-24 20:34:34 +0000180 {
181 HttpWebRequest connection = (HttpWebRequest)WebRequest.Create(uri);
182
Roger Meier284a9b52011-12-08 13:39:56 +0000183#if !SILVERLIGHT
Bryan Duxbury62359472010-06-24 20:34:34 +0000184 if (connectTimeout > 0)
185 {
186 connection.Timeout = connectTimeout;
187 }
188 if (readTimeout > 0)
189 {
190 connection.ReadWriteTimeout = readTimeout;
191 }
Roger Meier284a9b52011-12-08 13:39:56 +0000192#endif
Bryan Duxbury62359472010-06-24 20:34:34 +0000193 // Make the request
194 connection.ContentType = "application/x-thrift";
195 connection.Accept = "application/x-thrift";
196 connection.UserAgent = "C#/THttpClient";
197 connection.Method = "POST";
Roger Meier284a9b52011-12-08 13:39:56 +0000198#if !SILVERLIGHT
Bryan Duxbury62359472010-06-24 20:34:34 +0000199 connection.ProtocolVersion = HttpVersion.Version10;
Roger Meier284a9b52011-12-08 13:39:56 +0000200#endif
Bryan Duxbury62359472010-06-24 20:34:34 +0000201
Roger Meier284a9b52011-12-08 13:39:56 +0000202 //add custom headers here
Bryan Duxbury62359472010-06-24 20:34:34 +0000203 foreach (KeyValuePair<string, string> item in customHeaders)
204 {
Roger Meier284a9b52011-12-08 13:39:56 +0000205#if !SILVERLIGHT
Bryan Duxbury62359472010-06-24 20:34:34 +0000206 connection.Headers.Add(item.Key, item.Value);
Roger Meier284a9b52011-12-08 13:39:56 +0000207#else
208 connection.Headers[item.Key] = item.Value;
209#endif
Bryan Duxbury62359472010-06-24 20:34:34 +0000210 }
211
Jake Farrell12ac2ac2011-12-09 02:21:37 +0000212#if !SILVERLIGHT
Jake Farrell5e022aa2012-05-18 00:33:54 +0000213 connection.Proxy = proxy;
Jake Farrell12ac2ac2011-12-09 02:21:37 +0000214#endif
Bryan Duxbury62359472010-06-24 20:34:34 +0000215
Roger Meier284a9b52011-12-08 13:39:56 +0000216 return connection;
Bryan Duxbury62359472010-06-24 20:34:34 +0000217 }
Roger Meier284a9b52011-12-08 13:39:56 +0000218
219#if SILVERLIGHT
220 public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
221 {
222 // Extract request and reset buffer
223 var data = outputStream.ToArray();
224
225 //requestBuffer_ = new MemoryStream();
226
227 try
228 {
229 // Create connection object
230 var flushAsyncResult = new FlushAsyncResult(callback, state);
231 flushAsyncResult.Connection = CreateRequest();
232
233 flushAsyncResult.Data = data;
234
235
236 flushAsyncResult.Connection.BeginGetRequestStream(GetRequestStreamCallback, flushAsyncResult);
237 return flushAsyncResult;
238
239 }
240 catch (IOException iox)
241 {
242 throw new TTransportException(iox.ToString());
243 }
244 }
245
246 public override void EndFlush(IAsyncResult asyncResult)
247 {
248 try
249 {
250 var flushAsyncResult = (FlushAsyncResult) asyncResult;
251
252 if (!flushAsyncResult.IsCompleted)
253 {
254 var waitHandle = flushAsyncResult.AsyncWaitHandle;
255 waitHandle.WaitOne(); // blocking INFINITEly
256 waitHandle.Close();
257 }
258
259 if (flushAsyncResult.AsyncException != null)
260 {
261 throw flushAsyncResult.AsyncException;
262 }
263 } finally
264 {
265 outputStream = new MemoryStream();
266 }
267
268 }
269
270
271 private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
272 {
273 var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
274 try
275 {
276 var reqStream = flushAsyncResult.Connection.EndGetRequestStream(asynchronousResult);
277 reqStream.Write(flushAsyncResult.Data, 0, flushAsyncResult.Data.Length);
278 reqStream.Flush();
279 reqStream.Close();
280
281 // Start the asynchronous operation to get the response
282 flushAsyncResult.Connection.BeginGetResponse(GetResponseCallback, flushAsyncResult);
283 }
284 catch (Exception exception)
285 {
286 flushAsyncResult.AsyncException = new TTransportException(exception.ToString());
287 flushAsyncResult.UpdateStatusToComplete();
288 flushAsyncResult.NotifyCallbackWhenAvailable();
289 }
290 }
291
292 private void GetResponseCallback(IAsyncResult asynchronousResult)
293 {
294 var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
295 try
296 {
297 inputStream = flushAsyncResult.Connection.EndGetResponse(asynchronousResult).GetResponseStream();
298 }
299 catch (Exception exception)
300 {
301 flushAsyncResult.AsyncException = new TTransportException(exception.ToString());
302 }
303 flushAsyncResult.UpdateStatusToComplete();
304 flushAsyncResult.NotifyCallbackWhenAvailable();
305 }
306
307 // Based on http://msmvps.com/blogs/luisabreu/archive/2009/06/15/multithreading-implementing-the-iasyncresult-interface.aspx
308 class FlushAsyncResult : IAsyncResult
309 {
310 private volatile Boolean _isCompleted;
311 private ManualResetEvent _evt;
312 private readonly AsyncCallback _cbMethod;
313 private readonly Object _state;
314
315 public FlushAsyncResult(AsyncCallback cbMethod, Object state)
316 {
317 _cbMethod = cbMethod;
318 _state = state;
319 }
320
321 internal byte[] Data { get; set; }
322 internal HttpWebRequest Connection { get; set; }
323 internal TTransportException AsyncException { get; set; }
324
325 public object AsyncState
326 {
327 get { return _state; }
328 }
329 public WaitHandle AsyncWaitHandle
330 {
331 get { return GetEvtHandle(); }
332 }
333 public bool CompletedSynchronously
334 {
335 get { return false; }
336 }
337 public bool IsCompleted
338 {
339 get { return _isCompleted; }
340 }
341 private readonly Object _locker = new Object();
342 private ManualResetEvent GetEvtHandle()
343 {
344 lock (_locker)
345 {
346 if (_evt == null)
347 {
348 _evt = new ManualResetEvent(false);
349 }
350 if (_isCompleted)
351 {
352 _evt.Set();
353 }
354 }
355 return _evt;
356 }
357 internal void UpdateStatusToComplete()
358 {
359 _isCompleted = true; //1. set _iscompleted to true
360 lock (_locker)
361 {
362 if (_evt != null)
363 {
364 _evt.Set(); //2. set the event, when it exists
365 }
366 }
367 }
368
369 internal void NotifyCallbackWhenAvailable()
370 {
371 if (_cbMethod != null)
372 {
373 _cbMethod(this);
374 }
375 }
376 }
377
378#endif
Roger Meierb1ec4cc2012-04-11 21:21:41 +0000379#region " IDisposable Support "
380 private bool _IsDisposed;
381
382 // IDisposable
383 protected override void Dispose(bool disposing)
384 {
385 if (!_IsDisposed)
386 {
387 if (disposing)
388 {
389 if (inputStream != null)
390 inputStream.Dispose();
391 if (outputStream != null)
392 outputStream.Dispose();
393 }
394 }
395 _IsDisposed = true;
396 }
397#endregion
398 }
Bryan Duxbury62359472010-06-24 20:34:34 +0000399}