/***************************************************************************
*
* 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
}
}
}