blob: d19b7a76fa964b81b90db9777257f82aebfaeb6f [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
Roger Meier284a9b52011-12-08 13:39:56 +0000219 public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
220 {
221 // Extract request and reset buffer
222 var data = outputStream.ToArray();
223
224 //requestBuffer_ = new MemoryStream();
225
226 try
227 {
228 // Create connection object
229 var flushAsyncResult = new FlushAsyncResult(callback, state);
230 flushAsyncResult.Connection = CreateRequest();
231
232 flushAsyncResult.Data = data;
233
234
235 flushAsyncResult.Connection.BeginGetRequestStream(GetRequestStreamCallback, flushAsyncResult);
236 return flushAsyncResult;
237
238 }
239 catch (IOException iox)
240 {
241 throw new TTransportException(iox.ToString());
242 }
243 }
244
245 public override void EndFlush(IAsyncResult asyncResult)
246 {
247 try
248 {
249 var flushAsyncResult = (FlushAsyncResult) asyncResult;
250
251 if (!flushAsyncResult.IsCompleted)
252 {
253 var waitHandle = flushAsyncResult.AsyncWaitHandle;
254 waitHandle.WaitOne(); // blocking INFINITEly
255 waitHandle.Close();
256 }
257
258 if (flushAsyncResult.AsyncException != null)
259 {
260 throw flushAsyncResult.AsyncException;
261 }
262 } finally
263 {
264 outputStream = new MemoryStream();
265 }
266
267 }
268
Roger Meier284a9b52011-12-08 13:39:56 +0000269 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
Roger Meierb1ec4cc2012-04-11 21:21:41 +0000376#region " IDisposable Support "
377 private bool _IsDisposed;
378
379 // IDisposable
380 protected override void Dispose(bool disposing)
381 {
382 if (!_IsDisposed)
383 {
384 if (disposing)
385 {
386 if (inputStream != null)
387 inputStream.Dispose();
388 if (outputStream != null)
389 outputStream.Dispose();
390 }
391 }
392 _IsDisposed = true;
393 }
394#endregion
395 }
Bryan Duxbury62359472010-06-24 20:34:34 +0000396}