tink_http icon indicating copy to clipboard operation
tink_http copied to clipboard

Upload progress

Open kevinresol opened this issue 4 years ago • 4 comments

kevinresol avatar Dec 03 '20 02:12 kevinresol

Hmm. Off the top of my head, I'd say this can be implemented once in Client rather than in each ClientObject. Not sure that's better, but it's certainly less work ^^

back2dos avatar Dec 04 '20 07:12 back2dos

How does that work?

kevinresol avatar Dec 04 '20 07:12 kevinresol

Well. For starters we need something to monitor the progress of a source. This looks like it might do the trick:

class WithProgress<E> extends StreamBase<Chunk, E> {
  var data:SourceObject<E>;
  var progressed:Int->Void;
  var written:Int;

  override function get_depleted():Bool
    return data.depleted;

  public function new(data, progressed, written = 0) {
    this.data = data;
    this.progressed = progressed;
    this.written = written;
  }

  override function next():Future<Step<Chunk, E>> {
    return data.next().map(function (step) return switch step {
      case Link(value, next):
        var written = this.written + value.length;
        progressed(written);
        Link(value, new WithProgress(next, progressed, written));
      default: step;
    });
  }

  override function forEach<Safety>(handler:Handler<Chunk, Safety>):Future<Conclusion<Chunk, Safety, E>> {
    var written = this.written;
    return data.forEach(function (chunk) {
      return handler.apply(chunk).map(function (h) {
        if (h.match(Finish | Resume))
          progressed(written += chunk.length);
        return h;
      });
    }).map(function (conclusion):Conclusion<Chunk, Safety, E> return switch conclusion {
      case Halted(rest): Halted(new WithProgress(rest, progressed, written));
      case Clogged(error, at): Clogged(error, new WithProgress(at, progressed, written));
      case Failed(error): Failed(error);
      case Depleted: Depleted;
    });
  }
}

It's not 100% ideal. For example calling Stream::next will count as progress, no matter whether the bytes are actually processed or not. But in case the consumer then starts forEach on the original stream (rather than the tail of the Link), it starts counting again at 0, so the number should be correct. Or so I hope ^^

With that in place, in the presence of a progress handler, you can extract the total size from the corresponding message head (if set) and wrap the message body WithProgress so that it calls the handler appropriately.

back2dos avatar Dec 04 '20 08:12 back2dos

But of course this presumes that the underlying client can actually gradually read the data from the Source, which some platforms might not allow.

back2dos avatar Dec 04 '20 08:12 back2dos