@@ -7,12 +7,13 @@ use deno_core::futures::StreamExt;
7
7
use deno_core:: include_js_files;
8
8
use deno_core:: op;
9
9
use deno_core:: AsyncRefCell ;
10
- use deno_core:: CancelTryFuture ;
11
10
use deno_core:: Extension ;
12
11
use deno_core:: OpState ;
13
12
use deno_core:: Resource ;
14
13
use deno_core:: ResourceId ;
14
+ use deno_core:: WriteOutcome ;
15
15
use deno_core:: { AsyncResult , BufView , ByteString , CancelHandle , RcRef } ;
16
+ use deno_core:: { CancelFuture , CancelTryFuture } ;
16
17
use hyper:: body:: HttpBody ;
17
18
use hyper:: header:: { HeaderName , HeaderValue } ;
18
19
use hyper:: { Body , Request , Response } ;
@@ -23,6 +24,8 @@ use std::collections::HashMap;
23
24
use std:: path:: PathBuf ;
24
25
use std:: pin:: Pin ;
25
26
use std:: rc:: Rc ;
27
+ use std:: task:: Context ;
28
+ use std:: task:: Poll ;
26
29
use tokio:: sync:: { mpsc, oneshot} ;
27
30
28
31
pub fn init ( ) -> Extension {
@@ -33,7 +36,8 @@ pub fn init() -> Extension {
33
36
) )
34
37
. ops ( vec ! [
35
38
op_user_worker_create:: decl( ) ,
36
- op_user_worker_fetch:: decl( ) ,
39
+ op_user_worker_fetch_build:: decl( ) ,
40
+ op_user_worker_fetch_send:: decl( ) ,
37
41
] )
38
42
. build ( )
39
43
}
@@ -88,6 +92,13 @@ pub struct UserWorkerRequest {
88
92
has_body : bool ,
89
93
}
90
94
95
+ #[ derive( Serialize ) ]
96
+ #[ serde( rename_all = "camelCase" ) ]
97
+ pub struct UserWorkerBuiltRequest {
98
+ request_rid : ResourceId ,
99
+ request_body_rid : Option < ResourceId > ,
100
+ }
101
+
91
102
#[ derive( Serialize ) ]
92
103
#[ serde( rename_all = "camelCase" ) ]
93
104
pub struct UserWorkerResponse {
@@ -97,14 +108,63 @@ pub struct UserWorkerResponse {
97
108
body_rid : ResourceId ,
98
109
}
99
110
100
- type BytesStream = Pin < Box < dyn Stream < Item = Result < bytes :: Bytes , std :: io :: Error > > + Unpin > > ;
111
+ struct UserWorkerRequestResource ( Request < Body > ) ;
101
112
102
- struct UserWorkerRquestBodyResource {
103
- writer : AsyncRefCell < Peekable < BytesStream > > ,
113
+ impl Resource for UserWorkerRequestResource {
114
+ fn name ( & self ) -> Cow < str > {
115
+ "userWorkerRequest" . into ( )
116
+ }
117
+ }
118
+
119
+ struct UserWorkerRequestBodyResource {
120
+ body : AsyncRefCell < mpsc:: Sender < Option < bytes:: Bytes > > > ,
104
121
cancel : CancelHandle ,
105
- size : Option < u64 > ,
106
122
}
107
123
124
+ impl Resource for UserWorkerRequestBodyResource {
125
+ fn name ( & self ) -> Cow < str > {
126
+ "userWorkerRequestBody" . into ( )
127
+ }
128
+
129
+ fn write ( self : Rc < Self > , buf : BufView ) -> AsyncResult < WriteOutcome > {
130
+ Box :: pin ( async move {
131
+ let bytes: bytes:: Bytes = buf. into ( ) ;
132
+ let nwritten = bytes. len ( ) ;
133
+ let body = RcRef :: map ( & self , |r| & r. body ) . borrow_mut ( ) . await ;
134
+ let cancel = RcRef :: map ( self , |r| & r. cancel ) ;
135
+ body. send ( Some ( bytes) )
136
+ . or_cancel ( cancel)
137
+ . await ?
138
+ . map_err ( |_| type_error ( "request body receiver not connected (request closed)" ) ) ?;
139
+ Ok ( WriteOutcome :: Full { nwritten } )
140
+ } )
141
+ }
142
+
143
+ fn shutdown ( self : Rc < Self > ) -> AsyncResult < ( ) > {
144
+ Box :: pin ( async move {
145
+ let body = RcRef :: map ( & self , |r| & r. body ) . borrow_mut ( ) . await ;
146
+ let cancel = RcRef :: map ( self , |r| & r. cancel ) ;
147
+ // There is a case where hyper knows the size of the response body up
148
+ // front (through content-length header on the resp), where it will drop
149
+ // the body once that content length has been reached, regardless of if
150
+ // the stream is complete or not. This is expected behaviour, but it means
151
+ // that if you stream a body with an up front known size (eg a Blob),
152
+ // explicit shutdown can never succeed because the body (and by extension
153
+ // the receiver) will have dropped by the time we try to shutdown. As such
154
+ // we ignore if the receiver is closed, because we know that the request
155
+ // is complete in good health in that case.
156
+ body. send ( None ) . or_cancel ( cancel) . await ?. ok ( ) ;
157
+ Ok ( ( ) )
158
+ } )
159
+ }
160
+
161
+ fn close ( self : Rc < Self > ) {
162
+ self . cancel . cancel ( )
163
+ }
164
+ }
165
+
166
+ type BytesStream = Pin < Box < dyn Stream < Item = Result < bytes:: Bytes , std:: io:: Error > > + Unpin > > ;
167
+
108
168
struct UserWorkerResponseBodyResource {
109
169
reader : AsyncRefCell < Peekable < BytesStream > > ,
110
170
cancel : CancelHandle ,
@@ -159,18 +219,20 @@ impl Resource for UserWorkerResponseBodyResource {
159
219
}
160
220
161
221
#[ op]
162
- pub async fn op_user_worker_fetch (
163
- state : Rc < RefCell < OpState > > ,
164
- key : String ,
222
+ pub fn op_user_worker_fetch_build (
223
+ state : & mut OpState ,
165
224
req : UserWorkerRequest ,
166
- ) -> Result < UserWorkerResponse , AnyError > {
167
- let mut op_state = state. borrow_mut ( ) ;
168
- let tx = op_state. borrow :: < mpsc:: UnboundedSender < UserWorkerMsgs > > ( ) ;
169
- let ( result_tx, result_rx) = oneshot:: channel :: < Response < Body > > ( ) ;
170
-
225
+ ) -> Result < UserWorkerBuiltRequest , AnyError > {
171
226
let mut body = Body :: empty ( ) ;
227
+ let mut request_body_rid = None ;
172
228
if req. has_body {
173
- //body = Body::wrap_stream(stream);
229
+ let ( stream, tx) = MpscByteStream :: new ( ) ;
230
+ body = Body :: wrap_stream ( stream) ;
231
+
232
+ request_body_rid = Some ( state. resource_table . add ( UserWorkerRequestBodyResource {
233
+ body : AsyncRefCell :: new ( tx) ,
234
+ cancel : CancelHandle :: default ( ) ,
235
+ } ) ) ;
174
236
}
175
237
176
238
let mut request = Request :: builder ( )
@@ -183,18 +245,47 @@ pub async fn op_user_worker_fetch(
183
245
for ( key, value) in req. headers {
184
246
if !key. is_empty ( ) {
185
247
let header_name = HeaderName :: try_from ( key) . unwrap ( ) ;
186
- let header_value = HeaderValue :: try_from ( value) . unwrap_or ( HeaderValue :: from_static ( "" ) ) ;
248
+ let mut header_value =
249
+ HeaderValue :: try_from ( value) . unwrap_or ( HeaderValue :: from_static ( "" ) ) ;
187
250
188
- // skip content-length header, this would be re-calculated and added to the request
189
- if header_name. eq ( "content-length" ) {
190
- continue ;
251
+ // if request has no body explicitly set the content-length to 0
252
+ if !req . has_body && header_name. eq ( "content-length" ) {
253
+ header_value = HeaderValue :: from ( 0 ) ;
191
254
}
192
255
193
256
request. headers_mut ( ) . append ( header_name, header_value) ;
194
257
}
195
258
}
196
259
197
- tx. send ( UserWorkerMsgs :: SendRequest ( key, request, result_tx) ) ;
260
+ let request_rid = state. resource_table . add ( UserWorkerRequestResource ( request) ) ;
261
+
262
+ Ok ( UserWorkerBuiltRequest {
263
+ request_rid,
264
+ request_body_rid,
265
+ } )
266
+ }
267
+
268
+ #[ op]
269
+ pub async fn op_user_worker_fetch_send (
270
+ state : Rc < RefCell < OpState > > ,
271
+ key : String ,
272
+ rid : ResourceId ,
273
+ ) -> Result < UserWorkerResponse , AnyError > {
274
+ let mut op_state = state. borrow_mut ( ) ;
275
+ let tx = op_state
276
+ . borrow :: < mpsc:: UnboundedSender < UserWorkerMsgs > > ( )
277
+ . clone ( ) ;
278
+
279
+ let request = op_state
280
+ . resource_table
281
+ . take :: < UserWorkerRequestResource > ( rid) ?;
282
+ drop ( op_state) ;
283
+
284
+ let request = Rc :: try_unwrap ( request)
285
+ . ok ( )
286
+ . expect ( "multiple op_user_worker_fetch_send ongoing" ) ;
287
+ let ( result_tx, result_rx) = oneshot:: channel :: < Response < Body > > ( ) ;
288
+ tx. send ( UserWorkerMsgs :: SendRequest ( key, request. 0 , result_tx) ) ;
198
289
199
290
let result = result_rx. await ;
200
291
if result. is_err ( ) {
@@ -227,6 +318,8 @@ pub async fn op_user_worker_fetch(
227
318
. into_body ( )
228
319
. map ( |r| r. map_err ( |err| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , err) ) ) ,
229
320
) ;
321
+
322
+ let mut op_state = state. borrow_mut ( ) ;
230
323
let body_rid = op_state. resource_table . add ( UserWorkerResponseBodyResource {
231
324
reader : AsyncRefCell :: new ( stream. peekable ( ) ) ,
232
325
cancel : CancelHandle :: default ( ) ,
@@ -241,3 +334,44 @@ pub async fn op_user_worker_fetch(
241
334
} ;
242
335
Ok ( response)
243
336
}
337
+
338
+ // [copied from https://github.com/denoland/deno/blob/v1.31.3/ext/fetch/byte_stream.rs]
339
+ // [MpscByteStream] is a stream of bytes that is backed by a mpsc channel. It is
340
+ // used to bridge between the fetch task and the HTTP body stream. The stream
341
+ // has the special property that it errors if the channel is closed before an
342
+ // explicit EOF is sent (in the form of a [None] value on the sender).
343
+ pub struct MpscByteStream {
344
+ receiver : mpsc:: Receiver < Option < bytes:: Bytes > > ,
345
+ shutdown : bool ,
346
+ }
347
+
348
+ impl MpscByteStream {
349
+ pub fn new ( ) -> ( Self , mpsc:: Sender < Option < bytes:: Bytes > > ) {
350
+ let ( sender, receiver) = mpsc:: channel :: < Option < bytes:: Bytes > > ( 1 ) ;
351
+ let this = Self {
352
+ receiver,
353
+ shutdown : false ,
354
+ } ;
355
+ ( this, sender)
356
+ }
357
+ }
358
+
359
+ impl Stream for MpscByteStream {
360
+ type Item = Result < bytes:: Bytes , std:: io:: Error > ;
361
+
362
+ fn poll_next ( mut self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Option < Self :: Item > > {
363
+ let val = std:: task:: ready!( self . receiver. poll_recv( cx) ) ;
364
+ match val {
365
+ None if self . shutdown => Poll :: Ready ( None ) ,
366
+ None => Poll :: Ready ( Some ( Err ( std:: io:: Error :: new (
367
+ std:: io:: ErrorKind :: UnexpectedEof ,
368
+ "channel closed" ,
369
+ ) ) ) ) ,
370
+ Some ( None ) => {
371
+ self . shutdown = true ;
372
+ Poll :: Ready ( None )
373
+ }
374
+ Some ( Some ( val) ) => Poll :: Ready ( Some ( Ok ( val) ) ) ,
375
+ }
376
+ }
377
+ }
0 commit comments