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