public static void AppendList <T>(this IHeaderDictionary Headers, string name, IList <T> values) { if (name == null) { throw new ArgumentNullException(nameof(name)); } if (values == null) { throw new ArgumentNullException(nameof(values)); } switch (values.Count) { case 0: Headers.Append(name, StringValues.Empty); break; case 1: Headers.Append(name, new StringValues(values[0].ToString())); break; default: var newValues = new string[values.Count]; for (var i = 0; i < values.Count; i++) { newValues[i] = values[i].ToString(); } Headers.Append(name, new StringValues(newValues)); break; } }
public void GlobalSetup() { _knownSingleValueResponseHeaders = new HttpResponseHeaders(); _knownSingleValueResponseHeaders.Server = "Value"; _knownSingleValueResponseHeaders.Date = "Value"; _knownSingleValueResponseHeaders.ContentType = "Value"; _knownSingleValueResponseHeaders.SetCookie = "Value"; _knownMultipleValueResponseHeaders = new HttpResponseHeaders(); _knownMultipleValueResponseHeaders.Server = new StringValues(new[] { "One", "Two" }); _knownMultipleValueResponseHeaders.Date = new StringValues(new[] { "One", "Two" }); _knownMultipleValueResponseHeaders.ContentType = new StringValues(new[] { "One", "Two" }); _knownMultipleValueResponseHeaders.SetCookie = new StringValues(new[] { "One", "Two" }); _unknownSingleValueResponseHeaders = new HttpResponseHeaders(); _unknownSingleValueResponseHeaders.Append("One", "Value"); _unknownSingleValueResponseHeaders.Append("Two", "Value"); _unknownSingleValueResponseHeaders.Append("Three", "Value"); _unknownSingleValueResponseHeaders.Append("Four", "Value"); _unknownMultipleValueResponseHeaders = new HttpResponseHeaders(); _unknownMultipleValueResponseHeaders.Append("One", new StringValues(new[] { "One", "Two" })); _unknownMultipleValueResponseHeaders.Append("Two", new StringValues(new[] { "One", "Two" })); _unknownMultipleValueResponseHeaders.Append("Three", new StringValues(new[] { "One", "Two" })); _unknownMultipleValueResponseHeaders.Append("Four", new StringValues(new[] { "One", "Two" })); _enumerator = new Http2HeadersEnumerator(); }
public void GlobalSetup() { _http2HeadersEnumerator = new Http2HeadersEnumerator(); _hpackEncoder = new DynamicHPackEncoder(); _buffer = new byte[1024 * 1024]; _knownResponseHeaders = new HttpResponseHeaders(); _knownResponseHeaders.Server = "Kestrel"; _knownResponseHeaders.ContentType = "application/json"; _knownResponseHeaders.Date = "Date!"; _knownResponseHeaders.ContentLength = 0; _knownResponseHeaders.AcceptRanges = "Ranges!"; _knownResponseHeaders.TransferEncoding = "Encoding!"; _knownResponseHeaders.Via = "Via!"; _knownResponseHeaders.Vary = "Vary!"; _knownResponseHeaders.WWWAuthenticate = "Authenticate!"; _knownResponseHeaders.LastModified = "Modified!"; _knownResponseHeaders.Expires = "Expires!"; _knownResponseHeaders.Age = "Age!"; _unknownResponseHeaders = new HttpResponseHeaders(); for (var i = 0; i < 10; i++) { _unknownResponseHeaders.Append("Unknown" + i, "Value" + i); } }
public void Append_MergesAndAppends() { IHeaderDictionary headers = CreateHeaders(CustomHeaderRawValues); headers.Append(CustomHeaderKey, "vA, vB"); IList <string> values = headers.GetValues(CustomHeaderKey); Assert.Equal(new[] { CustomHeaderJoinedValues + ",vA, vB" }, values); }
internal void WriteHeaders( IHeaderDictionary headers, bool includeDescription, IReadOnlyList <IMetric> metrics) { if (metrics.Count == 0) { return; } if (_timingAllowOriginValue != null) { headers.Append(TimingAllowOriginHeaderName, _timingAllowOriginValue); } var serverTimingValue = BuildServerTimingHeader(metrics, includeDescription); headers.Append(ServerTimingHeaderName, serverTimingValue); }
private static void CopyResponseHeaders(HttpHeaders source, IHeaderDictionary destination) { foreach (var header in source) { var headerName = header.Key; if (RequestUtilities.ResponseHeadersToSkip.Contains(headerName)) { continue; } destination.Append(headerName, header.Value.ToArray()); } }
private void ModifyHttpHeadersForCompressionOnce() { if (_httpHeadersModifiedForCompressionFlag.Set()) { IHeaderDictionary responseHeaders = _context.Response.Headers; _currentCompressor.AppendHttpHeaders((key, value) => { responseHeaders.Append(key, new StringValues(value)); }); responseHeaders.Remove(HeaderNames.ContentMD5); responseHeaders.Remove(HeaderNames.ContentLength); } }
private static void CopyResponseHeaders(HttpContext httpContext, HttpHeaders source, IHeaderDictionary destination) { var isHttp2OrGreater = ProtocolHelper.IsHttp2OrGreater(httpContext.Request.Protocol); foreach (var header in source) { var headerName = header.Key; if (RequestUtilities.ShouldSkipResponseHeader(headerName, isHttp2OrGreater)) { continue; } destination.Append(headerName, header.Value.ToArray()); } }
private static void RunRemainingResponseTransforms(HttpResponseMessage response, HttpContext context, IHeaderDictionary destination, IReadOnlyDictionary <string, ResponseHeaderTransform> transforms, HashSet <string> transformsRun) { transformsRun ??= EmptyHash; // Run any transforms that weren't run yet. foreach (var(headerName, transform) in transforms) // TODO: What about multiple transforms per header? Last wins? { if (!transformsRun.Contains(headerName)) { var headerValue = StringValues.Empty; headerValue = transform.Apply(context, response, headerValue); if (!StringValues.IsNullOrEmpty(headerValue)) { destination.Append(headerName, headerValue); } } } }
private static void CopyResponseHeaders(HttpResponseMessage response, HttpHeaders source, HttpContext context, IHeaderDictionary destination, IReadOnlyDictionary <string, ResponseHeaderTransform> transforms, ref HashSet <string> transformsRun) { foreach (var header in source) { var headerName = header.Key; if (RequestUtilities.ResponseHeadersToSkip.Contains(headerName)) { continue; } var headerValue = new StringValues(header.Value.ToArray()); if (transforms.TryGetValue(headerName, out var transform)) { (transformsRun ??= new HashSet <string>(StringComparer.OrdinalIgnoreCase)).Add(headerName); headerValue = transform.Apply(context, response, headerValue); } if (!StringValues.IsNullOrEmpty(headerValue)) { destination.Append(headerName, headerValue); } } }
public Task Invoke(HttpContext httpContext, Config config) { IHeaderDictionary headers = httpContext.Response.Headers; // Access Control headers.Append("Access-Control-Allow-Credentials", "true"); headers.Append("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS"); headers.Append("Access-Control-Allow-Headers", "Authorization, Accept, Origin, Content-Type, X-Requested-With, Connection, Transfer-Encoding"); // HSTS headers.Append("strict-transport-security", "max-age=31536000; includeSubdomains; preload"); // XSS Protection headers.Append("X-XSS-Protection", "1; mode=block"); // Content Type Options headers.Append("X-Content-Type-Options", "nosniff"); // Referrer Policy headers.Append("Referrer-Policy", "no-referrer, strict-origin-when-cross-origin"); return(_next(httpContext)); }
public async Task Invoke(HttpContext context) { bool useMinification = _options.IsMinificationEnabled() && _minificationManagers.Count > 0; bool useCompression = _options.IsCompressionEnabled() && _compressionManager != null; if (!useMinification && !useCompression) { await _next.Invoke(context); return; } HttpRequest request = context.Request; HttpResponse response = context.Response; using (var cachedStream = new MemoryStream()) { Stream originalStream = response.Body; response.Body = cachedStream; try { await _next.Invoke(context); } catch (Exception) { response.Body = originalStream; cachedStream.SetLength(0); throw; } byte[] cachedBytes = cachedStream.ToArray(); int cachedByteCount = cachedBytes.Length; bool isProcessed = false; response.Body = originalStream; cachedStream.SetLength(0); if (request.Method == "GET" && response.StatusCode == 200 && _options.IsAllowableResponseSize(cachedByteCount)) { string contentType = response.ContentType; string mediaType = null; Encoding encoding = null; if (contentType != null) { MediaTypeHeaderValue mediaTypeHeader; if (MediaTypeHeaderValue.TryParse(contentType, out mediaTypeHeader)) { mediaType = mediaTypeHeader.MediaType.ToLowerInvariant(); encoding = mediaTypeHeader.Encoding; } } encoding = encoding ?? Encoding.GetEncoding(0); string currentUrl = request.Path.Value; QueryString queryString = request.QueryString; if (queryString.HasValue) { currentUrl += queryString.Value; } string content = encoding.GetString(cachedBytes); string processedContent = content; IHeaderDictionary responseHeaders = response.Headers; bool isEncodedContent = responseHeaders.IsEncodedContent(); Action <string, string> appendHttpHeader = (key, value) => { responseHeaders.Append(key, new StringValues(value)); }; if (useMinification) { foreach (IMarkupMinificationManager minificationManager in _minificationManagers) { if (mediaType != null && minificationManager.IsSupportedMediaType(mediaType) && minificationManager.IsProcessablePage(currentUrl)) { if (isEncodedContent) { throw new InvalidOperationException( string.Format( AspNetCommonStrings.MarkupMinificationIsNotApplicableToEncodedContent, responseHeaders["Content-Encoding"] ) ); } IMarkupMinifier minifier = minificationManager.CreateMinifier(); MarkupMinificationResult minificationResult = minifier.Minify(processedContent, currentUrl, encoding, false); if (minificationResult.Errors.Count == 0) { processedContent = minificationResult.MinifiedContent; if (_options.IsPoweredByHttpHeadersEnabled()) { minificationManager.AppendPoweredByHttpHeader(appendHttpHeader); } isProcessed = true; } } if (isProcessed) { break; } } } if (useCompression && !isEncodedContent && _compressionManager.IsSupportedMediaType(mediaType)) { byte[] processedBytes = encoding.GetBytes(processedContent); using (var inputStream = new MemoryStream(processedBytes)) using (var outputStream = new MemoryStream()) { string acceptEncoding = request.Headers["Accept-Encoding"]; ICompressor compressor = _compressionManager.CreateCompressor(acceptEncoding); using (Stream compressedStream = compressor.Compress(outputStream)) { await inputStream.CopyToAsync(compressedStream); } byte[] compressedBytes = outputStream.ToArray(); int compressedByteCount = compressedBytes.Length; if (outputStream.CanWrite) { outputStream.SetLength(0); } inputStream.SetLength(0); responseHeaders["Content-Length"] = compressedByteCount.ToString(); compressor.AppendHttpHeaders(appendHttpHeader); await originalStream.WriteAsync(compressedBytes, 0, compressedByteCount); } isProcessed = true; } else { if (isProcessed) { byte[] processedBytes = encoding.GetBytes(processedContent); int processedByteCount = processedBytes.Length; responseHeaders["Content-Length"] = processedByteCount.ToString(); await originalStream.WriteAsync(processedBytes, 0, processedByteCount); } } } if (!isProcessed) { await originalStream.WriteAsync(cachedBytes, 0, cachedByteCount); } } }
public void Append(string key, string value) { OriginalHeaders.Append(key, value); }
private void SetNoCacheOnHeader(IHeaderDictionary headers, int expirationDays) { headers.Append("CacheControl", "no-cache, no-store, must-revalidate"); headers.Append("Expires", expirationDays.ToString()); headers.Append("Pragma", "no-cache"); }
private void SetCommonHeaders(IHeaderDictionary headers) { headers.Append("Cache-Control", "public, max-age=31536000"); }
public async Task Finish() { if (_minificationEnabled) { byte[] cachedBytes = _cachedStream.ToArray(); int cachedByteCount = cachedBytes.Length; bool isMinified = false; if (cachedByteCount > 0 && _options.IsAllowableResponseSize(cachedByteCount)) { Encoding encoding = _encoding ?? Encoding.GetEncoding(0); string content = encoding.GetString(cachedBytes); IMarkupMinifier minifier = _currentMinificationManager.CreateMinifier(); MarkupMinificationResult minificationResult = minifier.Minify(content, _currentUrl, _encoding, _currentMinificationManager.GenerateStatistics); if (minificationResult.Errors.Count == 0) { IHeaderDictionary responseHeaders = _context.Response.Headers; Action <string, string> appendHttpHeader = (key, value) => { responseHeaders.Append(key, new StringValues(value)); }; if (_options.IsPoweredByHttpHeadersEnabled()) { _currentMinificationManager.AppendPoweredByHttpHeader(appendHttpHeader); } responseHeaders.Remove(HeaderNames.ContentMD5); string processedContent = minificationResult.MinifiedContent; byte[] processedBytes = encoding.GetBytes(processedContent); int processedByteCount = processedBytes.Length; if (_compressionEnabled) { _currentCompressor.AppendHttpHeaders(appendHttpHeader); responseHeaders.Remove(HeaderNames.ContentLength); await _compressionStream.WriteAsync(processedBytes, 0, processedByteCount); } else { responseHeaders[HeaderNames.ContentLength] = processedByteCount.ToString(); await _originalStream.WriteAsync(processedBytes, 0, processedByteCount); } isMinified = true; } if (!isMinified) { Stream outputStream = _compressionEnabled ? _compressionStream : _originalStream; _cachedStream.Seek(0, SeekOrigin.Begin); await _cachedStream.CopyToAsync(outputStream); } } _cachedStream.Clear(); } }
private async Task Cache(HttpContext context) { //bool useMinification = _options.EnableMinification && _minificationManagers.Count > 0; //bool useCompression = _options.EnableCompression && _compressionManager != null; var useMinification = _options.EnableMinification; var useCompression = _options.EnableCompression; var cacheSetting = _options.Setting; var currentUrl = context.Request.Path.Value; var accept = $"{context.Request.Headers["Accept"]}"; var isHtml = context.Request.Method == "GET" && !string.IsNullOrEmpty(accept) && accept.Contains("text/html") && context.Request.Headers["X-Requested-With"] != "XMLHttpRequest"; #region 做缓存处理 if (!isHtml || (!useMinification && !useCompression) || !_options.EnableCache || !cacheSetting.IgnorePages.Contains(currentUrl)) { await _next.Invoke(context); return; } #endregion var cachePage = cacheSetting.CachePages.FirstOrDefault(a => a.Url == currentUrl); if (cachePage == null) { cachePage = new CachePage() { Url = currentUrl } } ; if (cachePage.CacheTime == 0) { cachePage.CacheTime = cacheSetting.CacheTime; } var cacheKey = GetKey(context.Request, cachePage.VaryByParams); #region 判断缓存是否存在 var cacheContent = _cacheService.Get <CacheContent>(cacheKey); if (cacheContent != null) { context.Response.Headers.Add("C-Cache", "Hit"); if (cacheContent.Headers != null && cacheContent.Headers.Count > 0) { foreach (var ch in cacheContent.Headers) { context.Response.Headers.Add(ch.Key, ch.Value); } } await context.Response.Body.WriteAsync(cacheContent.Content, 0, cacheContent.Content.Length); return; } #endregion HttpRequest request = context.Request; HttpResponse response = context.Response; using (var cachedStream = new MemoryStream()) { Stream originalStream = response.Body; response.Body = cachedStream; try { await _next.Invoke(context); } catch (Exception) { response.Body = originalStream; cachedStream.Clear(); throw; } byte[] cachedBytes = cachedStream.ToArray(); int cachedByteCount = cachedBytes.Length; bool isProcessed = false; response.Body = originalStream; cachedStream.Clear(); if (cachedByteCount == 0) { return; } if (response.StatusCode == 200) { string httpMethod = request.Method; string contentType = response.ContentType; string mediaType = null; Encoding encoding = null; if (contentType != null) { MediaTypeHeaderValue mediaTypeHeader; if (MediaTypeHeaderValue.TryParse(contentType, out mediaTypeHeader)) { mediaType = mediaTypeHeader.MediaType.Value; encoding = mediaTypeHeader.Encoding; } } encoding = encoding ?? Encoding.GetEncoding(0); string content = encoding.GetString(cachedBytes); string processedContent = content; IHeaderDictionary responseHeaders = response.Headers; bool isEncodedContent = responseHeaders.IsEncodedContent(); Action <string, string> appendHttpHeader = (key, value) => { responseHeaders.Append(key, new StringValues(value)); }; #region Html压缩 if (useMinification && _options.IsAllowableResponseSize(cachedByteCount)) { var htmlMinifier = new HtmlMinifier(); var result = htmlMinifier.Minify(processedContent); if (result.Errors.Count == 0) { processedContent = result.MinifiedContent; isProcessed = true; } //foreach (IMarkupMinificationManager minificationManager in _minificationManagers) //{ // if (minificationManager.IsSupportedHttpMethod(httpMethod) // && mediaType != null && minificationManager.IsSupportedMediaType(mediaType) // && minificationManager.IsProcessablePage(currentUrl)) // { // if (isEncodedContent) // { // throw new InvalidOperationException( // string.Format( // AspNetCommonStrings.MarkupMinificationIsNotApplicableToEncodedContent, // responseHeaders["Content-Encoding"] // ) // ); // } // IMarkupMinifier minifier = minificationManager.CreateMinifier(); // MarkupMinificationResult minificationResult = minifier.Minify(processedContent, // currentUrl, encoding, minificationManager.GenerateStatistics); // if (minificationResult.Errors.Count == 0) // { // processedContent = minificationResult.MinifiedContent; // isProcessed = true; // } // } // if (isProcessed) // { // break; // } //} } #endregion byte[] processedBytes; if (isProcessed) { processedBytes = encoding.GetBytes(processedContent); } else { processedBytes = cachedBytes; } #region GZip压缩 if (useCompression && !isEncodedContent) { string acceptEncoding = request.Headers["Accept-Encoding"]; string[] encodingTokens = acceptEncoding .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(e => e.Trim().ToLowerInvariant()) .ToArray(); ICompressor compressor = null; foreach (var et in encodingTokens) { if (et == EncodingTokenConstants.Deflate) { compressor = new DeflateCompressor(new DeflateCompressionSettings() { Level = System.IO.Compression.CompressionLevel.Fastest }); break; } else if (et == EncodingTokenConstants.GZip) { compressor = new GZipCompressor(new GZipCompressionSettings() { Level = System.IO.Compression.CompressionLevel.Fastest }); } } if (compressor != null) { using (var inputStream = new MemoryStream(processedBytes)) using (var outputStream = new MemoryStream()) { using (Stream compressedStream = compressor.Compress(outputStream)) { await inputStream.CopyToAsync(compressedStream); } byte[] compressedBytes = outputStream.ToArray(); processedBytes = compressedBytes; outputStream.Clear(); inputStream.Clear(); responseHeaders["Content-Length"] = compressedBytes.Length.ToString(); compressor.AppendHttpHeaders(appendHttpHeader); await originalStream.WriteAsync(compressedBytes, 0, compressedBytes.Length); } } //using (var inputStream = new MemoryStream(processedBytes)) //using (var outputStream = new MemoryStream()) //{ // string acceptEncoding = request.Headers["Accept-Encoding"]; // ICompressor compressor = _compressionManager.CreateCompressor(acceptEncoding); // using (Stream compressedStream = compressor.Compress(outputStream)) // { // await inputStream.CopyToAsync(compressedStream); // } // byte[] compressedBytes = outputStream.ToArray(); // processedBytes = compressedBytes; // int compressedByteCount = compressedBytes.Length; // outputStream.Clear(); // inputStream.Clear(); // responseHeaders["Content-Length"] = compressedByteCount.ToString(); // compressor.AppendHttpHeaders(appendHttpHeader); // await originalStream.WriteAsync(compressedBytes, 0, compressedByteCount); //} isProcessed = true; } #endregion else { if (isProcessed) { int processedByteCount = processedBytes.Length; responseHeaders["Content-Length"] = processedByteCount.ToString(); await originalStream.WriteAsync(processedBytes, 0, processedByteCount); } } #region 保存到缓存中 cacheContent = new CacheContent() { Content = processedBytes, ContentType = contentType, Headers = new Dictionary <string, string>() }; foreach (var rh in responseHeaders) { cacheContent.Headers.Add(rh.Key, rh.Value); } await _cacheService.SetAsync(cacheKey, cacheContent, TimeSpan.FromSeconds(cachePage.CacheTime)); context.Response.Headers.Add("C-Cache", "Cached"); //Task.Factory.StartNew(obj => //{ // var cc = (CacheContent)obj; //}, cacheContent); #endregion } if (!isProcessed) { await originalStream.WriteAsync(cachedBytes, 0, cachedByteCount); } } }
protected async Task InternalFinishAsync() { if (_minificationEnabled) { bool isMinified = false; int cachedByteCount = (int)_cachedStream.Length; IHeaderDictionary responseHeaders = _context.Response.Headers; Action <string, string> appendHttpHeader = (key, value) => { responseHeaders.Append(key, new StringValues(value)); }; if (cachedByteCount > 0 && _options.IsAllowableResponseSize(cachedByteCount)) { Encoding encoding = _encoding ?? _options.DefaultEncoding; #if NETSTANDARD1_3 byte[] cachedBytes = _cachedStream.ToArray(); string content = encoding.GetString(cachedBytes); #else byte[] cachedBytes = _cachedStream.GetBuffer(); string content = encoding.GetString(cachedBytes, 0, cachedByteCount); #endif IMarkupMinifier minifier = _currentMinificationManager.CreateMinifier(); MarkupMinificationResult minificationResult = minifier.Minify(content, _currentUrl, _encoding, _currentMinificationManager.GenerateStatistics); if (minificationResult.Errors.Count == 0) { if (_options.IsPoweredByHttpHeadersEnabled()) { _currentMinificationManager.AppendPoweredByHttpHeader(appendHttpHeader); } responseHeaders.Remove(HeaderNames.ContentMD5); string processedContent = minificationResult.MinifiedContent; var byteArrayPool = ArrayPool <byte> .Shared; int processedByteCount = encoding.GetByteCount(processedContent); byte[] processedBytes = byteArrayPool.Rent(processedByteCount); try { encoding.GetBytes(processedContent, 0, processedContent.Length, processedBytes, 0); if (_compressionEnabled) { _currentCompressor.AppendHttpHeaders(appendHttpHeader); responseHeaders.Remove(HeaderNames.ContentLength); await _compressionStream.WriteAsync(processedBytes, 0, processedByteCount); } else { responseHeaders[HeaderNames.ContentLength] = processedByteCount.ToString(); await _originalStream.WriteAsync(processedBytes, 0, processedByteCount); } } finally { byteArrayPool.Return(processedBytes); } isMinified = true; } } if (!isMinified) { Stream outputStream; if (_compressionEnabled) { outputStream = _compressionStream; _currentCompressor.AppendHttpHeaders(appendHttpHeader); } else { outputStream = _originalStream; } _cachedStream.Seek(0, SeekOrigin.Begin); await _cachedStream.CopyToAsync(outputStream); } _cachedStream.Clear(); } }