コード例 #1
0
        /// <summary>
        /// Process incoming data from the WebSocket
        /// </summary>
        /// <param name="webSocket">Remote WebSocket</param>
        /// <param name="endpointConnection">Local UNIX socket connection</param>
        /// <param name="sessionId">Session ID</param>
        /// <param name="cancellationToken">Cancellation token</param>
        /// <returns>Asynchronous task</returns>
        private async Task ReadFromWebSocket(WebSocket webSocket, HttpEndpointConnection endpointConnection, int sessionId, CancellationToken cancellationToken)
        {
            int bufferSize = _configuration.GetValue("WebSocketBufferSize", 8192);

            byte[] rxBuffer = new byte[bufferSize];

            do
            {
                // Read data from the WebSocket
                WebSocketReceiveResult result = await webSocket.ReceiveAsync(rxBuffer, cancellationToken);

                if (result.MessageType == WebSocketMessageType.Close)
                {
                    // Remote end is closing this connection
                    return;
                }
                if (result.MessageType == WebSocketMessageType.Binary)
                {
                    // Terminate the connection if binary content is received
                    await CloseConnection(webSocket, WebSocketCloseStatus.InvalidMessageType, "Only text commands are supported");

                    break;
                }

                // Forward it to the UNIX socket connection
                ReceivedHttpRequest receivedHttpRequest = new ReceivedHttpRequest
                {
                    Body      = Encoding.UTF8.GetString(rxBuffer, 0, result.Count),
                    SessionId = sessionId
                };
                await endpointConnection.SendHttpRequest(receivedHttpRequest, cancellationToken);
            }while (!cancellationToken.IsCancellationRequested);
        }
コード例 #2
0
        /// <summary>
        /// Read another message from the web socket and print it to the console
        /// </summary>
        /// <param name="connection">WebSocket connection</param>
        /// <param name="cancellationToken">Cancellation token</param>
        /// <returns>Asynchronous task</returns>
        private static async Task ReadFromWebSocket(HttpEndpointConnection connection, CancellationToken cancellationToken)
        {
            // Note that no content has been received when we get here for the first time.
            // In this case, it may take a while before/if data can be received from the client
            do
            {
                ReceivedHttpRequest websocketRequest = await connection.ReadRequest(cancellationToken);

                Console.WriteLine(websocketRequest.Body);
            }while (!cancellationToken.IsCancellationRequested);
        }
コード例 #3
0
        /// <summary>
        /// Handle an message
        /// </summary>
        /// <param name="context">Context unique for this handler instance</param>
        /// <param name="message">Message to process</param>
        public void HandleUpstream(IPipelineHandlerContext context, IPipelineMessage message)
        {
            if (message is Closed)
            {
                _bodyBytesLeft = 0;
                _headerParser.Reset();
            }
            else if (message is Received)
            {
                var msg = (Received)message;

                // complete the body
                if (_bodyBytesLeft > 0)
                {
                    var bytesToSend = Math.Min(_bodyBytesLeft, msg.BufferReader.RemainingLength);
                    _bodyBytesLeft -= bytesToSend;
                    context.SendUpstream(message);
                    return;
                }

                _headerParser.Parse(msg.BufferReader);
                if (_headerCompleted)
                {
                    var request = (IRequest)_message;

                    var ourRequest = _message as HttpRequest;
                    if (ourRequest != null)
                    {
                        ourRequest.RemoteEndPoint = msg.RemoteEndPoint as IPEndPoint;
                    }
                    request.AddHeader("RemoteAddress", msg.RemoteEndPoint.ToString());

                    var receivedHttpRequest = new ReceivedHttpRequest(request);

                    _headerParser.Reset();
                    _headerCompleted = false;

                    context.SendUpstream(receivedHttpRequest);
                    if (msg.BufferReader.RemainingLength > 0)
                    {
                        context.SendUpstream(msg);
                    }
                }

                return;
            }

            context.SendUpstream(message);
        }
コード例 #4
0
        /// <summary>
        /// Invokes the <see cref="IErrorFormatter.Format"/> and guards against any exceptions that it might throw.
        /// </summary>
        /// <param name="response">Response to send back</param>
        /// <param name="msg">Request pipeline message</param>
        /// <param name="exception">Caught exception</param>
        protected virtual void FormatException(IResponse response, ReceivedHttpRequest msg, HttpException exception)
        {
            var formatterContext = new ErrorFormatterContext(exception, msg.HttpRequest, response);

            try
            {
                _formatter.Format(formatterContext);
            }
            catch (Exception err)
            {
                _logger.Error(string.Format("Formatter '{0}' failed to process request.", _formatter.GetType().FullName), err);
                var formatter = new SimpleErrorFormatter();
                formatter.Format(formatterContext);
            }
        }
コード例 #5
0
        public void Test()
        {
            var service = Substitute.For <IBodyDecoder>();
            var context = Substitute.For <IPipelineHandlerContext>();
            var request = new HttpRequest("GET", "/", "HTTP/1.1");

            request.Body = new MemoryStream(Encoding.ASCII.GetBytes("Hello world!"));
            var msg = new ReceivedHttpRequest(request);
            var sut = new BodyDecoder(service, 65535, 65535);

            sut.HandleUpstream(context, msg);
            var received = new Received(new IPEndPoint(IPAddress.Loopback, 9231), Substitute.For <IBufferReader>());

            sut.HandleUpstream(context, received);
        }
コード例 #6
0
        /// <summary>
        /// Handle an message
        /// </summary>
        /// <param name="context">Context unique for this handler instance</param>
        /// <param name="message">Message to process</param>
        public void HandleUpstream(IPipelineHandlerContext context, IPipelineMessage message)
        {
            if (message is Closed)
            {
                _bodyBytesLeft = 0;
                _parser.Reset();
            }
            else if (message is Received)
            {
                var msg = (Received)message;

                // complete the body
                if (_bodyBytesLeft > 0)
                {
                    _bodyBytesLeft -= msg.BufferSlice.Count;
                    context.SendUpstream(message);
                    return;
                }

                var httpMsg = _parser.Parse(msg.BufferSlice);
                if (httpMsg != null)
                {
                    var recivedHttpMsg = new ReceivedHttpRequest((IRequest)httpMsg);
                    _bodyBytesLeft = recivedHttpMsg.HttpRequest.ContentLength;
                    _parser.Reset();

                    // send up the message to let someone else handle the body
                    context.SendUpstream(recivedHttpMsg);
                    if (msg.BufferSlice.RemainingLength > 0)
                    {
                        context.SendUpstream(msg);
                    }
                }

                return;
            }

            context.SendUpstream(message);
        }
コード例 #7
0
        /// <summary>
        /// Handle an message
        /// </summary>
        /// <param name="context">Context unique for this handler instance</param>
        /// <param name="message">Message to process</param>
        public void HandleUpstream(IPipelineHandlerContext context, IPipelineMessage message)
        {
            if (message is Closed)
            {
                _bodyBytesLeft = 0;
                _parser.Reset();
            }
            else if (message is Received)
            {
                var msg = (Received) message;

                // complete the body
                if (_bodyBytesLeft > 0)
                {
                    _bodyBytesLeft -= msg.BufferSlice.Count;
                    context.SendUpstream(message);
                    return;
                }

                var httpMsg = _parser.Parse(msg.BufferSlice);
                if (httpMsg != null)
                {
                    var recivedHttpMsg = new ReceivedHttpRequest((IRequest) httpMsg);
                    _bodyBytesLeft = recivedHttpMsg.HttpRequest.ContentLength;
                    _parser.Reset();

                    // send up the message to let someone else handle the body
                    context.SendUpstream(recivedHttpMsg);
                    if (msg.BufferSlice.RemainingLength > 0)
                        context.SendUpstream(msg);
                }

                return;
            }

            context.SendUpstream(message);
        }
コード例 #8
0
 /// <summary>
 /// Invokes the <see cref="IErrorFormatter.Format"/> and guards against any exceptions that it might throw.
 /// </summary>
 /// <param name="response">Response to send back</param>
 /// <param name="msg">Request pipeline message</param>
 /// <param name="exception">Caught exception</param>
 protected virtual void FormatException(IResponse response, ReceivedHttpRequest msg, HttpException exception)
 {
     var formatterContext = new ErrorFormatterContext(exception, msg.HttpRequest, response);
     try
     {
         _formatter.Format(formatterContext);
     }
     catch (Exception err)
     {
         _logger.Error(string.Format("Formatter '{0}' failed to process request.", _formatter.GetType().FullName), err);
         var formatter = new SimpleErrorFormatter();
         formatter.Format(formatterContext);
     }
 }
コード例 #9
0
        /// <summary>
        /// Process a RESTful HTTP request
        /// </summary>
        /// <param name="context">HTTP context</param>
        /// <param name="endpointConnection">Endpoint connection</param>
        /// <param name="sessionId">Session ID</param>
        /// <returns>Asynchronous task</returns>
        private async Task ProcessRestRequst(HttpContext context, HttpEndpointConnection endpointConnection, int sessionId)
        {
            // Deal with RESTful HTTP requests. Read the full HTTP request body first
            string body;

            using (StreamReader reader = new StreamReader(context.Request.Body))
            {
                body = await reader.ReadToEndAsync();
            }

            // Prepare the HTTP request notification
            ReceivedHttpRequest receivedHttpRequest = new ReceivedHttpRequest
            {
                Body        = body,
                ContentType = context.Request.ContentType,
                SessionId   = sessionId
            };

            foreach (var item in context.Request.Headers)
            {
                receivedHttpRequest.Headers.Add(item.Key, item.Value.ToString());
            }

            foreach (var item in context.Request.Query)
            {
                receivedHttpRequest.Queries.Add(item.Key, item.Value.ToString());
            }

            // Send it to the third-party application and get a response type
            await endpointConnection.SendHttpRequest(receivedHttpRequest);

            SendHttpResponse httpResponse = await endpointConnection.GetHttpResponse();

            // Send the response to the HTTP client
            context.Response.StatusCode = httpResponse.StatusCode;
            if (httpResponse.ResponseType == HttpResponseType.File)
            {
                context.Response.ContentType = "application/octet-stream";

                using FileStream fs = new FileStream(httpResponse.Response, FileMode.Open, FileAccess.Read);
                await fs.CopyToAsync(context.Response.Body);
            }
            else
            {
                switch (httpResponse.ResponseType)
                {
                case HttpResponseType.StatusCode:
                    context.Response.ContentType = null;
                    break;

                case HttpResponseType.PlainText:
                    context.Response.ContentType = "text/plain;charset=utf-8";
                    break;

                case HttpResponseType.JSON:
                    context.Response.ContentType = "application/json";
                    break;
                }

                await context.Response.BodyWriter.WriteAsync(Encoding.UTF8.GetBytes(httpResponse.Response));
            }
        }
コード例 #10
0
        /// <summary>
        /// Called when a new HTTP or WebSocket request has been received
        /// </summary>
        /// <param name="unixSocket">UNIX socket for HTTP endpoints</param>
        /// <param name="requestConnection">Connection from the UNIX socket representing the HTTP or WebSocket request</param>
        private static async void OnHttpRequestReceived(HttpEndpointUnixSocket unixSocket, HttpEndpointConnection requestConnection)
        {
            // Note that a call to ReadRequest can throw an exception in case DCS only created a test connection!
            // DCS may do that when an application attempts to register an existing endpoint twice

            if (_method == HttpEndpointType.WebSocket)
            {
                if (_websocketConnected)
                {
                    await requestConnection.SendResponse(1000, "Demo application only supports one WebSocket connection");

                    return;
                }

                _websocketConnected = true;
                if (!_quiet)
                {
                    Console.WriteLine("WebSocket connected, type 'close' to close this connection");
                }

                try
                {
                    using CancellationTokenSource cts = new CancellationTokenSource();
                    Task webSocketTask = ReadFromWebSocket(requestConnection, cts.Token);
                    Task consoleTask   = ReadFromConsole(requestConnection, cts.Token);

                    await Task.WhenAny(webSocketTask, consoleTask);

                    cts.Cancel();
                }
                catch (Exception e)
                {
                    if (!(e is OperationCanceledException) && !(e is SocketException))
                    {
                        Console.WriteLine("Unexpected error:");
                        Console.WriteLine(e);
                    }
                }
                finally
                {
                    _websocketConnected = false;
                    if (!_quiet)
                    {
                        Console.WriteLine("WebSocket disconnected");
                    }
                }
            }
            else
            {
                // Read the HTTP response from the client
                ReceivedHttpRequest request = await requestConnection.ReadRequest();

                if (string.IsNullOrWhiteSpace(_cmd))
                {
                    // Write this event to the console if possible
                    if (!_quiet)
                    {
                        Console.WriteLine("Got new HTTP request from session {0}", request.SessionId);
                    }

                    // Only print a demo response in case no process is supposed to be started
                    string response = $"This demo text has been returned from a third-party application.\n\nMethod: {_method}\nSession ID: {request.SessionId}";
                    if (request.Headers.Count > 0)
                    {
                        response += "\n\nHeaders:";
                        foreach (var kv in request.Headers)
                        {
                            response += $"\n{kv.Key} = {kv.Value}";
                        }
                    }
                    if (request.Queries.Count > 0)
                    {
                        response += "\n\nQueries:";
                        foreach (var kv in request.Queries)
                        {
                            response += $"\n{kv.Key} = {kv.Value}";
                        }
                    }
                    if (!string.IsNullOrWhiteSpace(request.Body))
                    {
                        response += "\n\nBody:\n" + request.Body;
                    }
                    await requestConnection.SendResponse(200, response, HttpResponseType.PlainText);
                }
                else
                {
                    // Replace query values in the arguments
                    string args = _cmd;
                    foreach (var kv in request.Queries)
                    {
                        args.Replace($"%{kv.Key}%", kv.Value);
                    }

                    // Prepare the process start info
                    using Process process = new Process
                          {
                              StartInfo = new ProcessStartInfo
                              {
                                  FileName  = _cmd,
                                  Arguments = args,
                                  RedirectStandardOutput = true
                              }
                          };

                    // Start a process and wait for it to exit
                    string output = "";
                    process.OutputDataReceived += (object sender, DataReceivedEventArgs e) => output += e.Data;
                    process.ErrorDataReceived  += (object sender, DataReceivedEventArgs e) => output += e.Data;
                    if (process.Start())
                    {
                        process.BeginOutputReadLine();
                        process.BeginErrorReadLine();
                        process.WaitForExit();
                        await requestConnection.SendResponse(200, output, HttpResponseType.PlainText);
                    }
                    else
                    {
                        await requestConnection.SendResponse(501, "Failed to start process", HttpResponseType.StatusCode);
                    }
                }
            }
        }
コード例 #11
0
        /// <summary>
        /// Process a RESTful HTTP request
        /// </summary>
        /// <param name="context">HTTP context</param>
        /// <param name="endpoint">HTTP endpoint descriptor</param>
        /// <param name="endpointConnection">Endpoint connection</param>
        /// <param name="sessionId">Session ID</param>
        /// <returns>Asynchronous task</returns>
        private async Task ProcessRestRequst(HttpContext context, HttpEndpoint endpoint, HttpEndpointConnection endpointConnection, int sessionId)
        {
            string body;

            if (endpoint.IsUploadRequest)
            {
                // Write to a temporary file
                string filename = Path.GetTempFileName();
                using (FileStream fileStream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read))
                {
                    await context.Request.Body.CopyToAsync(fileStream);
                }

                // Adjust the file permissions if possible
                endpointConnection.GetPeerCredentials(out _, out int uid, out int gid);
                if (uid != 0 && gid != 0)
                {
                    LinuxApi.Commands.Chown(filename, uid, gid);
                }
                body = filename;
            }
            else
            {
                // Read the body content
                using StreamReader reader = new StreamReader(context.Request.Body);
                body = await reader.ReadToEndAsync();
            }

            // Prepare the HTTP request notification
            ReceivedHttpRequest receivedHttpRequest = new ReceivedHttpRequest
            {
                Body        = body,
                ContentType = context.Request.ContentType,
                SessionId   = sessionId
            };

            foreach (var item in context.Request.Headers)
            {
                receivedHttpRequest.Headers.Add(item.Key, item.Value.ToString());
            }

            foreach (var item in context.Request.Query)
            {
                receivedHttpRequest.Queries.Add(item.Key, item.Value.ToString());
            }

            // Send it to the third-party application and get a response type
            await endpointConnection.SendHttpRequest(receivedHttpRequest);

            SendHttpResponse httpResponse = await endpointConnection.GetHttpResponse();

            // Send the response to the HTTP client
            context.Response.StatusCode = httpResponse.StatusCode;
            if (httpResponse.ResponseType == HttpResponseType.File)
            {
                context.Response.ContentType = "application/octet-stream";

                using FileStream fs = new FileStream(httpResponse.Response, FileMode.Open, FileAccess.Read);
                await fs.CopyToAsync(context.Response.Body);
            }
            else
            {
                switch (httpResponse.ResponseType)
                {
                case HttpResponseType.StatusCode:
                    context.Response.ContentType = null;
                    break;

                case HttpResponseType.PlainText:
                    context.Response.ContentType = "text/plain;charset=utf-8";
                    break;

                case HttpResponseType.JSON:
                    context.Response.ContentType = "application/json";
                    break;
                }

                await context.Response.BodyWriter.WriteAsync(Encoding.UTF8.GetBytes(httpResponse.Response));
            }
        }