HTTPCacheContentWriter.cs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. using System;
  2. using System.IO;
  3. using Best.HTTP.Shared;
  4. using Best.HTTP.Shared.Logger;
  5. using Best.HTTP.Shared.PlatformSupport.Memory;
  6. using UnityEngine;
  7. namespace Best.HTTP.Caching
  8. {
  9. /// <summary>
  10. /// Represents a writer for caching HTTP response content.
  11. /// </summary>
  12. public class HTTPCacheContentWriter
  13. {
  14. /// <summary>
  15. /// Gets the parent HTTPCache instance associated with this content writer.
  16. /// </summary>
  17. public HTTPCache Cache { get; private set; }
  18. /// <summary>
  19. /// Hash identifying the resource. If <see cref="Write(BufferSegment)"/> fails, it becomes an invalid one.
  20. /// </summary>
  21. public Hash128 Hash { get; private set; }
  22. /// <summary>
  23. /// Expected length of the content. Has a non-zero value only when the server is sending a "content-length" header.
  24. /// </summary>
  25. public ulong ExpectedLength { get; private set; }
  26. /// <summary>
  27. /// Number of bytes written to the cache.
  28. /// </summary>
  29. public ulong ProcessedLength { get; private set; }
  30. /// <summary>
  31. /// Context of this cache writer used for logging.
  32. /// </summary>
  33. public LoggingContext Context { get; private set; }
  34. /// <summary>
  35. /// Underlying stream the download bytes are written into.
  36. /// </summary>
  37. private Stream _contentStream;
  38. internal HTTPCacheContentWriter(HTTPCache cache, Hash128 hash, Stream contentStream, ulong expectedLength, LoggingContext loggingContext)
  39. {
  40. this.Cache = cache;
  41. this.Hash = hash;
  42. this._contentStream = contentStream;
  43. this.ExpectedLength = expectedLength;
  44. this.Context = loggingContext;
  45. }
  46. /// <summary>
  47. /// Writes content to the underlying stream.
  48. /// </summary>
  49. /// <param name="segment"><see cref="BufferSegment"/> holding a reference to the data and containing information about the offset and count of the valid range of data.</param>
  50. public void Write(BufferSegment segment)
  51. {
  52. if (!this.Hash.isValid)
  53. return;
  54. try
  55. {
  56. this._contentStream?.Write(segment.Data, segment.Offset, segment.Count);
  57. this.ProcessedLength += (ulong)segment.Count;
  58. if (this.ProcessedLength > this.ExpectedLength)
  59. {
  60. if (!this.Cache.IsThereEnoughSpaceAfterMaintain(this.ProcessedLength, this.Context))
  61. {
  62. HTTPManager.Logger.Information(nameof(HTTPCacheContentWriter), $"Not enough space({this.ProcessedLength:N0}) in cache({this.Cache.CacheSize:N0}), even after Maintain!", this.Context);
  63. this.Cache?.EndCache(this, false, this.Context);
  64. }
  65. }
  66. }
  67. catch (Exception ex)
  68. {
  69. HTTPManager.Logger.Warning(nameof(HTTPCacheContentWriter), $"{nameof(Write)}({segment}): {ex}", this.Context);
  70. // EndCache will call Close, we don't have to in this catch block
  71. this.Cache?.EndCache(this, false, this.Context);
  72. }
  73. }
  74. /// <summary>
  75. /// Close the underlying stream and invalidate the hash.
  76. /// </summary>
  77. internal void Close()
  78. {
  79. this._contentStream?.Close();
  80. this._contentStream = null;
  81. // this will set an invalid cache, further HTTPCaching.EndCache calls will return early.
  82. this.Hash = new Hash128();
  83. }
  84. public override string ToString() => $"[{nameof(HTTPCacheContentWriter)} {Hash} {ProcessedLength:N0}]";
  85. }
  86. }