/// <summary>
        /// Read response from stream
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="response"></param>
        /// <param name="rewrite"></param>
        /// <returns>redirected target</returns>
        protected async Task <string> ReadAsync(Stream stream, HttpResponse response,
                                                string host, Func <string, string> rewrite)
        {
            // Now read response back and write out to response
            var reader = new HttpResponseReader(stream);

            var    isChunked       = false;
            string contentEncoding = null;
            var    headers         = new HeaderDictionary();

            // Parse headers and status code
            Console.WriteLine();
            var s = await reader.ReadLineAsync().ConfigureAwait(false); // header

            if (string.IsNullOrEmpty(s))
            {
                return(null);  // throw
            }
            Console.WriteLine(s);

            var    statusCode = int.Parse(s.Split(' ')[1]);
            string target     = null;

            while (true)
            {
                s = await reader.ReadLineAsync().ConfigureAwait(false);

                if (string.IsNullOrEmpty(s))
                {
                    break;
                }
                Console.WriteLine(s);
                var index = s.IndexOf(':');
                if (index < 0)
                {
                    continue;
                }
                var key = s.Substring(0, index).Trim();
                var val = s.Substring(index + 1).Trim();

                /**/ if (key.Equals("Transfer-Encoding",
                                    StringComparison.CurrentCultureIgnoreCase))
                {
                    isChunked = val.Equals("chunked", StringComparison.CurrentCultureIgnoreCase);
                }
                else if (key.Equals("Content-Encoding",
                                    StringComparison.CurrentCultureIgnoreCase))
                {
                    contentEncoding = val.ToLowerInvariant();
                }
                else if (key.Equals("Set-Cookie"))
                {
                    // TODO:
                    // continue;
                }
                else
                {
                    if (key.Equals("Location",
                                   StringComparison.CurrentCultureIgnoreCase))
                    {
                        target = val;
                    }
                    val = _urls.Replace(val, (f) => rewrite(f.Value));
                }
                if (headers.ContainsKey(key))
                {
                    headers.Remove(key);
                }
                headers.Add(key, val);
            }

            if (statusCode >= StatusCodes.Status300MultipleChoices ||
                statusCode < StatusCodes.Status400BadRequest)
            {
                if (!string.IsNullOrEmpty(target))
                {
                    // Redirect to new location
                    return(target);
                }
            }

            // Copy headers to response
            response.StatusCode = statusCode;
            response.Headers.Clear();
            foreach (var entry in headers)
            {
                response.Headers.Add(entry.Key, entry.Value);
            }

            if (response.StatusCode == StatusCodes.Status204NoContent ||
                response.StatusCode == StatusCodes.Status205ResetContent ||
                response.StatusCode == StatusCodes.Status304NotModified)
            {
                response.ContentLength = 0;
                await response.Body.FlushAsync();
            }
            else if (IsTextContent(response.ContentType, out var encoder))
            {
                // Remember host as fallback
                response.Cookies.Append("host", host);

                await reader.ReadBodyAsync(response, isChunked, (body, len) => {
                    if (!string.IsNullOrWhiteSpace(contentEncoding))
                    {
                        var mem = new MemoryStream(body, 0, len);
                        /**/
                        if (contentEncoding.Equals("gzip"))
                        {
                            using (var gzip = new GZipStream(mem,
                                                             CompressionMode.Decompress, false)) {
                                s = gzip.ToString(encoder);
                            }
                        }
                        else if (contentEncoding.Equals("deflate"))
                        {
                            using (var deflate = new DeflateStream(mem,
                                                                   CompressionMode.Decompress, false)) {
                                s = deflate.ToString(encoder);
                            }
                        }
                        else
                        {
                            throw new Exception("Unexpected content encoding");
                        }
                    }