]> git.proxmox.com Git - ceph.git/blame - ceph/src/jaegertracing/thrift/lib/csharp/src/Transport/THttpClient.cs
buildsys: switch source download to quincy
[ceph.git] / ceph / src / jaegertracing / thrift / lib / csharp / src / Transport / THttpClient.cs
CommitLineData
f67539c2
TL
1/**
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;
26using System.Threading;
27using System.Linq;
28using System.Security.Cryptography.X509Certificates;
29using System.IO.Compression;
30
31namespace Thrift.Transport
32{
33 public class THttpClient : TTransport, IDisposable
34 {
35 private readonly Uri uri;
36 private readonly X509Certificate[] certificates;
37 private Stream inputStream;
38 private MemoryStream outputStream = new MemoryStream();
39
40 // Timeouts in milliseconds
41 private int connectTimeout = 30000;
42
43 private int readTimeout = 30000;
44
45 private IDictionary<string, string> customHeaders = new Dictionary<string, string>();
46 private string userAgent = "C#/THttpClient";
47
48#if !SILVERLIGHT
49 private IWebProxy proxy = WebRequest.DefaultWebProxy;
50#endif
51
52 public THttpClient(Uri u)
53 : this(u, Enumerable.Empty<X509Certificate>())
54 {
55 }
56 public THttpClient(Uri u, string userAgent)
57 : this(u, userAgent, Enumerable.Empty<X509Certificate>())
58 {
59 }
60
61 public THttpClient(Uri u, IEnumerable<X509Certificate> certificates)
62 {
63 uri = u;
64 this.certificates = (certificates ?? Enumerable.Empty<X509Certificate>()).ToArray();
65 }
66 public THttpClient(Uri u, string userAgent, IEnumerable<X509Certificate> certificates)
67 {
68 uri = u;
69 this.userAgent = userAgent;
70 this.certificates = (certificates ?? Enumerable.Empty<X509Certificate>()).ToArray();
71 }
72
73 public int ConnectTimeout
74 {
75 set
76 {
77 connectTimeout = value;
78 }
79 }
80
81 public int ReadTimeout
82 {
83 set
84 {
85 readTimeout = value;
86 }
87 }
88
89 public IDictionary<string, string> CustomHeaders
90 {
91 get
92 {
93 return customHeaders;
94 }
95 }
96
97#if !SILVERLIGHT
98 public IWebProxy Proxy
99 {
100 set
101 {
102 proxy = value;
103 }
104 }
105#endif
106
107 public override bool IsOpen
108 {
109 get
110 {
111 return true;
112 }
113 }
114
115 public override void Open()
116 {
117 }
118
119 public override void Close()
120 {
121 if (inputStream != null)
122 {
123 inputStream.Close();
124 inputStream = null;
125 }
126 if (outputStream != null)
127 {
128 outputStream.Close();
129 outputStream = null;
130 }
131 }
132
133 public override int Read(byte[] buf, int off, int len)
134 {
135 if (inputStream == null)
136 {
137 throw new TTransportException(TTransportException.ExceptionType.NotOpen, "No request has been sent");
138 }
139
140 try
141 {
142 int ret = inputStream.Read(buf, off, len);
143
144 if (ret == -1)
145 {
146 throw new TTransportException(TTransportException.ExceptionType.EndOfFile, "No more data available");
147 }
148
149 return ret;
150 }
151 catch (IOException iox)
152 {
153 throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString(), iox);
154 }
155 }
156
157 public override void Write(byte[] buf, int off, int len)
158 {
159 outputStream.Write(buf, off, len);
160 }
161
162#if !SILVERLIGHT
163 public override void Flush()
164 {
165 try
166 {
167 SendRequest();
168 }
169 finally
170 {
171 outputStream = new MemoryStream();
172 }
173 }
174
175 private void SendRequest()
176 {
177 try
178 {
179 HttpWebRequest connection = CreateRequest();
180 connection.Headers.Add("Accept-Encoding", "gzip, deflate");
181
182 byte[] data = outputStream.ToArray();
183 connection.ContentLength = data.Length;
184
185 using (Stream requestStream = connection.GetRequestStream())
186 {
187 requestStream.Write(data, 0, data.Length);
188
189 // Resolve HTTP hang that can happens after successive calls by making sure
190 // that we release the response and response stream. To support this, we copy
191 // the response to a memory stream.
192 using (var response = connection.GetResponse())
193 {
194 using (var responseStream = response.GetResponseStream())
195 {
196 // Copy the response to a memory stream so that we can
197 // cleanly close the response and response stream.
198 inputStream = new MemoryStream();
199 byte[] buffer = new byte[8192]; // multiple of 4096
200 int bytesRead;
201 while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
202 {
203 inputStream.Write(buffer, 0, bytesRead);
204 }
205 inputStream.Seek(0, 0);
206 }
207
208 var encodings = response.Headers.GetValues("Content-Encoding");
209 if (encodings != null)
210 {
211 foreach (var encoding in encodings)
212 {
213 switch (encoding)
214 {
215 case "gzip":
216 DecompressGZipped(ref inputStream);
217 break;
218 case "deflate":
219 DecompressDeflated(ref inputStream);
220 break;
221 default:
222 break;
223 }
224 }
225 }
226 }
227 }
228 }
229 catch (IOException iox)
230 {
231 throw new TTransportException(TTransportException.ExceptionType.Unknown, iox.ToString(), iox);
232 }
233 catch (WebException wx)
234 {
235 throw new TTransportException(TTransportException.ExceptionType.Unknown, "Couldn't connect to server: " + wx, wx);
236 }
237 }
238
239 private void DecompressDeflated(ref Stream inputStream)
240 {
241 var tmp = new MemoryStream();
242 using (var decomp = new DeflateStream(inputStream, CompressionMode.Decompress))
243 {
244 decomp.CopyTo(tmp);
245 }
246 inputStream.Dispose();
247 inputStream = tmp;
248 inputStream.Seek(0, 0);
249 }
250
251 private void DecompressGZipped(ref Stream inputStream)
252 {
253 var tmp = new MemoryStream();
254 using (var decomp = new GZipStream(inputStream, CompressionMode.Decompress))
255 {
256 decomp.CopyTo(tmp);
257 }
258 inputStream.Dispose();
259 inputStream = tmp;
260 inputStream.Seek(0, 0);
261 }
262#endif
263 private HttpWebRequest CreateRequest()
264 {
265 HttpWebRequest connection = (HttpWebRequest)WebRequest.Create(uri);
266
267
268#if !SILVERLIGHT
269 // Adding certificates through code is not supported with WP7 Silverlight
270 // see "Windows Phone 7 and Certificates_FINAL_121610.pdf"
271 connection.ClientCertificates.AddRange(certificates);
272
273 if (connectTimeout > 0)
274 {
275 connection.Timeout = connectTimeout;
276 }
277 if (readTimeout > 0)
278 {
279 connection.ReadWriteTimeout = readTimeout;
280 }
281#endif
282 // Make the request
283 connection.ContentType = "application/x-thrift";
284 connection.Accept = "application/x-thrift";
285 connection.UserAgent = userAgent;
286 connection.Method = "POST";
287#if !SILVERLIGHT
288 connection.ProtocolVersion = HttpVersion.Version10;
289#endif
290
291 //add custom headers here
292 foreach (KeyValuePair<string, string> item in customHeaders)
293 {
294#if !SILVERLIGHT
295 connection.Headers.Add(item.Key, item.Value);
296#else
297 connection.Headers[item.Key] = item.Value;
298#endif
299 }
300
301#if !SILVERLIGHT
302 connection.Proxy = proxy;
303#endif
304
305 return connection;
306 }
307
308 public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
309 {
310 // Extract request and reset buffer
311 var data = outputStream.ToArray();
312
313 //requestBuffer_ = new MemoryStream();
314
315 try
316 {
317 // Create connection object
318 var flushAsyncResult = new FlushAsyncResult(callback, state);
319 flushAsyncResult.Connection = CreateRequest();
320
321 flushAsyncResult.Data = data;
322
323
324 flushAsyncResult.Connection.BeginGetRequestStream(GetRequestStreamCallback, flushAsyncResult);
325 return flushAsyncResult;
326
327 }
328 catch (IOException iox)
329 {
330 throw new TTransportException(iox.ToString(), iox);
331 }
332 }
333
334 public override void EndFlush(IAsyncResult asyncResult)
335 {
336 try
337 {
338 var flushAsyncResult = (FlushAsyncResult)asyncResult;
339
340 if (!flushAsyncResult.IsCompleted)
341 {
342 var waitHandle = flushAsyncResult.AsyncWaitHandle;
343 waitHandle.WaitOne(); // blocking INFINITEly
344 waitHandle.Close();
345 }
346
347 if (flushAsyncResult.AsyncException != null)
348 {
349 throw flushAsyncResult.AsyncException;
350 }
351 }
352 finally
353 {
354 outputStream = new MemoryStream();
355 }
356
357 }
358
359 private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
360 {
361 var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
362 try
363 {
364 var reqStream = flushAsyncResult.Connection.EndGetRequestStream(asynchronousResult);
365 reqStream.Write(flushAsyncResult.Data, 0, flushAsyncResult.Data.Length);
366 reqStream.Flush();
367 reqStream.Close();
368
369 // Start the asynchronous operation to get the response
370 flushAsyncResult.Connection.BeginGetResponse(GetResponseCallback, flushAsyncResult);
371 }
372 catch (Exception exception)
373 {
374 flushAsyncResult.AsyncException = new TTransportException(exception.ToString(), exception);
375 flushAsyncResult.UpdateStatusToComplete();
376 flushAsyncResult.NotifyCallbackWhenAvailable();
377 }
378 }
379
380 private void GetResponseCallback(IAsyncResult asynchronousResult)
381 {
382 var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
383 try
384 {
385 inputStream = flushAsyncResult.Connection.EndGetResponse(asynchronousResult).GetResponseStream();
386 }
387 catch (Exception exception)
388 {
389 flushAsyncResult.AsyncException = new TTransportException(exception.ToString(), exception);
390 }
391 flushAsyncResult.UpdateStatusToComplete();
392 flushAsyncResult.NotifyCallbackWhenAvailable();
393 }
394
395 // Based on http://msmvps.com/blogs/luisabreu/archive/2009/06/15/multithreading-implementing-the-iasyncresult-interface.aspx
396 class FlushAsyncResult : IAsyncResult
397 {
398 private volatile Boolean _isCompleted;
399 private ManualResetEvent _evt;
400 private readonly AsyncCallback _cbMethod;
401 private readonly object _state;
402
403 public FlushAsyncResult(AsyncCallback cbMethod, object state)
404 {
405 _cbMethod = cbMethod;
406 _state = state;
407 }
408
409 internal byte[] Data { get; set; }
410 internal HttpWebRequest Connection { get; set; }
411 internal TTransportException AsyncException { get; set; }
412
413 public object AsyncState
414 {
415 get { return _state; }
416 }
417 public WaitHandle AsyncWaitHandle
418 {
419 get { return GetEvtHandle(); }
420 }
421 public bool CompletedSynchronously
422 {
423 get { return false; }
424 }
425 public bool IsCompleted
426 {
427 get { return _isCompleted; }
428 }
429 private readonly object _locker = new object();
430 private ManualResetEvent GetEvtHandle()
431 {
432 lock (_locker)
433 {
434 if (_evt == null)
435 {
436 _evt = new ManualResetEvent(false);
437 }
438 if (_isCompleted)
439 {
440 _evt.Set();
441 }
442 }
443 return _evt;
444 }
445 internal void UpdateStatusToComplete()
446 {
447 _isCompleted = true; //1. set _iscompleted to true
448 lock (_locker)
449 {
450 if (_evt != null)
451 {
452 _evt.Set(); //2. set the event, when it exists
453 }
454 }
455 }
456
457 internal void NotifyCallbackWhenAvailable()
458 {
459 if (_cbMethod != null)
460 {
461 _cbMethod(this);
462 }
463 }
464 }
465
466 #region " IDisposable Support "
467 private bool _IsDisposed;
468
469 // IDisposable
470 protected override void Dispose(bool disposing)
471 {
472 if (!_IsDisposed)
473 {
474 if (disposing)
475 {
476 if (inputStream != null)
477 inputStream.Dispose();
478 if (outputStream != null)
479 outputStream.Dispose();
480 }
481 }
482 _IsDisposed = true;
483 }
484 #endregion
485 }
486}