UploadSettings.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. using System;
  2. using System.IO;
  3. using Best.HTTP.Request.Upload;
  4. using Best.HTTP.Shared;
  5. using Best.HTTP.Shared.Extensions;
  6. namespace Best.HTTP.Request.Settings
  7. {
  8. public delegate void OnHeadersSentDelegate(HTTPRequest req);
  9. /// <summary>
  10. /// Options for sending the request headers and content, including upload progress monitoring.
  11. /// </summary>
  12. /// <remarks><see cref="SetupRequest"/> might be called when redirected or retried!</remarks>
  13. public class UploadSettings : IDisposable
  14. {
  15. /// <summary>
  16. /// Size of the internal buffer, and upload progress will be fired when this size of data sent to the wire. Its default value is 4 KiB.
  17. /// </summary>
  18. public int UploadChunkSize = 4 * 1024;
  19. /// <summary>
  20. /// The stream that the plugin will use to send data to the server.
  21. /// </summary>
  22. /// <remarks>
  23. /// The stream can be any regular <see cref="System.IO.Stream"/> implementation or a specialized one inheriting from <see cref="UploadStreamBase"/>:
  24. /// <list type="bullet">
  25. /// <item><term><see cref="DynamicUploadStream"/></term><description>A specialized <see cref="UploadStreamBase"/> for data generated on-the-fly or periodically. The request remains active until the <see cref="DynamicUploadStream.Complete"/> method is invoked, ensuring continuous data feed even during temporary empty states.</description></item>
  26. /// <item><term><see cref="JSonDataStream{T}"/></term><description>An <see cref="UploadStreamBase"/> implementation to convert and upload the object as JSON data. It sets the <c>"Content-Type"</c> header to <c>"application/json; charset=utf-8"</c>.</description></item>
  27. /// <item><term><see cref="Upload.Forms.UrlEncodedStream"/></term><description>An <see cref="UploadStreamBase"/> implementation representing a stream that prepares and sends data as URL-encoded form data in an HTTP request.</description></item>
  28. /// <item><term><see cref="Upload.Forms.MultipartFormDataStream"/></term><description>An <see cref="UploadStreamBase"/> based implementation of the <c>multipart/form-data</c> Content-Type. It's very memory-effective, streams are read into memory in chunks.</description></item>
  29. /// </list>
  30. /// </remarks>
  31. public Stream UploadStream;
  32. /// <summary>
  33. /// Set to <c>false</c> if the plugin MUST NOT dispose <see cref="UploadStream"/> after the request is finished.
  34. /// </summary>
  35. public bool DisposeStream = true;
  36. /// <summary>
  37. /// Called periodically when data sent to the server.
  38. /// </summary>
  39. public OnProgressDelegate OnUploadProgress;
  40. /// <summary>
  41. /// This event is fired after the headers are sent to the server.
  42. /// </summary>
  43. public event OnHeadersSentDelegate OnHeadersSent
  44. {
  45. add { _onHeadersSent += value; }
  46. remove { _onHeadersSent -= value; }
  47. }
  48. private OnHeadersSentDelegate _onHeadersSent;
  49. // <summary>
  50. // Whether to send an "<c>Expect: 100-continue</c>" header and value when there's content to send (<see cref="UploadSettings.UploadStream"/> != <c>null</c>).
  51. // By using "<c>Expect: 100-continue</c>" the server is able to respond with an error (like <c>401-unauthorized</c>, <c>405-method not allowed</c>, etc.) or redirect before the client sends the whole payload.
  52. // </summary>
  53. // <remarks>
  54. // More details can be found here:
  55. // <list type="bullet">
  56. // <item><description><see href="https://www.rfc-editor.org/rfc/rfc9110#name-expect">RFC-9110 - Expect header</see></description></item>
  57. // <item><description><see href="https://daniel.haxx.se/blog/2020/02/27/expect-tweaks-in-curl/">EXPECT: TWEAKS IN CURL (by Daniel Stenberg)</see></description></item>
  58. // </list>
  59. // </remarks>
  60. //public bool SendExpect100Continue = true;
  61. // False by default, set to true only when "Expect: 100-continue" sent out.
  62. //internal bool Expect100Continue = false;
  63. //internal void ResetExpects() => this.SendExpect100Continue = this.Expect100Continue = false;
  64. private bool isDisposed;
  65. /// <summary>
  66. /// Called every time the request is sent out (redirected or retried).
  67. /// </summary>
  68. /// <param name="request">The <see cref="HTTPRequest"/> being prepared.</param>
  69. /// <param name="dispatchHeadersSentCallback"><c>true</c> if the <see cref="OnHeadersSent"/> can be fired.</param>
  70. public virtual void SetupRequest(HTTPRequest request, bool dispatchHeadersSentCallback)
  71. {
  72. if (isDisposed)
  73. throw new ObjectDisposedException(nameof(UploadSettings));
  74. if (this.UploadStream is UploadStreamBase upStream)
  75. upStream.BeforeSendHeaders(request);
  76. // Decide on whether append an "expect: 100-continue" or not.
  77. // https://www.rfc-editor.org/rfc/rfc9110#name-expect
  78. /*
  79. if (this.SendExpect100Continue && this.UploadStream != null)
  80. {
  81. request.AddHeader("expect", "100-continue");
  82. this.Expect100Continue = true;
  83. }
  84. else
  85. request.RemoveHeader("expect");
  86. */
  87. if (dispatchHeadersSentCallback)
  88. {
  89. // Call the callback on the unity main thread
  90. if (HTTPUpdateDelegator.Instance.IsMainThread())
  91. call_onBeforeHeaderSend(request);
  92. else
  93. new RunOnceOnMainThread(() => call_onBeforeHeaderSend(request), request.Context)
  94. .Subscribe();
  95. }
  96. }
  97. protected void call_onBeforeHeaderSend(HTTPRequest request)
  98. {
  99. try
  100. {
  101. _onHeadersSent?.Invoke(request);
  102. }
  103. catch (Exception ex)
  104. {
  105. HTTPManager.Logger.Exception(nameof(UploadSettings), nameof(OnHeadersSent), ex, request.Context);
  106. }
  107. }
  108. protected virtual void Dispose(bool disposing)
  109. {
  110. if (!isDisposed)
  111. {
  112. if (disposing)
  113. {
  114. if (this.DisposeStream)
  115. {
  116. var stream = this.UploadStream;
  117. if (stream != null)
  118. {
  119. this.UploadStream?.Dispose();
  120. this.UploadStream = null;
  121. isDisposed = true;
  122. }
  123. }
  124. }
  125. }
  126. }
  127. /// <summary>
  128. /// Dispose of resources used by the UploadSettings instance.
  129. /// </summary>
  130. public void Dispose()
  131. {
  132. Dispose(disposing: true);
  133. GC.SuppressFinalize(this);
  134. }
  135. }
  136. }