public static Task <string> GetTitleAsync([NotNull] this WebResponse thisValue, IOSettings settings = null, CancellationToken token = default(CancellationToken))
        {
            token.ThrowIfCancellationRequested();

            string         result       = null;
            StringBuilder  sb           = new StringBuilder();
            IOReadSettings readSettings = new IOReadSettings(settings, (bf, _) =>
            {
                token.ThrowIfCancellationRequested();
                sb.Append(bf);

                string contents = sb.ToString();
                Match m         = WebResponseHelper.TitleCheckExpression.Match(contents);

                if (m.Success)
                {
                    // we found a <title></title> match =]
                    result = m.Groups[1].Value;
                    return(false);
                }

                // reached end of head-block; no title found =[
                return(!contents.Contains("</head>", StringComparison.OrdinalIgnoreCase));
            });

            return(ReadAsync(thisValue, readSettings, token)
                   .ContinueWith(_ => result, token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default));
        }
        public static Task <(string Title, string Buffer)> PeekAsync([NotNull] this WebResponse thisValue, IOSettings settings = null, CancellationToken token = default(CancellationToken))
        {
            token.ThrowIfCancellationRequested();

            bool           titleFound   = false;
            bool           bufferFilled = false;
            string         title        = null;
            int            bufferSize   = settings?.BufferSize ?? IOSettings.BUFFER_DEFAULT;
            StringBuilder  bufferFetch  = new StringBuilder(bufferSize);
            StringBuilder  sb           = new StringBuilder(bufferSize);
            IOReadSettings readSettings = new IOReadSettings(settings, (buf, _) =>
            {
                token.ThrowIfCancellationRequested();
                sb.Append(buf);

                string contents = sb.ToString();

                if (!bufferFilled)
                {
                    bufferFetch.Append(buf);
                    bufferFilled = bufferFetch.Length >= bufferSize;
                }

                if (!titleFound)
                {
                    Match m = WebResponseHelper.TitleCheckExpression.Match(contents);

                    if (m.Success)
                    {
                        // we found a <title></title> match =]
                        title      = m.Groups[1].Value;
                        titleFound = true;
                    }
                    else
                    {
                        // reached end of head-block; no title found =[
                        titleFound = contents.Contains("</head>", StringComparison.OrdinalIgnoreCase);
                    }
                }

                token.ThrowIfCancellationRequested();
                return(!titleFound || !bufferFilled);
            });

            return(ReadAsync(thisValue, readSettings, token)
                   .ContinueWith(_ => (title, bufferFetch.ToString()), token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default));
        }
        public static (string Title, string Buffer) Peek([NotNull] this WebResponse thisValue, IOSettings settings = null)
        {
            bool           titleFound   = false;
            bool           bufferFilled = false;
            string         title        = null;
            int            bufferSize   = settings?.BufferSize ?? IOSettings.BUFFER_DEFAULT;
            StringBuilder  bufferFetch  = new StringBuilder(bufferSize);
            StringBuilder  sb           = new StringBuilder(bufferSize);
            IOReadSettings readSettings = new IOReadSettings(settings, (buf, _) =>
            {
                sb.Append(buf);

                string contents = sb.ToString();

                if (!bufferFilled)
                {
                    bufferFetch.Append(buf);
                    bufferFilled = bufferFetch.Length >= bufferSize;
                }

                if (!titleFound)
                {
                    Match m = WebResponseHelper.TitleCheckExpression.Match(contents);

                    if (m.Success)
                    {
                        // we found a <title></title> match =]
                        title      = m.Groups[1].Value;
                        titleFound = true;
                    }
                    else
                    {
                        // reached end of head-block; no title found =[
                        titleFound = contents.Contains("</head>", StringComparison.OrdinalIgnoreCase);
                    }
                }

                return(!titleFound || !bufferFilled);
            });

            return(Read(thisValue, readSettings)
                                ? (title, bufferFetch.ToString())
                                : (null, null));
        }
        public static string GetString([NotNull] this WebResponse thisValue, IOSettings settings = null)
        {
            bool           bufferFilled = false;
            int            bufferSize   = settings?.BufferSize ?? IOSettings.BUFFER_DEFAULT;
            StringBuilder  sb           = new StringBuilder(bufferSize);
            IOReadSettings readSettings = new IOReadSettings(settings, (buf, _) =>
            {
                if (bufferFilled)
                {
                    return(!bufferFilled);
                }
                sb.Append(buf);
                bufferFilled = sb.Length >= bufferSize;
                return(!bufferFilled);
            });

            return(Read(thisValue, readSettings) && sb.Length > 0
                                ? sb.ToString()
                                : null);
        }
        public static Task <string> GetStringAsync([NotNull] this WebResponse thisValue, IOSettings settings = null, CancellationToken token = default(CancellationToken))
        {
            token.ThrowIfCancellationRequested();

            bool           bufferFilled = false;
            int            bufferSize   = settings?.BufferSize ?? IOSettings.BUFFER_DEFAULT;
            StringBuilder  sb           = new StringBuilder(bufferSize);
            IOReadSettings readSettings = new IOReadSettings(settings, (buf, _) =>
            {
                if (!bufferFilled)
                {
                    sb.Append(buf);
                    bufferFilled = sb.Length >= bufferSize;
                }

                token.ThrowIfCancellationRequested();
                return(!bufferFilled);
            });

            return(ReadAsync(thisValue, readSettings, token)
                   .ContinueWith(_ => sb.ToString(), token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default));
        }
        public static string GetTitle([NotNull] this WebResponse thisValue, IOSettings settings = null)
        {
            string         result       = null;
            StringBuilder  sb           = new StringBuilder();
            IOReadSettings readSettings = new IOReadSettings(settings, (bf, _) =>
            {
                sb.Append(bf);

                string contents = sb.ToString();
                Match m         = WebResponseHelper.TitleCheckExpression.Match(contents);

                if (m.Success)
                {
                    // we found a <title></title> match =]
                    result = m.Groups[1].Value;
                    return(false);
                }

                // reached end of head-block; no title found =[
                return(!contents.Contains("</head>", StringComparison.OrdinalIgnoreCase));
            });

            return(Read(thisValue, readSettings) ? result : null);
        }
        public static Task <UrlSearchResult> SearchAsync([NotNull] this WebResponse thisValue, string searchFor, UrlSearchFlags flags, IOSettings settings = null, CancellationToken token = default(CancellationToken))
        {
            token.ThrowIfCancellationRequested();
            bool hasTitleFlag  = flags.FastHasFlag(UrlSearchFlags.Title);
            bool hasBufferFlag = flags.FastHasFlag(UrlSearchFlags.Buffer);
            bool hasSearchFlag = !string.IsNullOrEmpty(searchFor);

            UrlSearchResult result = new UrlSearchResult();

            try
            {
                Func <char[], int, bool> onRead;

                if (hasTitleFlag || hasBufferFlag || hasSearchFlag)
                {
                    StringBuilder    sb         = new StringBuilder();
                    StringComparison comparison = flags.FastHasFlag(UrlSearchFlags.IgnoreCase)
                                                        ? StringComparison.InvariantCultureIgnoreCase
                                                        : StringComparison.InvariantCulture;
                    onRead = (c, _) =>
                    {
                        if (result.Status == UrlSearchStatus.Unknown)
                        {
                            result.Status = UrlSearchStatus.Success;
                        }
                        sb.Append(c);

                        if (hasBufferFlag)
                        {
                            result.Buffer = sb.ToString();
                            hasBufferFlag = false;
                        }

                        token.ThrowIfCancellationRequested();
                        if (!hasTitleFlag && !hasSearchFlag)
                        {
                            return(false);
                        }

                        string contents = sb.ToString();

                        if (hasTitleFlag)
                        {
                            Match m = WebResponseHelper.TitleCheckExpression.Match(contents);

                            if (m.Success)
                            {
                                result.Title = m.Groups[1].Value;
                                hasTitleFlag = false;
                            }

                            if (hasTitleFlag && contents.Contains("</head>", StringComparison.OrdinalIgnoreCase))
                            {
                                hasTitleFlag = false;
                            }
                        }

                        if (hasSearchFlag)
                        {
                            // ReSharper disable once AssignNullToNotNullAttribute
                            if (contents.Contains(searchFor, comparison))
                            {
                                result.Status = UrlSearchStatus.Found;
                                hasSearchFlag = false;
                            }
                        }

                        token.ThrowIfCancellationRequested();
                        return(hasTitleFlag || hasSearchFlag || hasBufferFlag);
                    };
                }
                else
                {
                    onRead = (_, _) =>
                    {
                        token.ThrowIfCancellationRequested();
                        if (result.Status == UrlSearchStatus.Unknown)
                        {
                            result.Status = UrlSearchStatus.Success;
                        }
                        return(false);
                    };
                }

                IOReadSettings readSettings = new IOReadSettings(settings, onRead);
                if (!ReadAsync(thisValue, readSettings, token).Execute())
                {
                    result.Status = UrlSearchStatus.Failed;
                }
            }
            catch (WebException wex)
            {
                result.Status    = UrlSearchStatus.Unauthorized;
                result.Exception = wex;
            }
            catch (Exception ex)
            {
                result.Status    = UrlSearchStatus.Error;
                result.Exception = ex;
            }

            return(Task.FromResult(result));
        }