@@ -18,11 +18,8 @@ package org.http4s
18
18
19
19
import cats .effect .kernel .Async
20
20
import cats .effect .kernel .Resource
21
- import cats .effect .std .Dispatcher
22
- import cats .effect .std .Queue
23
21
import cats .effect .syntax .all ._
24
22
import cats .syntax .all ._
25
- import fs2 .Chunk
26
23
import fs2 .Stream
27
24
import org .http4s .headers .`Transfer-Encoding`
28
25
import org .scalajs .dom .Blob
@@ -122,29 +119,73 @@ package object dom {
122
119
}
123
120
124
121
private [dom] def toReadableStream [F [_]](in : Stream [F , Byte ])(
125
- implicit F : Async [F ]): Resource [F , ReadableStream [Uint8Array ]] =
126
- Dispatcher .sequential.flatMap { dispatcher =>
127
- Resource .eval(Queue .synchronous[F , Option [Chunk [Byte ]]]).flatMap { chunks =>
128
- in.enqueueNoneTerminatedChunks(chunks).compile.drain.background.evalMap { _ =>
129
- F .delay {
130
- val source = new ReadableStreamUnderlyingSource [Uint8Array ] {
131
- `type` = ReadableStreamType .bytes
132
- pull = js.defined { controller =>
133
- dispatcher.unsafeToPromise {
134
- chunks.take.flatMap {
135
- case Some (chunk) =>
136
- F .delay(controller.enqueue(chunk.toUint8Array))
137
- case None => F .delay(controller.close())
138
- }
122
+ implicit F : Async [F ]): Resource [F , ReadableStream [Uint8Array ]] = {
123
+
124
+ final class Synchronizer [A ] {
125
+
126
+ type TakeCallback = Either [Throwable , A ] => Unit
127
+ type OfferCallback = Either [Throwable , TakeCallback ] => Unit
128
+
129
+ private [this ] var callback : AnyRef = null
130
+ @ inline private [this ] def offerCallback = callback.asInstanceOf [OfferCallback ]
131
+ @ inline private [this ] def takeCallback = callback.asInstanceOf [TakeCallback ]
132
+
133
+ def offer (cb : OfferCallback ): Unit =
134
+ if (callback ne null ) {
135
+ cb(Right (takeCallback))
136
+ callback = null
137
+ } else {
138
+ callback = cb
139
+ }
140
+
141
+ def take (cb : TakeCallback ): Unit =
142
+ if (callback ne null ) {
143
+ offerCallback(Right (cb))
144
+ callback = null
145
+ } else {
146
+ callback = cb
147
+ }
148
+ }
149
+
150
+ Resource .eval(F .delay(new Synchronizer [Option [Uint8Array ]])).flatMap { synchronizer =>
151
+ val offers = in
152
+ .chunks
153
+ .noneTerminate
154
+ .foreach { chunk =>
155
+ F .async[Either [Throwable , Option [Uint8Array ]] => Unit ] { cb =>
156
+ F .delay(synchronizer.offer(cb)).as(Some (F .unit))
157
+ }.flatMap(cb => F .delay(cb(Right (chunk.map(_.toUint8Array)))))
158
+ }
159
+ .compile
160
+ .drain
161
+
162
+ offers.background.evalMap { _ =>
163
+ F .delay {
164
+ val source = new ReadableStreamUnderlyingSource [Uint8Array ] {
165
+ `type` = ReadableStreamType .bytes
166
+ pull = js.defined { controller =>
167
+ new js.Promise [Unit ]({ (resolve, reject) =>
168
+ synchronizer.take {
169
+ case Right (Some (bytes)) =>
170
+ controller.enqueue(bytes)
171
+ resolve(())
172
+ ()
173
+ case Right (None ) =>
174
+ controller.close()
175
+ resolve(())
176
+ ()
177
+ case Left (ex) =>
178
+ reject(ex)
179
+ ()
139
180
}
140
- }
181
+ })
141
182
}
142
- ReadableStream [Uint8Array ](source)
143
183
}
184
+ ReadableStream [Uint8Array ](source)
144
185
}
145
186
}
146
187
}
147
-
188
+ }
148
189
private [dom] lazy val supportsRequestStreams = {
149
190
val request = new DomRequest (
150
191
" data:a/a;charset=utf-8," ,
0 commit comments