// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';

import 'abortable.dart';
import 'base_client.dart';
import 'base_request.dart';
import 'byte_stream.dart';

/// An HTTP request where the request body is sent asynchronously after the
/// connection has been established and the headers have been sent.
///
/// When the request is sent via [BaseClient.send], only the headers and
/// whatever data has already been written to [StreamedRequest.sink] will be
/// sent immediately. More data will be sent as soon as it's written to
/// [StreamedRequest.sink], and when the sink is closed the request will end.
///
/// For example:
/// ```dart
/// final request = http.StreamedRequest('POST', Uri.http('example.com', ''))
///     ..contentLength = 10
///     ..sink.add([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
///
/// // The sink must be closed to end the request.
/// // The Future returned from `close()` may not complete until after the
/// // request is sent, and it should not be awaited.
/// unawaited(request.sink.close());
/// final response = await request.send();
/// ```
class StreamedRequest extends BaseRequest {
  /// The sink to which to write data that will be sent as the request body.
  ///
  /// This may be safely written to before the request is sent; the data will be
  /// buffered.
  ///
  /// Closing this signals the end of the request.
  StreamSink<List<int>> get sink => _controller.sink;

  /// The controller for [sink], from which [BaseRequest] will read data for
  /// [finalize].
  final StreamController<List<int>> _controller;

  /// Creates a new streaming request.
  StreamedRequest(super.method, super.url)
      : _controller = StreamController<List<int>>(sync: true);

  /// Freezes all mutable fields and returns a single-subscription [ByteStream]
  /// that emits the data being written to [sink].
  @override
  ByteStream finalize() {
    super.finalize();
    return ByteStream(_controller.stream);
  }
}

/// A [StreamedRequest] which supports abortion using [abortTrigger].
///
/// A future breaking version of 'package:http' will merge this into
/// [StreamedRequest], making it a requirement.
final class AbortableStreamedRequest extends StreamedRequest with Abortable {
  AbortableStreamedRequest(super.method, super.url, {this.abortTrigger})
      : super();

  @override
  final Future<void>? abortTrigger;
}
