예제 #1
0
파일: HttpServer.cs 프로젝트: hc4/ceenhttpd
        /// <summary>
        /// Listens to incoming connections and calls the spawner method for each new connection
        /// </summary>
        /// <returns>Awaitable task.</returns>
        /// <param name="addr">The address to listen to.</param>
        /// <param name="usessl">A flag indicating if the socket listens for SSL requests</param>
        /// <param name="stoptoken">The stoptoken.</param>
        /// <param name="config">The server configuration</param>
        /// <param name="spawner">The method handling the new connection.</param>
        private static async Task ListenToSocketInternalAsync(IPEndPoint addr, bool usessl, CancellationToken stoptoken, ServerConfig config, Action <TcpClient, EndPoint, string, RunnerControl> spawner)
        {
            var rc = new RunnerControl(stoptoken, usessl, config);

            var listener = new TcpListener(addr);

            listener.Start(config.SocketBacklog);

            var taskid = SetLoggingSocketHandlerID();

            while (!stoptoken.IsCancellationRequested)
            {
                // Wait if there are too many active
                config.DebugLogHandler?.Invoke("Waiting for throttle", taskid, null);
                await rc.ThrottleTask;
                config.DebugLogHandler?.Invoke("Waiting for socket", taskid, null);
                var ls = listener.AcceptTcpClientAsync();

                if (await Task.WhenAny(rc.StopTask, ls) == ls)
                {
                    config.DebugLogHandler?.Invoke("Re-waiting for socket", taskid, null);
                    var client    = await ls;
                    var newtaskid = SetLoggingTaskHandlerID();

                    try
                    {
                        int wt, cpt;
                        ThreadPool.GetAvailableThreads(out wt, out cpt);
                        config.DebugLogHandler?.Invoke(string.Format("Threadpool says {0}, {1}", wt, cpt), taskid, newtaskid);

                        config.DebugLogHandler?.Invoke(string.Format("Spawning runner with id: {0}", newtaskid), taskid, newtaskid);

                        // Read the endpoint here to avoid crashes when invoking the spawner
                        var ep = client.Client.RemoteEndPoint;
                        ThreadPool.QueueUserWorkItem(x => spawner(client, ep, newtaskid, rc));
                    }
                    catch (Exception ex)
                    {
                        config.DebugLogHandler?.Invoke("Failed to listen to socket", taskid, ex);
                    }
                }
            }

            config.DebugLogHandler?.Invoke("Stopping", taskid, null);

            listener.Stop();
            rc.Stop(taskid);

            config.DebugLogHandler?.Invoke("Socket stopped, waiting for workers ...", taskid, null);
            await rc.FinishedTask;

            config.DebugLogHandler?.Invoke("Stopped", taskid, null);
        }
예제 #2
0
파일: HttpServer.cs 프로젝트: hc4/ceenhttpd
 /// <summary>
 /// Listens to incoming connections and calls the spawner method for each new connection
 /// </summary>
 /// <returns>Awaitable task.</returns>
 /// <param name="acceptAsync">Method that returns an accepted socket.</param>
 /// <param name="usessl">A flag indicating if the socket listens for SSL requests</param>
 /// <param name="stoptoken">The stoptoken.</param>
 /// <param name="config">The server configuration</param>
 /// <param name="spawner">The method handling the new connection.</param>
 public static Task ListenToSocketAsync(Func <CancellationToken, Task <KeyValuePair <long, EndPoint> > > acceptAsync, bool usessl, CancellationToken stoptoken, ServerConfig config, Action <long, EndPoint, string> spawner)
 {
     return(ListenToSocketInternalAsync(acceptAsync, usessl, stoptoken, config, (client, remoteendpoint, logid, controller) => spawner(client, remoteendpoint, logid)));
 }
예제 #3
0
파일: HttpServer.cs 프로젝트: hc4/ceenhttpd
        /// <summary>
        /// Listens to incoming connections and calls the spawner method for each new connection
        /// </summary>
        /// <returns>Awaitable task.</returns>
        /// <param name="acceptAsync">Method that returns an accepted socket.</param>
        /// <param name="usessl">A flag indicating if the socket listens for SSL requests</param>
        /// <param name="stoptoken">The stoptoken.</param>
        /// <param name="config">The server configuration</param>
        /// <param name="spawner">The method handling the new connection.</param>
        private static async Task ListenToSocketInternalAsync(Func <CancellationToken, Task <KeyValuePair <long, EndPoint> > > acceptAsync, bool usessl, CancellationToken stoptoken, ServerConfig config, Action <long, EndPoint, string, RunnerControl> spawner)
        {
            if (acceptAsync == null)
            {
                throw new ArgumentNullException(nameof(acceptAsync));
            }

            var rc = new RunnerControl(stoptoken, usessl, config);

            var taskid = SetLoggingSocketHandlerID();

            while (!stoptoken.IsCancellationRequested)
            {
                // Wait if there are too many active
                config.DebugLogHandler?.Invoke("Waiting for throttle", taskid, null);
                await rc.ThrottleTask;
                config.DebugLogHandler?.Invoke("Waiting for socket", taskid, null);
                var ls = acceptAsync(stoptoken);

                if (await Task.WhenAny(rc.StopTask, ls) == ls)
                {
                    config.DebugLogHandler?.Invoke("Re-waiting for socket", taskid, null);
                    var client    = await ls;
                    var newtaskid = SetLoggingTaskHandlerID();

                    try
                    {
                        int wt, cpt;
                        ThreadPool.GetAvailableThreads(out wt, out cpt);
                        config.DebugLogHandler?.Invoke(string.Format("Threadpool says {0}, {1}", wt, cpt), taskid, newtaskid);
                        config.DebugLogHandler?.Invoke(string.Format("Spawning runner with id: {0}", newtaskid), taskid, newtaskid);

                        ThreadPool.QueueUserWorkItem(x => spawner(client.Key, client.Value, newtaskid, rc));
                    }
                    catch (Exception ex)
                    {
                        config.DebugLogHandler?.Invoke("Failed to listen to socket", taskid, ex);
                    }
                }
            }

            config.DebugLogHandler?.Invoke("Stopping", taskid, null);
            rc.Stop(taskid);

            config.DebugLogHandler?.Invoke("Socket stopped, waiting for workers ...", taskid, null);
            await rc.FinishedTask;

            config.DebugLogHandler?.Invoke("Stopped", taskid, null);
        }
예제 #4
0
파일: HttpServer.cs 프로젝트: hc4/ceenhttpd
 /// <summary>
 /// Creates and initializes a new InterProcess bridge
 /// </summary>
 /// <returns>The app domain bridge.</returns>
 /// <param name="usessl">If set to <c>true</c> use ssl.</param>
 /// <param name="config">The server config.</param>
 public static InterProcessBridge CreateInterProcessBridge(bool usessl, ServerConfig config)
 {
     return(new InterProcessBridge(usessl, config));
 }
예제 #5
0
파일: HttpServer.cs 프로젝트: hc4/ceenhttpd
 /// <summary>
 /// Listens to incoming connections and calls the spawner method for each new connection
 /// </summary>
 /// <returns>Awaitable task.</returns>
 /// <param name="addr">The address to listen to.</param>
 /// <param name="usessl">A flag indicating if the socket listens for SSL requests</param>
 /// <param name="stoptoken">The stoptoken.</param>
 /// <param name="config">The server configuration</param>
 /// <param name="spawner">The method handling the new connection.</param>
 public static Task ListenToSocketAsync(IPEndPoint addr, bool usessl, CancellationToken stoptoken, ServerConfig config, Action <TcpClient, EndPoint, string> spawner)
 {
     return(ListenToSocketInternalAsync(addr, usessl, stoptoken, config, (client, remoteendpoint, logid, controller) => spawner(client, remoteendpoint, logid)));
 }
예제 #6
0
파일: HttpServer.cs 프로젝트: hc4/ceenhttpd
 /// <summary>
 /// Creates and initializes a new AppDomain bridge
 /// </summary>
 /// <returns>The app domain bridge.</returns>
 /// <param name="usessl">If set to <c>true</c> use ssl.</param>
 /// <param name="config">The server config.</param>
 public static AppDomainBridge CreateAppDomainBridge(bool usessl, ServerConfig config)
 {
     return(new AppDomainBridge(usessl, config));
 }
예제 #7
0
파일: HttpServer.cs 프로젝트: hc4/ceenhttpd
 /// <summary>
 /// Setup this instance
 /// </summary>
 /// <param name="usessl">If set to <c>true</c> use ssl.</param>
 /// <param name="config">The configuration.</param>
 public InterProcessBridge(bool usessl, ServerConfig config)
 {
     Setup(usessl, config);
 }
예제 #8
0
파일: HttpServer.cs 프로젝트: hc4/ceenhttpd
 /// <summary>
 /// Initializes a new instance of the <see cref="T:Ceen.Httpd.HttpServer.AppDomainBridge"/> class.
 /// </summary>
 /// <param name="usessl">If set to <c>true</c> use ssl.</param>
 /// <param name="config">The server config.</param>
 public AppDomainBridge(bool usessl, ServerConfig config)
 {
     Setup(usessl, config);
 }
예제 #9
0
        /// <summary>
        /// Parses url encoded form data
        /// </summary>
        /// <returns>An awaitable task.</returns>
        /// <param name="reader">The stream to read from.</param>
        /// <param name="config">The server configuration.</param>
        /// <param name="idletime">The maximum idle time.</param>
        /// <param name="timeouttask">A task that signals request timeout.</param>
        /// <param name="stoptask">A task that signals server stop.</param>
        internal async Task ParseFormData(BufferedStreamReader reader, ServerConfig config, TimeSpan idletime, Task timeouttask, Task stoptask)
        {
            if (this.IsContentType("application/x-www-form-urlencoded"))
            {
                if (this.ContentLength != 0)
                {
                    if (this.ContentLength > config.MaxUrlEncodedFormSize)
                    {
                        throw new HttpException(HttpStatusCode.PayloadTooLarge);
                    }

                    var enc = this.GetEncodingForContentType();
                    ParseQueryString(
                        enc.GetString(
                            this.ContentLength > 0
                            ? await reader.RepeatReadAsync(this.ContentLength, idletime, timeouttask, stoptask)
                            : await reader.ReadUntilCrlfAsync(config.MaxRequestLineSize, config.MaxUrlEncodedFormSize, idletime, timeouttask, stoptask)
                            ), this.Form);
                }

                this.Body = new LimitedBodyStream(reader, 0, idletime, timeouttask, stoptask);
            }
            else if (RequestUtility.IsMultipartRequest(this.ContentType) && this.ContentLength > 0 && this.ContentLength < config.MaxUrlEncodedFormSize && config.AutoParseMultipartFormData)
            {
                await ParseMultiPart(
                    async (headers, stream) =>
                {
                    var dispositionItems = RequestUtility.SplitHeaderLine(headers["Content-Disposition"]);
                    if (!string.Equals(dispositionItems.FirstOrDefault().Key, "form-data", StringComparison.OrdinalIgnoreCase))
                    {
                        throw new HttpException(HttpStatusCode.BadRequest);
                    }

                    var name = RequestUtility.GetHeaderComponent(headers["Content-Disposition"], "name");
                    if (string.IsNullOrWhiteSpace("name"))
                    {
                        throw new HttpException(HttpStatusCode.BadRequest);
                    }

                    var filename = RequestUtility.GetHeaderComponent(headers["Content-Disposition"], "filename");
                    var charset  = RequestUtility.GetHeaderComponent(headers["Content-Type"], "charset") ?? "ascii";

                    if (string.IsNullOrWhiteSpace(filename))
                    {
                        using (var sr = new StreamReader(stream, RequestUtility.GetEncodingForCharset(charset)))
                        {
                            var rtask = sr.ReadToEndAsync();
                            var rt    = await Task.WhenAny(timeouttask, stoptask, rtask);
                            if (rt != rtask)
                            {
                                if (rt == stoptask)
                                {
                                    throw new TaskCanceledException();
                                }
                                else
                                {
                                    throw new HttpException(HttpStatusCode.RequestTimeout);
                                }
                            }

                            this.Form[name] = rtask.Result;
                        }
                    }
                    else
                    {
                        var me = new MultipartItem(headers)
                        {
                            Name     = name,
                            Filename = filename,
                            Data     = new MemoryStream()
                        };

                        Task rtask;
                        Task rt;

                        using (var cs = new CancellationTokenSource(idletime))
                        {
                            rtask = stream.CopyToAsync(me.Data, 8 * 1024, cs.Token);
                            rt    = await Task.WhenAny(timeouttask, stoptask, rtask);
                        }

                        if (rt != rtask)
                        {
                            if (rt == stoptask)
                            {
                                throw new TaskCanceledException();
                            }
                            else
                            {
                                throw new HttpException(HttpStatusCode.RequestTimeout);
                            }
                        }

                        rtask.GetAwaiter().GetResult();
                        me.Data.Position = 0;

                        this.Files.Add(me);
                    }
                },
                    reader,
                    config,
                    idletime,
                    timeouttask,
                    stoptask
                    );

                this.Body = new LimitedBodyStream(reader, 0, idletime, timeouttask, stoptask);
            }
            else
            {
                this.Body = new LimitedBodyStream(reader, this.ContentLength, idletime, timeouttask, stoptask);
            }
        }
예제 #10
0
        /// <summary>
        /// Parses the stream items, if sent as multipart encoded
        /// </summary>
        /// <param name="itemparser">The parser method</param>
        /// <param name="reader">The stream to read from.</param>
        /// <param name="config">The server configuration.</param>
        /// <param name="idletime">The maximum idle time.</param>
        /// <param name="timeouttask">A task that signals request timeout.</param>
        /// <param name="stoptask">A task that signals server stop.</param>
        /// <returns>An awaitable task</returns>
        private async Task ParseMultiPart(Func <IDictionary <string, string>, Stream, Task> itemparser, BufferedStreamReader reader, ServerConfig config, TimeSpan idletime, Task timeouttask, Task stoptask)
        {
            if ((this.ContentType ?? "").StartsWith("multipart/form-data", StringComparison.OrdinalIgnoreCase))
            {
                if (this.ContentLength > config.MaxPostSize)
                {
                    throw new HttpException(HttpStatusCode.PayloadTooLarge);
                }

                var startpos = reader.Position;
                var trail    = new byte[2];
                var parts    = this.ContentType.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                var bndpart  = parts.FirstOrDefault(x => x.Trim().StartsWith("boundary", StringComparison.OrdinalIgnoreCase)) ?? string.Empty;
                var boundary = bndpart.Split(new char[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
                if (string.IsNullOrWhiteSpace(boundary))
                {
                    throw new HttpException(HttpStatusCode.BadRequest);
                }

                // Since we have read the headers, we have consumed the initial CRLF
                // so we adjust the initial boundary reading to skip the CRLF
                var itemboundary = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary);
                var tmp          = await reader.RepeatReadAsync(itemboundary.Length - 2, idletime, timeouttask, stoptask);

                if (!Enumerable.SequenceEqual(itemboundary.Skip(2), tmp))
                {
                    throw new HttpException(HttpStatusCode.BadRequest);
                }

                await reader.RepeatReadAsync(trail, 0, 2, idletime, timeouttask, stoptask);

                if (trail[0] != '\r' || trail[1] != '\n')
                {
                    throw new HttpException(HttpStatusCode.BadRequest);
                }

                do
                {
                    var headers = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase).WithDefaultValue(null);

                    await reader.ReadHeaders(
                        config.MaxRequestLineSize,
                        config.MaxRequestHeaderSize,
                        idletime,
                        line =>
                    {
                        var components = line.Split(new char[] { ':' }, 2);
                        if (components.Length != 2 || string.IsNullOrWhiteSpace(components[0]))
                        {
                            throw new HttpException(HttpStatusCode.BadRequest);
                        }

                        headers[components[0].Trim()] = (components[1] ?? string.Empty).Trim();
                    },
                        timeouttask,
                        stoptask
                        );

                    await itemparser(headers, reader.GetDelimitedSubStream(itemboundary, idletime, timeouttask, stoptask));

                    await reader.RepeatReadAsync(trail, 0, 2, idletime, timeouttask, stoptask);
                }while (trail[0] == '\r' && trail[1] == '\n');


                if (trail[0] != '-' || trail[1] != '-')
                {
                    throw new HttpException(HttpStatusCode.BadRequest);
                }

                await reader.RepeatReadAsync(trail, 0, 2, idletime, timeouttask, stoptask);

                if (trail[0] != '\r' || trail[1] != '\n')
                {
                    throw new HttpException(HttpStatusCode.BadRequest);
                }

                if (this.ContentLength > 0 && this.ContentLength != (reader.Position - startpos))
                {
                    throw new HttpException(HttpStatusCode.BadRequest);
                }
            }
        }