Beispiel #1
0
        public Task <SocketReceiveResult> ReceiveAsync()
        {
            ThrowIfDisposed();

            var tcs = new TaskCompletionSource <SocketReceiveResult>();

            EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0);
            var      state = new AsyncReceiveState(_Socket, receivedFromEndPoint);

            state.TaskCompletionSource = tcs;

#if NETSTANDARD1_6
            _Socket.ReceiveFromAsync(new ArraySegment <Byte>(state.Buffer), SocketFlags.None, state.RemoteEndPoint)
            .ContinueWith((task, asyncState) =>
            {
                if (task.Status != TaskStatus.Faulted)
                {
                    var receiveState            = asyncState as AsyncReceiveState;
                    receiveState.RemoteEndPoint = task.Result.RemoteEndPoint;
                    ProcessResponse(receiveState, () => task.Result.ReceivedBytes, LocalIPAddress);
                }
            }, state);
#else
            _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state);
#endif

            return(tcs.Task);
        }
Beispiel #2
0
        public Task <SocketReceiveResult> ReceiveAsync(CancellationToken cancellationToken)
        {
            ThrowIfDisposed();
            var      tcs = new TaskCompletionSource <SocketReceiveResult>();
            EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0);

            var state = new AsyncReceiveState(_Socket, receivedFromEndPoint);

            state.TaskCompletionSource = tcs;

            cancellationToken.Register(() => tcs.TrySetCanceled());

            _receiveSocketAsyncEventArgs.RemoteEndPoint = receivedFromEndPoint;
            _currentReceiveTaskCompletionSource         = tcs;

            try
            {
                var willRaiseEvent = _Socket.ReceiveFromAsync(_receiveSocketAsyncEventArgs);

                if (!willRaiseEvent)
                {
                    _receiveSocketAsyncEventArgs_Completed(this, _receiveSocketAsyncEventArgs);
                }
            }
            catch (Exception ex)
            {
                tcs.TrySetException(ex);
            }

            return(tcs.Task);
        }
Beispiel #3
0
        public System.Threading.Tasks.Task <ReceivedUdpData> ReceiveAsync()
        {
            ThrowIfDisposed();

            var tcs = new TaskCompletionSource <ReceivedUdpData>();

            System.Net.EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0);
            var state = new AsyncReceiveState(_Socket, receivedFromEndPoint);

            state.TaskCompletionSource = tcs;
#if NETSTANDARD1_3
            _Socket.ReceiveFromAsync(new System.ArraySegment <Byte>(state.Buffer), System.Net.Sockets.SocketFlags.None, state.EndPoint)
            .ContinueWith((task, asyncState) =>
            {
                if (task.Status != TaskStatus.Faulted)
                {
                    var receiveState      = asyncState as AsyncReceiveState;
                    receiveState.EndPoint = task.Result.RemoteEndPoint;
                    ProcessResponse(receiveState, () => task.Result.ReceivedBytes);
                }
            }, state);
#else
            _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, System.Net.Sockets.SocketFlags.None, ref state.EndPoint,
                                     new AsyncCallback((result) => ProcessResponse(state, () => state.Socket.EndReceiveFrom(result, ref state.EndPoint))), state);
#endif

            return(tcs.Task);
        }
Beispiel #4
0
        //  handles the request on a separate ThreadPool thread.
        private void HandleRequestAsync(AsyncReceiveState asyncState)
        {
            //  TODO: first try pull the request from the request cache, otherwise parse it now
            //  convert the raw request bytes/string into an HttpRequest object for ease of use
            string  reqString = Text.Encoding.ASCII.GetString(asyncState.Buffer).Trim('\0');
            Request req;

            //  pull the request object from the request cache, or create it now
            if (RequestCache.ContainsKey(reqString))
            {
                req = RequestCache[reqString];
            }
            else
            {
                req = new Request(reqString, this, asyncState.Socket, asyncState.Site);
                RequestCache.TryAdd(reqString, req);
            }

            //  first try to serve the response from the output cache
            bool servedFromCache = false;

            //bool cacheAllowed = true;
            if (EnableOutputCache && req.CacheAllowed)
            {
                if (OutputCache.ContainsKey((req.AbsPath + req.QueryString)))
                {
                    Response res = OutputCache[(req.AbsPath + req.QueryString)];
                    SendResponse(req, res, asyncState.Socket);
                    servedFromCache = true;
                    DebugMessage(("Serving resource from cache: "
                                  + (req.AbsPath + ".")), DebugMessageType.UsageMessage, "HandleRequestAsync");
                }
            }

            //  response couldn't be served from cache, handle the request according to the site role
            if (servedFromCache)
            {
                //  if the current site is a reverse proxy/load balancer with upstream servers defined, forward the request to another server,
                //    otherwise handle the request by this server like normal
                if (asyncState.Site.Role == "Standard")
                {
                    //  raise an event to handle the request/response cycle (this can be overridden during implementation to allow for custom handling)
                    HandleRequest(new Tuple <object, object>(req, asyncState.Socket), null);
                }
                else if (asyncState.Site.Role == "Load Balancer")
                {
                    //  parse the upstream servers and select one using the defined algorithm
                    string[] upstreams = asyncState.Site.Upstream.Split(',');
                    Random   r         = new Random();
                    int      i         = r.Next(0, upstreams.Length);
                    //  forward the request to the selected upstream server
                    ProxyRequest(new Tuple <object, object, object>(req, upstreams[i], asyncState.Socket), null); //???
                }
            }
        }
Beispiel #5
0
        private static void ProcessResponse(AsyncReceiveState state, Func <int> receiveData)
        {
            try
            {
                var bytesRead = receiveData();

                var ipEndPoint = state.EndPoint as IPEndPoint;
                state.TaskCompletionSource.SetResult(
                    new ReceivedUdpData()
                {
                    Buffer        = state.Buffer,
                    ReceivedBytes = bytesRead,
                    ReceivedFrom  = new UdpEndPoint()
                    {
                        IPAddress = ipEndPoint.Address.ToString(),
                        Port      = ipEndPoint.Port
                    }
                }
                    );
            }
            catch (ObjectDisposedException)
            {
                state.TaskCompletionSource.SetCanceled();
            }
            catch (SocketException se)
            {
                if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown)
                {
                    state.TaskCompletionSource.SetException(se);
                }
                else
                {
                    state.TaskCompletionSource.SetCanceled();
                }
            }
#if NETSTANDARD
            // Unrecoverable exceptions should not be getting caught and will be dealt with on a broad level by a high-level catch-all handler
            // https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/breaking-change-rules.md#exceptions
#else
            catch (StackOverflowException)             // Handle this manually as we may not be able to call a sub method to check exception type etc.
            {
                throw;
            }
#endif
            catch (Exception ex)
            {
                if (ex.IsCritical())                 //Critical exceptions that indicate memory corruption etc. shouldn't be caught.
                {
                    throw;
                }

                state.TaskCompletionSource.SetException(ex);
            }
        }
Beispiel #6
0
        private void AsyncClientConnected(IAsyncResult ar)
        {
            //  get the async state object returned by the callback
            AsyncSendState asyncState = ((AsyncSendState)(ar.AsyncState));
            object         state      = asyncState.State;
            //  end the async connection request so we can check if we are connected to the server
            bool connectSuccessful = false;

            try
            {
                //  call the EndConnect method which will succeed or throw an error depending on the result of the connection
                asyncState.Socket.EndConnect(ar);
                //  at this point, the EndConnect succeeded and we are connected to the server! handle the success outside this Try block.
                connectSuccessful = true;
                ConnectSucceeded(state, null);
            }
            catch (Exception ex)
            {
                //  at this point, the EndConnect failed and we are NOT connected to the server!
                DebugMessage("Could not connect to the server.", DebugMessageType.ErrorMessage, "Connect", ex.Message);
                ConnectFailed(state, null);
            }

            //  if the client has connected, proceed
            if (connectSuccessful)
            {
                //  start waiting for messages from the server
                AsyncReceiveState receiveState = new AsyncReceiveState(ReceiveBufferSize, state);
                receiveState.Socket = asyncState.Socket;
                receiveState.Socket.BeginReceive(receiveState.Buffer, 0, ReceiveBufferSize, SocketFlags.None, new AsyncCallback(DataReceived), receiveState);
                //  make a request to the server
                AsyncSendState sendState = new AsyncSendState(asyncState.Socket, SendBufferSize, state);
                //  if the path is a directory, ensure it has a trailing /
                // If IO.Path.GetExtension(_request) = "" Then
                //     If _request.EndsWith("/") = False Then
                //         _request &= "/"
                //     End If
                // End If
                //  construct the GET request string and byte array:
                //  NOTE: node.js requires two vbCrLf terminator where other servers only require one. IIS 7.5 requires HTTP/1.1 and Host
                //    header or will not return headers with the response.
                string reqString = "";
                byte[] reqBytes  = null;
                reqString = ("GET "
                             + (_req.Path + (" HTTP/1.1" + ('\n' + ("Host: "
                                                                    + (_req.Host + ('\n' + '\n')))))));
                reqBytes = System.Text.Encoding.ASCII.GetBytes(reqString);
                //  send the reqBytes data to the server
                LogMessage(reqString, null);
                sendState.Socket.BeginSend(reqBytes, 0, reqBytes.Length, SocketFlags.None, new AsyncCallback(DataSent), sendState);
            }
        }
Beispiel #7
0
        public System.Threading.Tasks.Task<ReceivedUdpData> ReceiveAsync()
        {
            ThrowIfDisposed();

            var tcs = new TaskCompletionSource<ReceivedUdpData>();

            System.Net.EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0);
            var state = new AsyncReceiveState(_Socket, receivedFromEndPoint);
            state.TaskCompletionSource = tcs;
            _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, System.Net.Sockets.SocketFlags.None, ref state.EndPoint, new AsyncCallback(this.ProcessResponse), state);

            return tcs.Task;
        }
Beispiel #8
0
        public System.Threading.Tasks.Task <ReceivedUdpData> ReceiveAsync()
        {
            ThrowIfDisposed();

            var tcs = new TaskCompletionSource <ReceivedUdpData>();

            System.Net.EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0);
            var state = new AsyncReceiveState(_Socket, receivedFromEndPoint);

            state.TaskCompletionSource = tcs;
            _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, System.Net.Sockets.SocketFlags.None, ref state.EndPoint, new AsyncCallback(this.ProcessResponse), state);

            return(tcs.Task);
        }
Beispiel #9
0
        public System.Threading.Tasks.Task <ReceivedUdpData> ReceiveAsync()
        {
            ThrowIfDisposed();

            var tcs = new TaskCompletionSource <ReceivedUdpData>();

            System.Net.EndPoint receivedFromEndPoint = new IPEndPoint(GetDefaultIpAddress(_Socket), 0);
            var state = new AsyncReceiveState(_Socket, receivedFromEndPoint);

            state.TaskCompletionSource = tcs;
#if NETSTANDARD1_3
            _Socket.ReceiveFromAsync(new System.ArraySegment <Byte>(state.Buffer), System.Net.Sockets.SocketFlags.None, state.EndPoint)
            .ContinueWith((task, asyncState) =>
            {
                if (this.IsDisposed)
                {
                    return;
                }

                try
                {
                    if (task.Status != TaskStatus.Faulted)
                    {
                        var receiveState      = asyncState as AsyncReceiveState;
                        receiveState.EndPoint = task.Result.RemoteEndPoint;
                        ProcessResponse(receiveState, () => task.Result.ReceivedBytes);
                    }
                }
                catch (ObjectDisposedException) { if (!this.IsDisposed)
                                                  {
                                                      throw;
                                                  }
                }                                                                                        //Only rethrow disposed exceptions if we're NOT disposed, because then they are unexpected.
            }, state);
#else
            try
            {
                _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, System.Net.Sockets.SocketFlags.None, ref state.EndPoint,
                                         new AsyncCallback((result) => ProcessResponse(state, () => state.Socket.EndReceiveFrom(result, ref state.EndPoint))), state);
            }
            catch (ObjectDisposedException) { if (!this.IsDisposed)
                                              {
                                                  throw;
                                              }
            }                                                                            //Only rethrow disposed exceptions if we're NOT disposed, because then they are unexpected.
#endif

            return(tcs.Task);
        }
Beispiel #10
0
        // '' <summary>
        // '' This callback fires when the client socket receives data (an HTTP response) asynchronously from the server.
        // '' </summary>
        // '' NOTE:
        // ''     Since HTTP is by nature a stream, a response will be sent by the server which is broken down into pieces per the
        // ''     server's configured SendBufferSize, so we must continue issuing BeginReceive's on the socket until the end of the stream.
        // ''     We can properly detect the end of stream per the HTTP spec which states that the Content-Length header should be used to stop
        // ''     issuiing BeginReceive's when the total bytes received equals the Header + Content length, or in the case of a
        // ''     "Transfer-Encoding: chunked" header we look for a null character which signals termination of the chunked stream.
        // '' <param name="ar"></param>
        // '' <remarks></remarks>
        private void DataReceived(IAsyncResult ar)
        {
            //  get the async state object returned by the callback
            AsyncReceiveState asyncState     = ((AsyncReceiveState)(ar.AsyncState));
            string            responseChunk  = System.Text.Encoding.ASCII.GetString(asyncState.Buffer).TrimEnd(Convert.ToChar(0));
            string            responseString = (asyncState.Packet + responseChunk);

            //  if we haven't determined the Content-Length yet, try doing so now by attempting to extract it from the responseChunk:
            //  TODO: this halts on an error when we try a random URL.
            //  TODO: we need to handle the various transfer types here...check for chunked encoding and parse the size etc...
            if (asyncState.ReceiveSize == 0)
            {
                string contentLength = "";
                //string transferEncoding = "";
                contentLength          = responseChunk.SubstringEx("Content-Length: ", '\n');
                asyncState.ReceiveSize = int.Parse(contentLength);
            }

            //  if we haven't determined the Content offset yet, try doing so now. content is located after the header and two newlines (crlf) which is 4 bytes.
            if (asyncState.ContentOffset == 0)
            {
                int contentOffset;
                contentOffset            = (responseChunk.IndexOf(Environment.NewLine + Environment.NewLine) + 4);
                asyncState.ContentOffset = contentOffset;
            }

            //  add the responseChunk's length to the total received bytes count:
            asyncState.TotalBytesReceived = (asyncState.TotalBytesReceived + responseChunk.Length);
            //  if we haven't received all the bytes yet, issue another BeginReceive, otherwise we have all the data so we handle the response
            if (asyncState.TotalBytesReceived - asyncState.ContentOffset < asyncState.ReceiveSize)
            {
                AsyncReceiveState receiveState = new AsyncReceiveState(ReceiveBufferSize, asyncState.State);
                receiveState.Socket             = asyncState.Socket;
                receiveState.Packet             = responseString;
                receiveState.ReceiveSize        = asyncState.ReceiveSize;
                receiveState.TotalBytesReceived = asyncState.TotalBytesReceived;
                receiveState.Socket.BeginReceive(receiveState.Buffer, 0, ReceiveBufferSize, SocketFlags.None, new AsyncCallback(DataReceived), receiveState);
            }
            else
            {
                HandleResponse(responseString, (EventArgs)asyncState.State);
            }
        }
Beispiel #11
0
        //  sends the http response to the client
        private void SendResponse(Request req, Response res, Socket client)
        {
            //  convert the response into bytes and package it in an async object for callback purposes:
            // Dim responseBytes() As Byte = res.BuildResponseBytes
            AsyncSendState sendState = new AsyncSendState(client, SendBufferSize, null);

            sendState.BytesToSend = res.ResponseBytes;
            sendState.Tag         = req.AbsPath;
            //  start sending the response to the client in an async fashion:
            try
            {
                client.BeginSend(res.ResponseBytes, 0, res.ResponseBytes.Length, SocketFlags.None, SendResponseAsync, sendState);
            }
            catch (Exception ex)
            {
                DebugMessage("Unhandled exception in SendResponse when trying to send data to the client.", DebugMessageType.UsageMessage, "SendResponse", ex.Message);
            }

            //  determine whether or not to continue receiving more data from the client:
            //  TODO: tidy this up a bit instead of setting a property to true then checking it right afterwards...
            //  IMPORTANT: for keep-alive connections we should make a final receive call to the client and if the client does not send a Connection: keep-alive header then we know to disconnect
            if (res.Headers.ContainsKey("Connection") && res.Headers["Connection"].ToString().ToLower() == "keep-alive")
            {
                sendState.Persistent = true;
            }

            //  start receiving more data from the client in an async fashion
            if (sendState.Persistent)
            {
                AsyncReceiveState receiveState = new AsyncReceiveState(ReceiveBufferSize, null);
                receiveState.Site   = req.Site;
                receiveState.Socket = client;
                try
                {
                    receiveState.Socket.BeginReceive(receiveState.Buffer, 0, ReceiveBufferSize, SocketFlags.None, new AsyncCallback(RequestReceivedAsync), receiveState);
                }
                catch (Exception ex)
                {
                    DebugMessage(("SendResponse encountered an exception when trying to BeginReceive on the client socket. " + ex.Message), DebugMessageType.ErrorMessage, "ClientRequest", ex.Message);
                }
            }
        }
Beispiel #12
0
        // '' <summary>
        // '' Accepts the client request, builds the request/response objects and passes them to the event handler where the response
        // '' object will be finalized and sent back to the client.
        // '' </summary>
        // '' <param name="ar"></param>
        // '' <remarks></remarks>
        private void RequestReceivedAsync(IAsyncResult ar)
        {
            //  get the async state object:
            AsyncReceiveState asyncState = ((AsyncReceiveState)(ar.AsyncState));
            int numBytesReceived         = 0;

            try
            {
                //  call EndReceive which will give us the number of bytes received
                numBytesReceived = asyncState.Socket.EndReceive(ar);
            }
            catch (SocketException ex)
            {
                //  if we get a ConnectionReset exception, it could indicate that the client has disconnected
                if (ex.SocketErrorCode == SocketError.ConnectionReset)
                {
                    DebugMessage("EndReceive on the client socket failed because the client has disconnected.", DebugMessageType.UsageMessage, "RequestReceivedAsync", ex.Message);
                    //  update the connected client count in a thread safe way
                    Threading.Interlocked.Decrement(ref ConnectedClients);
                    // RaiseEvent ClientDisconnected(asyncState.Socket)
                    return;
                }
            }

            //  if we get numBytesReceived equal to zero, it could indicate that the client has disconnected
            //  TODO: does this actually disconnect the client though, or just assume it was?
            if (numBytesReceived == 0)
            {
                //  update the connected client count in a thread safe way
                Threading.Interlocked.Decrement(ref ConnectedClients);
                // RaiseEvent ClientDisconnected(asyncState.Socket)
                return;
            }

            //  if we've reached this point, we were able to parse the IAsyncResult which contains our raw request bytes, so proceed
            //    to handle it on a separate ThreadPool thread; it is important that we free up the I/O completion port being
            //    used for this async operation as soon as possible, therefore we don't even attempt to parse the requestBytes at
            //    this point and just immediately pass the raw request bytes to a ThreadPool thread for further processing
            Threading.ThreadPool.QueueUserWorkItem(new Threading.WaitCallback((o) => { HandleRequestAsync((AsyncReceiveState)o); }), asyncState);
        }
Beispiel #13
0
        private static void ProcessResponse(AsyncReceiveState state, Func <int> receiveData, IpAddressInfo localIpAddress)
        {
            try
            {
                var bytesRead = receiveData();

                var ipEndPoint = state.RemoteEndPoint as IPEndPoint;
                state.TaskCompletionSource.SetResult(
                    new SocketReceiveResult
                {
                    Buffer         = state.Buffer,
                    ReceivedBytes  = bytesRead,
                    RemoteEndPoint = ToIpEndPointInfo(ipEndPoint),
                    LocalIPAddress = localIpAddress
                }
                    );
            }
            catch (ObjectDisposedException)
            {
                state.TaskCompletionSource.SetCanceled();
            }
            catch (SocketException se)
            {
                if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown)
                {
                    state.TaskCompletionSource.SetException(se);
                }
                else
                {
                    state.TaskCompletionSource.SetCanceled();
                }
            }
            catch (Exception ex)
            {
                state.TaskCompletionSource.SetException(ex);
            }
        }
Beispiel #14
0
        // '' <summary>
        // '' Accepts the client that is attempting to connect, allows another client to connect (whenever that may be), and finally begins receiving data from the client that just connected.
        // '' </summary>
        // '' <param name="ar"></param>
        // '' <remarks></remarks>
        private void ClientConnectedAsync(IAsyncResult ar)
        {
            //  get the async state object from the async BeginAccept method, which contains the server's listening socket
            Site   s            = ((Site)(ar.AsyncState));
            Socket clientSocket = null;

            try
            {
                //  accept the client connection, giving us the client socket to work with:
                clientSocket = s.Socket.EndAccept(ar);
                //  update the connected client count in a thread safe way
                Threading.Interlocked.Increment(ref ConnectedClients);
                // RaiseEvent ClientConnected(clientSocket)
                // DebugMessage("Client connected. Total connections: " & _connections, DebugMessageType.UsageMessage, "ClientConnectedAsync")
                //  begin accepting another client connection:
                s.Socket.BeginAccept(0, new AsyncCallback(ClientConnectedAsync), s);
            }
            catch (ObjectDisposedException ex1)
            {
                //  if we get an ObjectDisposedException it that means the server socket terminated while this async method was still active
                DebugMessage("The server was closed before the async method could complete.", DebugMessageType.WarningMessage, "ClientConnectedAsync", ex1.Message);
                return;
            }
            catch (Exception ex2)
            {
                DebugMessage("An unhandled exception occurred in ClientConnectedAsync.", DebugMessageType.ErrorMessage, "ClientConnectedAsync", ex2.Message);
                return;
            }

            //  begin receiving data (http requests) from the client socket:
            AsyncReceiveState asyncState = new AsyncReceiveState(ReceiveBufferSize, null);

            asyncState.Site   = s;
            asyncState.Socket = clientSocket;
            asyncState.Socket.BeginReceive(asyncState.Buffer, 0, ReceiveBufferSize, SocketFlags.None, new AsyncCallback(RequestReceivedAsync), asyncState);
        }