Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@
<Reference Include="System.Text.Encoding.Extensions" />
<Reference Include="System.Threading" />
<Reference Include="System.Threading.Thread" />
<Reference Include="System.Threading.Tasks" />
<Reference Include="System.Threading.Tasks.Extensions" />
<Reference Include="System.Xml.ReaderWriter" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(NetCoreAppCurrent)'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Globalization;
using System.Net;
using System.Collections;
using System.ComponentModel;
using System.Text;
using System.Diagnostics;
using System.Net;
using System.Runtime.InteropServices;
using System.Xml;
using System.Threading;
using System.Security.Cryptography.X509Certificates;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;

namespace System.DirectoryServices.Protocols
{
Expand Down Expand Up @@ -44,7 +43,6 @@ internal enum LdapResult
private bool _needRebind = false;
internal static Hashtable s_handleTable = null;
internal static object s_objectLock = null;
private readonly GetLdapResponseCallback _fd = null;
private static readonly Hashtable s_asyncResultTable = null;
private static readonly LdapPartialResultsProcessor s_partialResultsProcessor = null;
private static readonly ManualResetEvent s_waitHandle = null;
Expand Down Expand Up @@ -84,7 +82,6 @@ public LdapConnection(LdapDirectoryIdentifier identifier, NetworkCredential cred

public LdapConnection(LdapDirectoryIdentifier identifier, NetworkCredential credential, AuthType authType)
{
_fd = new GetLdapResponseCallback(ConstructResponse);
_directoryIdentifier = identifier;
_directoryCredential = (credential != null) ? new NetworkCredential(credential.UserName, credential.Password, credential.Domain) : null;

Expand Down Expand Up @@ -288,7 +285,9 @@ public DirectoryResponse SendRequest(DirectoryRequest request, TimeSpan requestT

if (error == 0 && messageID != -1)
{
return ConstructResponse(messageID, operation, ResultAll.LDAP_MSG_ALL, requestTimeout, true);
ValueTask<DirectoryResponse> vt = ConstructResponseAsync(messageID, operation, ResultAll.LDAP_MSG_ALL, requestTimeout, true, sync: true);
Debug.Assert(vt.IsCompleted);
return vt.GetAwaiter().GetResult();
}
else
{
Expand Down Expand Up @@ -379,7 +378,30 @@ public IAsyncResult BeginSendRequest(DirectoryRequest request, TimeSpan requestT

s_asyncResultTable.Add(asyncResult, messageID);

_fd.BeginInvoke(messageID, operation, ResultAll.LDAP_MSG_ALL, requestTimeout, true, new AsyncCallback(ResponseCallback), requestState);
_ = ResponseCallback(ConstructResponseAsync(messageID, operation, ResultAll.LDAP_MSG_ALL, requestTimeout, true, sync: false), requestState);

static async Task ResponseCallback(ValueTask<DirectoryResponse> vt, LdapRequestState requestState)
{
try
{
DirectoryResponse response = await vt.ConfigureAwait(false);
requestState._response = response;
}
catch (Exception e)
{
requestState._exception = e;
requestState._response = null;
}

// Signal waitable object, indicate operation completed and fire callback.
requestState._ldapAsync._manualResetEvent.Set();
requestState._ldapAsync._completed = true;

if (requestState._ldapAsync._callback != null && !requestState._abortCalled)
{
requestState._ldapAsync._callback(requestState._ldapAsync);
}
}

return asyncResult;
}
Expand All @@ -404,31 +426,6 @@ public IAsyncResult BeginSendRequest(DirectoryRequest request, TimeSpan requestT
throw ConstructException(error, operation);
}

private void ResponseCallback(IAsyncResult asyncResult)
{
LdapRequestState requestState = (LdapRequestState)asyncResult.AsyncState;

try
{
DirectoryResponse response = _fd.EndInvoke(asyncResult);
requestState._response = response;
}
catch (Exception e)
{
requestState._exception = e;
requestState._response = null;
}

// Signal waitable object, indicate operation completed and fire callback.
requestState._ldapAsync._manualResetEvent.Set();
requestState._ldapAsync._completed = true;

if (requestState._ldapAsync._callback != null && !requestState._abortCalled)
{
requestState._ldapAsync._callback(requestState._ldapAsync);
}
}

public void Abort(IAsyncResult asyncResult)
{
if (_disposed)
Expand Down Expand Up @@ -1404,7 +1401,7 @@ internal LdapMod[] BuildAttributes(CollectionBase directoryAttributes, ArrayList
return attributes;
}

internal DirectoryResponse ConstructResponse(int messageId, LdapOperation operation, ResultAll resultType, TimeSpan requestTimeOut, bool exceptionOnTimeOut)
internal async ValueTask<DirectoryResponse> ConstructResponseAsync(int messageId, LdapOperation operation, ResultAll resultType, TimeSpan requestTimeOut, bool exceptionOnTimeOut, bool sync)
{
var timeout = new LDAP_TIMEVAL()
{
Expand Down Expand Up @@ -1436,7 +1433,35 @@ internal DirectoryResponse ConstructResponse(int messageId, LdapOperation operat
needAbandon = false;
}

int error = LdapPal.GetResultFromAsyncOperation(_ldapHandle, messageId, (int)resultType, timeout, ref ldapResult);
int error;
if (sync)
{
error = LdapPal.GetResultFromAsyncOperation(_ldapHandle, messageId, (int)resultType, timeout, ref ldapResult);
}
else
{
timeout.tv_sec = 0;
timeout.tv_usec = 0;
int iterationDelay = 1;
// Underlying native libraries don't support callback-based function, so we will instead use polling and
// use a Stopwatch to track the timeout manually.
Stopwatch watch = Stopwatch.StartNew();
while (true)
{
error = LdapPal.GetResultFromAsyncOperation(_ldapHandle, messageId, (int)resultType, timeout, ref ldapResult);
if (error != 0 || (requestTimeOut != Threading.Timeout.InfiniteTimeSpan && watch.Elapsed > requestTimeOut))
{
break;
}
await Task.Delay(Math.Min(iterationDelay, 100)).ConfigureAwait(false);
if (iterationDelay < 100)
{
iterationDelay *= 2;
}
}
watch.Stop();
}

if (error != -1 && error != 0)
{
// parsing the result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading;
using System.Collections;
using System.Diagnostics;
using System.Threading.Tasks;

namespace System.DirectoryServices.Protocols
{
Expand Down Expand Up @@ -135,7 +136,9 @@ private void GetResultsHelper(LdapPartialAsyncResult asyncResult)

try
{
SearchResponse response = (SearchResponse)connection.ConstructResponse(asyncResult._messageID, LdapOperation.LdapSearch, resultType, asyncResult._requestTimeout, false);
ValueTask<DirectoryResponse> vt = connection.ConstructResponseAsync(asyncResult._messageID, LdapOperation.LdapSearch, resultType, asyncResult._requestTimeout, false, sync: true);
Debug.Assert(vt.IsCompleted);
SearchResponse response = (SearchResponse)vt.GetAwaiter().GetResult();

// This should only happen in the polling thread case.
if (response == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;
using System.DirectoryServices.Tests;
using System.Globalization;
using System.Net;
using System.Threading;
using Xunit;

namespace System.DirectoryServices.Protocols.Tests
Expand Down Expand Up @@ -534,7 +536,8 @@ private SearchResultEntry SearchOrganizationalUnit(LdapConnection connection, st
{
string filter = $"(&(objectClass=organizationalUnit)(ou={ouName}))";
SearchRequest searchRequest = new SearchRequest(rootDn, filter, SearchScope.OneLevel, null);
SearchResponse searchResponse = (SearchResponse) connection.SendRequest(searchRequest);
IAsyncResult asyncResult = connection.BeginSendRequest(searchRequest, PartialResultProcessing.NoPartialResultSupport, null, null);
SearchResponse searchResponse = (SearchResponse)connection.EndSendRequest(asyncResult);

if (searchResponse.Entries.Count > 0)
return searchResponse.Entries[0];
Expand Down