using System;
using System.IO;
using Best.HTTP.Request.Upload;
using Best.HTTP.Shared;
using Best.HTTP.Shared.Extensions;
namespace Best.HTTP.Request.Settings
{
public delegate void OnHeadersSentDelegate(HTTPRequest req);
///
/// Options for sending the request headers and content, including upload progress monitoring.
///
/// might be called when redirected or retried!
public class UploadSettings : IDisposable
{
///
/// 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.
///
public int UploadChunkSize = 4 * 1024;
///
/// The stream that the plugin will use to send data to the server.
///
///
/// The stream can be any regular implementation or a specialized one inheriting from :
///
/// - A specialized for data generated on-the-fly or periodically. The request remains active until the method is invoked, ensuring continuous data feed even during temporary empty states.
/// - An implementation to convert and upload the object as JSON data. It sets the "Content-Type" header to "application/json; charset=utf-8".
/// - An implementation representing a stream that prepares and sends data as URL-encoded form data in an HTTP request.
/// - An based implementation of the multipart/form-data Content-Type. It's very memory-effective, streams are read into memory in chunks.
///
///
public Stream UploadStream;
///
/// Set to false if the plugin MUST NOT dispose after the request is finished.
///
public bool DisposeStream = true;
///
/// Called periodically when data sent to the server.
///
public OnProgressDelegate OnUploadProgress;
///
/// This event is fired after the headers are sent to the server.
///
public event OnHeadersSentDelegate OnHeadersSent
{
add { _onHeadersSent += value; }
remove { _onHeadersSent -= value; }
}
private OnHeadersSentDelegate _onHeadersSent;
//
// Whether to send an "Expect: 100-continue" header and value when there's content to send ( != null).
// By using "Expect: 100-continue" the server is able to respond with an error (like 401-unauthorized, 405-method not allowed, etc.) or redirect before the client sends the whole payload.
//
//
// More details can be found here:
//
// - RFC-9110 - Expect header
// - EXPECT: TWEAKS IN CURL (by Daniel Stenberg)
//
//
//public bool SendExpect100Continue = true;
// False by default, set to true only when "Expect: 100-continue" sent out.
//internal bool Expect100Continue = false;
//internal void ResetExpects() => this.SendExpect100Continue = this.Expect100Continue = false;
private bool isDisposed;
///
/// Called every time the request is sent out (redirected or retried).
///
/// The being prepared.
/// true if the can be fired.
public virtual void SetupRequest(HTTPRequest request, bool dispatchHeadersSentCallback)
{
if (isDisposed)
throw new ObjectDisposedException(nameof(UploadSettings));
if (this.UploadStream is UploadStreamBase upStream)
upStream.BeforeSendHeaders(request);
// Decide on whether append an "expect: 100-continue" or not.
// https://www.rfc-editor.org/rfc/rfc9110#name-expect
/*
if (this.SendExpect100Continue && this.UploadStream != null)
{
request.AddHeader("expect", "100-continue");
this.Expect100Continue = true;
}
else
request.RemoveHeader("expect");
*/
if (dispatchHeadersSentCallback)
{
// Call the callback on the unity main thread
if (HTTPUpdateDelegator.Instance.IsMainThread())
call_onBeforeHeaderSend(request);
else
new RunOnceOnMainThread(() => call_onBeforeHeaderSend(request), request.Context)
.Subscribe();
}
}
protected void call_onBeforeHeaderSend(HTTPRequest request)
{
try
{
_onHeadersSent?.Invoke(request);
}
catch (Exception ex)
{
HTTPManager.Logger.Exception(nameof(UploadSettings), nameof(OnHeadersSent), ex, request.Context);
}
}
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
if (disposing)
{
if (this.DisposeStream)
{
var stream = this.UploadStream;
if (stream != null)
{
this.UploadStream?.Dispose();
this.UploadStream = null;
isDisposed = true;
}
}
}
}
}
///
/// Dispose of resources used by the UploadSettings instance.
///
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}