/*************************************************************************** * * CurlS#arp * * Copyright (c) 2014 Dr. Masroor Ehsan (masroore@gmail.com) * Portions copyright (c) 2004, 2005 Jeff Phillips (jeff@jeffp.net) * * This software is licensed as described in the file LICENSE, which you * should have received as part of this distribution. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of this Software, and permit persons to whom the Software is * furnished to do so, under the terms of the LICENSE file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF * ANY KIND, either express or implied. * **************************************************************************/ using System; using System.Collections; using System.Runtime.InteropServices; namespace CurlSharp { /// /// Implements the curl_multi_xxx API. /// public class CurlMulti : IDisposable { // private members private readonly Hashtable _htEasy; private int _maxFd; private CurlMultiInfo[] _multiInfo; private bool _bGotMultiInfo; #if USE_LIBCURLSHIM private IntPtr _fdSets; #else private NativeMethods.fd_set _fd_read, _fd_write, _fd_except; #endif private IntPtr _pMulti; /// /// Constructor /// /// /// This is thrown /// if hasn't bee properly initialized. /// /// /// This is thrown if the native CurlMulti handle wasn't /// created successfully. /// public CurlMulti() { Curl.EnsureCurl(); _pMulti = NativeMethods.curl_multi_init(); ensureHandle(); _maxFd = 0; #if USE_LIBCURLSHIM _fdSets = IntPtr.Zero; _fdSets = NativeMethods.curl_shim_alloc_fd_sets(); #else _fd_read = NativeMethods.fd_set.Create(); _fd_read = NativeMethods.fd_set.Create(); _fd_write = NativeMethods.fd_set.Create(); _fd_except = NativeMethods.fd_set.Create(); #endif _multiInfo = null; _bGotMultiInfo = false; _htEasy = new Hashtable(); } /// /// Max file descriptor /// public int MaxFd { get { return _maxFd; } } /// /// Cleanup unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Destructor /// ~CurlMulti() { Dispose(false); } private void Dispose(bool disposing) { lock (this) { // if (disposing) // managed member cleanup // unmanaged cleanup if (_pMulti != IntPtr.Zero) { NativeMethods.curl_multi_cleanup(_pMulti); _pMulti = IntPtr.Zero; } #if USE_LIBCURLSHIM if (_fdSets != IntPtr.Zero) { NativeMethods.curl_shim_free_fd_sets(_fdSets); _fdSets = IntPtr.Zero; } #else _fd_read.Cleanup(); _fd_write.Cleanup(); _fd_except.Cleanup(); #endif } } private void ensureHandle() { if (_pMulti == IntPtr.Zero) throw new NullReferenceException("No internal multi handle"); } /// /// Add an CurlEasy object. /// /// /// object to add. /// /// /// A , hopefully CurlMultiCode.Ok /// /// /// This is thrown if the native CurlMulti handle wasn't /// created successfully. /// public CurlMultiCode AddHandle(CurlEasy curlEasy) { ensureHandle(); var p = curlEasy.Handle; _htEasy.Add(p, curlEasy); return NativeMethods.curl_multi_add_handle(_pMulti, p); } /// /// Remove an CurlEasy object. /// /// /// object to remove. /// /// /// A , hopefully CurlMultiCode.Ok /// /// /// This is thrown if the native CurlMulti handle wasn't /// created successfully. /// public CurlMultiCode RemoveHandle(CurlEasy curlEasy) { ensureHandle(); var p = curlEasy.Handle; _htEasy.Remove(p); return NativeMethods.curl_multi_remove_handle(_pMulti, curlEasy.Handle); } /// /// Get a string description of an error code. /// /// /// The for which to obtain the error /// string description. /// /// The string description. public String StrError(CurlMultiCode errorNum) { return Marshal.PtrToStringAnsi(NativeMethods.curl_multi_strerror(errorNum)); } /// /// Read/write data to/from each CurlEasy object. /// /// /// The number of objects still in process is /// written by this function to this reference parameter. /// /// /// A , hopefully CurlMultiCode.Ok /// /// /// This is thrown if the native CurlMulti handle wasn't /// created successfully. /// public CurlMultiCode Perform(ref int runningObjects) { ensureHandle(); return NativeMethods.curl_multi_perform(_pMulti, ref runningObjects); } /// /// Set internal file desriptor information before calling Select. /// /// /// A , hopefully CurlMultiCode.Ok /// /// /// This is thrown if the native CurlMulti handle wasn't /// created successfully. /// public CurlMultiCode FdSet() { ensureHandle(); #if USE_LIBCURLSHIM return NativeMethods.curl_shim_multi_fdset(_pMulti, _fdSets, ref _maxFd); #else NativeMethods.FD_ZERO(_fd_read); NativeMethods.FD_ZERO(_fd_write); NativeMethods.FD_ZERO(_fd_except); return NativeMethods.curl_multi_fdset(_pMulti, ref _fd_read, ref _fd_write, ref _fd_except, ref _maxFd); #endif } /// /// Call select() on the CurlEasy objects. /// /// /// The timeout for the internal select() call, /// in milliseconds. /// /// /// Number or objects with pending reads. /// /// /// This is thrown if the native CurlMulti handle wasn't /// created successfully. /// public int Select(int timeoutMillis) { ensureHandle(); #if USE_LIBCURLSHIM return NativeMethods.curl_shim_select(_maxFd + 1, _fdSets, timeoutMillis); #else var timeout = NativeMethods.timeval.Create(timeoutMillis); return NativeMethods.select(_maxFd + 1, ref _fd_read, ref _fd_write, ref _fd_except, ref timeout); //return NativeMethods.select2(_maxFd + 1, _fd_read, _fd_write, _fd_except, timeout); #endif } /// /// Obtain status information for a CurlMulti transfer. Requires /// CurlSharp be compiled with the libcurlshim helper. /// /// /// An array of objects, one for each /// object child. /// /// /// This is thrown if the native CurlMulti handle wasn't /// created successfully. /// public CurlMultiInfo[] InfoRead() { if (_bGotMultiInfo) return _multiInfo; _bGotMultiInfo = true; #if USE_LIBCURLSHIM var nMsgs = 0; var pInfo = NativeMethods.curl_shim_multi_info_read(_pMulti, ref nMsgs); if (pInfo != IntPtr.Zero) { _multiInfo = new CurlMultiInfo[nMsgs]; for (var i = 0; i < nMsgs; i++) { var msg = (CurlMessage) Marshal.ReadInt32(pInfo, i*12); var pEasy = Marshal.ReadIntPtr(pInfo, i*12 + 4); var code = (CurlCode) Marshal.ReadInt32(pInfo, i*12 + 8); _multiInfo[i] = new CurlMultiInfo(msg, (CurlEasy) _htEasy[pEasy], code); } NativeMethods.curl_shim_multi_info_free(pInfo); } return _multiInfo; #else throw new NotImplementedException( "Sorry, CurlMulti.InfoRead is not implemented on this system." ); #endif } } }