Beispiel #1
0
        static CurlHandler()
        {
            // curl_global_init call handled by Interop.LibCurl's cctor

            Interop.Http.CurlFeatures features = Interop.Http.GetSupportedFeatures();
            s_supportsSSL = (features & Interop.Http.CurlFeatures.CURL_VERSION_SSL) != 0;
            s_supportsAutomaticDecompression = (features & Interop.Http.CurlFeatures.CURL_VERSION_LIBZ) != 0;
            s_supportsHttp2Multiplexing      = (features & Interop.Http.CurlFeatures.CURL_VERSION_HTTP2) != 0 && Interop.Http.GetSupportsHttp2Multiplexing();

            if (NetEventSource.IsEnabled)
            {
                EventSourceTrace($"libcurl: {CurlVersionDescription} {CurlSslVersionDescription} {features}");
            }

            // By default every CurlHandler gets its own MultiAgent.  But for some backends,
            // we need to restrict the number of threads involved in processing libcurl work,
            // so we create a single MultiAgent that's used by all handlers.
            bool useSingleton = false;

            UseSingletonMultiAgent(ref useSingleton);
            if (useSingleton)
            {
                s_singletonSharedAgent = new MultiAgent(null);
            }
        }
Beispiel #2
0
 private static void EventSourceTrace(
     string message,
     MultiAgent agent = null, EasyRequest easy = null, [CallerMemberName] string memberName = null)
 {
     if (NetEventSource.IsEnabled)
     {
         EventSourceTraceCore(message, agent, easy, memberName);
     }
 }
Beispiel #3
0
 private static void EventSourceTrace <TArg0, TArg1, TArg2>
     (string formatMessage, TArg0 arg0, TArg1 arg1, TArg2 arg2,
     MultiAgent agent = null, EasyRequest easy = null, [CallerMemberName] string memberName = null)
 {
     if (NetEventSource.IsEnabled)
     {
         EventSourceTraceCore(string.Format(formatMessage, arg0, arg1, arg2), agent, easy, memberName);
     }
 }
Beispiel #4
0
        private static void EventSourceTraceCore(string message, MultiAgent agent, EasyRequest easy, string memberName)
        {
            // If we weren't handed a multi agent, see if we can get one from the EasyRequest
            if (agent == null && easy != null)
            {
                agent = easy._associatedMultiAgent;
            }

            NetEventSource.Log.HandlerMessage(
                (agent?.RunningWorkerId).GetValueOrDefault(),
                easy?.Task.Id ?? 0,
                memberName,
                message);
        }
Beispiel #5
0
            public EasyRequest(CurlHandler handler, MultiAgent agent, HttpRequestMessage requestMessage, CancellationToken cancellationToken) :
                base(TaskCreationOptions.RunContinuationsAsynchronously)
            {
                Debug.Assert(handler != null, $"Expected non-null {nameof(handler)}");
                Debug.Assert(agent != null, $"Expected non-null {nameof(agent)}");
                Debug.Assert(requestMessage != null, $"Expected non-null {nameof(requestMessage)}");

                _handler = handler;
                _associatedMultiAgent = agent;
                _requestMessage       = requestMessage;

                _cancellationToken = cancellationToken;
                _responseMessage   = new CurlResponseMessage(this);
            }
Beispiel #6
0
        public void CreatePacmanImage(Form formInstance, int StartXCoordinate, int StartYCoordinate)
        {
            // Create Pacman Image
            xStart = StartXCoordinate;
            yStart = StartYCoordinate;
            agents = new MultiAgent(Form1.gameboard.Matrix, new Point(xStart, yStart), Form1.ghost.GetGhostsCoordinates());



            PacmanImage.Name     = "PacmanImage";
            PacmanImage.SizeMode = PictureBoxSizeMode.AutoSize;
            Set_Pacman();
            formInstance.Controls.Add(PacmanImage);
            PacmanImage.BringToFront();
        }
Beispiel #7
0
            private static bool TryGetEasyRequest(IntPtr curlPtr, out EasyRequest easy)
            {
                Debug.Assert(curlPtr != IntPtr.Zero, "curlPtr is not null");

                IntPtr   gcHandlePtr;
                CURLcode getInfoResult = Interop.Http.EasyGetInfoPointer(curlPtr, CURLINFO.CURLINFO_PRIVATE, out gcHandlePtr);

                if (getInfoResult == CURLcode.CURLE_OK)
                {
                    return(MultiAgent.TryGetEasyRequestFromGCHandle(gcHandlePtr, out easy));
                }

                Debug.Fail($"Failed to get info on a completing easy handle: {getInfoResult}");
                easy = null;
                return(false);
            }
Beispiel #8
0
 public CurlHandler()
 {
     _agent = new MultiAgent(this);
 }
Beispiel #9
0
        private static void VerboseTrace(string text = null, [CallerMemberName] string memberName = null, EasyRequest easy = null, MultiAgent agent = null)
        {
            // If we weren't handed a multi agent, see if we can get one from the EasyRequest
            if (agent == null && easy != null && easy._associatedMultiAgent != null)
            {
                agent = easy._associatedMultiAgent;
            }
            int?agentId = agent != null ? agent.RunningWorkerId : null;

            // Get an ID string that provides info about which MultiAgent worker and which EasyRequest this trace is about
            string ids = "";

            if (agentId != null || easy != null)
            {
                ids = "(" +
                      (agentId != null ? "M#" + agentId : "") +
                      (agentId != null && easy != null ? ", " : "") +
                      (easy != null ? "E#" + easy.Task.Id : "") +
                      ")";
            }

            // Create the message and trace it out
            string msg = string.Format("[{0, -30}]{1, -16}: {2}", memberName, ids, text);

            Interop.Sys.PrintF("%s\n", msg);
        }
Beispiel #10
0
        private static void VerboseTrace(string text = null, [CallerMemberName] string memberName = null, EasyRequest easy = null, MultiAgent agent = null)
        {
            // If we weren't handed a multi agent, see if we can get one from the EasyRequest
            if (agent == null && easy != null && easy._associatedMultiAgent != null)
            {
                agent = easy._associatedMultiAgent;
            }
            int? agentId = agent != null ? agent.RunningWorkerId : null;

            // Get an ID string that provides info about which MultiAgent worker and which EasyRequest this trace is about
            string ids = "";
            if (agentId != null || easy != null)
            {
                ids = "(" +
                    (agentId != null ? "M#" + agentId : "") +
                    (agentId != null && easy != null ? ", " : "") +
                    (easy != null ? "E#" + easy.Task.Id : "") +
                    ")";
            }

            // Create the message and trace it out
            string msg = string.Format("[{0, -30}]{1, -16}: {2}", memberName, ids, text);
            Interop.Sys.PrintF("%s\n", msg);
        }
Beispiel #11
0
            /// <summary>
            /// Transfers up to <paramref name="length"/> data from the <paramref name="easy"/>'s
            /// request content (non-memory) stream to the buffer.
            /// </summary>
            /// <returns>The number of bytes transferred.</returns>
            private static ulong TransferDataFromRequestStream(IntPtr buffer, int length, EasyRequest easy)
            {
                MultiAgent multi = easy._associatedMultiAgent;

                // First check to see whether there's any data available from a previous asynchronous read request.
                // If there is, the transfer state's Task field will be non-null, with its Result representing
                // the number of bytes read.  The Buffer will then contain all of that read data.  If the Count
                // is 0, then this is the first time we're checking that Task, and so we populate the Count
                // from that read result.  After that, we can transfer as much data remains between Offset and
                // Count.  Multiple callbacks may pull from that one read.

                EasyRequest.SendTransferState sts = easy._sendTransferState;
                if (sts != null)
                {
                    // Is there a previous read that may still have data to be consumed?
                    if (sts._task != null)
                    {
                        if (!sts._task.IsCompleted)
                        {
                            // We have a previous read that's not yet completed.  This should be quite rare, but it can
                            // happen when we're unpaused prematurely, potentially due to the request still finishing
                            // being sent as the server starts to send a response.  Since we still have the outstanding
                            // read, we simply re-pause.  When the task completes (which could have happened immediately
                            // after the check). the continuation we previously created will fire and queue an unpause.
                            // Since all of this processing is single-threaded on the current thread, that unpause request 
                            // is guaranteed to happen after this re-pause.
                            multi.VerboseTrace("Re-pausing reading after a spurious un-pause", easy: easy);
                            return Interop.Http.CURL_READFUNC_PAUSE;
                        }

                        // Determine how many bytes were read on the last asynchronous read.
                        // If nothing was read, then we're done and can simply return 0 to indicate
                        // the end of the stream.
                        int bytesRead = sts._task.GetAwaiter().GetResult(); // will throw if read failed
                        Debug.Assert(bytesRead >= 0 && bytesRead <= sts._buffer.Length, "ReadAsync returned an invalid result length: " + bytesRead);
                        if (bytesRead == 0)
                        {
                            multi.VerboseTrace("End of stream from stored task", easy: easy);
                            sts.SetTaskOffsetCount(null, 0, 0);
                            return 0;
                        }

                        // If Count is still 0, then this is the first time after the task completed
                        // that we're examining the data: transfer the bytesRead to the Count.
                        if (sts._count == 0)
                        {
                            multi.VerboseTrace("ReadAsync completed with bytes: " + bytesRead, easy: easy);
                            sts._count = bytesRead;
                        }

                        // Now Offset and Count are both accurate.  Determine how much data we can copy to libcurl...
                        int availableData = sts._count - sts._offset;
                        Debug.Assert(availableData > 0, "There must be some data still available.");

                        // ... and copy as much of that as libcurl will allow.
                        int bytesToCopy = Math.Min(availableData, length);
                        Marshal.Copy(sts._buffer, sts._offset, buffer, bytesToCopy);
                        multi.VerboseTrace("Copied " + bytesToCopy + " bytes from request stream", easy: easy);

                        // Update the offset.  If we've gone through all of the data, reset the state 
                        // so that the next time we're called back we'll do a new read.
                        sts._offset += bytesToCopy;
                        Debug.Assert(sts._offset <= sts._count, "Offset should never exceed count");
                        if (sts._offset == sts._count)
                        {
                            sts.SetTaskOffsetCount(null, 0, 0);
                        }

                        // Return the amount of data copied
                        Debug.Assert(bytesToCopy > 0, "We should never return 0 bytes here.");
                        return (ulong)bytesToCopy;
                    }

                    // sts was non-null but sts.Task was null, meaning there was no previous task/data
                    // from which to satisfy any of this request.
                }
                else // sts == null
                {
                    // Allocate a transfer state object to use for the remainder of this request.
                    easy._sendTransferState = sts = new EasyRequest.SendTransferState();
                }

                Debug.Assert(sts != null, "By this point we should have a transfer object");
                Debug.Assert(sts._task == null, "There shouldn't be a task now.");
                Debug.Assert(sts._count == 0, "Count should be zero.");
                Debug.Assert(sts._offset == 0, "Offset should be zero.");

                // If we get here, there was no previously read data available to copy.
                // Initiate a new asynchronous read.
                Task<int> asyncRead = easy._requestContentStream.ReadAsyncInternal(
                    sts._buffer, 0, Math.Min(sts._buffer.Length, length), easy._cancellationToken);
                Debug.Assert(asyncRead != null, "Badly implemented stream returned a null task from ReadAsync");

                // Even though it's "Async", it's possible this read could complete synchronously or extremely quickly.  
                // Check to see if it did, in which case we can also satisfy the libcurl request synchronously in this callback.
                if (asyncRead.IsCompleted)
                {
                    multi.VerboseTrace("ReadAsync completed immediately", easy: easy);

                    // Get the amount of data read.
                    int bytesRead = asyncRead.GetAwaiter().GetResult(); // will throw if read failed
                    if (bytesRead == 0)
                    {
                        multi.VerboseTrace("End of stream from quick returning ReadAsync", easy: easy);
                        return 0;
                    }

                    // Copy as much as we can.
                    int bytesToCopy = Math.Min(bytesRead, length);
                    Debug.Assert(bytesToCopy > 0 && bytesToCopy <= sts._buffer.Length, "ReadAsync quickly returned an invalid result length: " + bytesToCopy);
                    Marshal.Copy(sts._buffer, 0, buffer, bytesToCopy);
                    multi.VerboseTrace("Copied " + bytesToCopy + " from quick returning ReadAsync", easy: easy);

                    // If we read more than we were able to copy, stash it away for the next read.
                    if (bytesToCopy < bytesRead)
                    {
                        multi.VerboseTrace("Stashing away " + (bytesRead - bytesToCopy) + " bytes for next read.", easy: easy);
                        sts.SetTaskOffsetCount(asyncRead, bytesToCopy, bytesRead);
                    }

                    // Return the number of bytes read.
                    return (ulong)bytesToCopy;
                }

                // Otherwise, the read completed asynchronously.  Store the task, and hook up a continuation 
                // such that the connection will be unpaused once the task completes.
                sts.SetTaskOffsetCount(asyncRead, 0, 0);
                asyncRead.ContinueWith((t, s) =>
                {
                    EasyRequest easyRef = (EasyRequest)s;
                    easyRef._associatedMultiAgent.RequestUnpause(easyRef);
                }, easy, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);

                // Then pause the connection.
                multi.VerboseTrace("Pausing the connection", easy: easy);
                return Interop.Http.CURL_READFUNC_PAUSE;
            }
Beispiel #12
0
 public CurlHandler()
 {
     // If the shared MultiAgent was initialized, use it.
     // Otherwise, create a new MultiAgent for this handler.
     _agent = s_singletonSharedAgent ?? new MultiAgent(this);
 }