@@ -35,7 +35,7 @@ using v8::Value;
35
35
namespace quic {
36
36
37
37
#define STREAM_STATE (V ) \
38
- V (ID, id, int64_t ) \
38
+ V (ID, id, stream_id) \
39
39
V (PENDING, pending, uint8_t ) \
40
40
V (FIN_SENT, fin_sent, uint8_t ) \
41
41
V (FIN_RECEIVED, fin_received, uint8_t ) \
@@ -107,7 +107,7 @@ PendingStream::~PendingStream() {
107
107
}
108
108
}
109
109
110
- void PendingStream::fulfill (int64_t id) {
110
+ void PendingStream::fulfill (stream_id id) {
111
111
CHECK (waiting_);
112
112
waiting_ = false ;
113
113
stream_->NotifyStreamOpened (id);
@@ -145,31 +145,76 @@ Maybe<std::shared_ptr<DataQueue>> Stream::GetDataQueueFromSource(
145
145
DCHECK_IMPLIES (!value->IsUndefined (), value->IsObject ());
146
146
std::vector<std::unique_ptr<DataQueue::Entry>> entries;
147
147
if (value->IsUndefined ()) {
148
+ // Return an empty DataQueue.
148
149
return Just (std::shared_ptr<DataQueue>());
149
150
} else if (value->IsArrayBuffer ()) {
151
+ // DataQueue is created from an ArrayBuffer.
150
152
auto buffer = value.As <ArrayBuffer>();
153
+ // We require that the ArrayBuffer be detachable. This ensures that the
154
+ // underlying memory can be transferred to the DataQueue without risk
155
+ // of the memory being modified by JavaScript code while it is owned
156
+ // by the DataQueue.
157
+ if (!buffer->IsDetachable ()) {
158
+ THROW_ERR_INVALID_ARG_TYPE (env, " Data source not detachable" );
159
+ return Nothing<std::shared_ptr<DataQueue>>();
160
+ }
161
+ auto backing = buffer->GetBackingStore ();
162
+ uint64_t offset = 0 ;
163
+ uint64_t length = buffer->ByteLength ();
164
+ if (buffer->Detach (Local<Value>()).IsNothing ()) {
165
+ THROW_ERR_INVALID_ARG_TYPE (env, " Data source not detachable" );
166
+ return Nothing<std::shared_ptr<DataQueue>>();
167
+ }
151
168
entries.push_back (DataQueue::CreateInMemoryEntryFromBackingStore (
152
- buffer-> GetBackingStore ( ), 0 , buffer-> ByteLength () ));
169
+ std::move (backing ), offset, length ));
153
170
return Just (DataQueue::CreateIdempotent (std::move (entries)));
154
171
} else if (value->IsSharedArrayBuffer ()) {
155
- auto buffer = value.As <SharedArrayBuffer>();
156
- entries.push_back (DataQueue::CreateInMemoryEntryFromBackingStore (
157
- buffer->GetBackingStore (), 0 , buffer->ByteLength ()));
158
- return Just (DataQueue::CreateIdempotent (std::move (entries)));
172
+ // We aren't going to allow use of SharedArrayBuffer as a data source.
173
+ // The reason is that SharedArrayBuffer memory is possibly shared with
174
+ // other JavaScript code and we cannot detach it, making it impossible
175
+ // for us to guarantee that the memory will not be modified while it
176
+ // is owned by the DataQueue.
177
+ THROW_ERR_INVALID_ARG_TYPE (env, " SharedArrayBuffer is not allowed" );
178
+ return Nothing<std::shared_ptr<DataQueue>>();
159
179
} else if (value->IsArrayBufferView ()) {
160
- auto entry =
161
- DataQueue::CreateInMemoryEntryFromView (value.As <ArrayBufferView>());
162
- if (!entry) {
180
+ auto view = value.As <ArrayBufferView>();
181
+ auto buffer = view->Buffer ();
182
+ if (buffer->IsSharedArrayBuffer ()) {
183
+ // We aren't going to allow use of SharedArrayBuffer as a data source.
184
+ // The reason is that SharedArrayBuffer memory is possibly shared with
185
+ // other JavaScript code and we cannot detach it, making it impossible
186
+ // for us to guarantee that the memory will not be modified while it
187
+ // is owned by the DataQueue.
188
+ THROW_ERR_INVALID_ARG_TYPE (env, " SharedArrayBuffer is not allowed" );
189
+ return Nothing<std::shared_ptr<DataQueue>>();
190
+ }
191
+ if (!buffer->IsDetachable ()) {
163
192
THROW_ERR_INVALID_ARG_TYPE (env, " Data source not detachable" );
164
193
return Nothing<std::shared_ptr<DataQueue>>();
165
194
}
166
- entries.push_back (std::move (entry));
195
+ if (buffer->Detach (Local<Value>()).IsNothing ()) {
196
+ THROW_ERR_INVALID_ARG_TYPE (env, " Data source not detachable" );
197
+ return Nothing<std::shared_ptr<DataQueue>>();
198
+ }
199
+ auto backing = buffer->GetBackingStore ();
200
+ auto offset = view->ByteOffset ();
201
+ auto length = view->ByteLength ();
202
+ entries.push_back (DataQueue::CreateInMemoryEntryFromBackingStore (
203
+ std::move (backing), offset, length));
167
204
return Just (DataQueue::CreateIdempotent (std::move (entries)));
168
205
} else if (Blob::HasInstance (env, value)) {
169
206
Blob* blob;
170
207
ASSIGN_OR_RETURN_UNWRAP (
171
208
&blob, value, Nothing<std::shared_ptr<DataQueue>>());
172
209
return Just (blob->getDataQueue ().slice (0 ));
210
+ } else if (value->IsString ()) {
211
+ Utf8Value str (env->isolate (), value);
212
+ JS_TRY_ALLOCATE_BACKING_OR_RETURN (
213
+ env, backing, str.length (), Nothing<std::shared_ptr<DataQueue>>());
214
+ memcpy (backing->Data (), *str, str.length ());
215
+ entries.push_back (DataQueue::CreateInMemoryEntryFromBackingStore (
216
+ std::move (backing), 0 , backing->ByteLength ()));
217
+ return Just (DataQueue::CreateIdempotent (std::move (entries)));
173
218
}
174
219
// TODO(jasnell): Add streaming sources...
175
220
THROW_ERR_INVALID_ARG_TYPE (env, " Invalid data source type" );
@@ -182,15 +227,16 @@ struct Stream::Impl {
182
227
// Attaches an outbound data source to the stream.
183
228
JS_METHOD (AttachSource) {
184
229
Environment* env = Environment::GetCurrent (args);
230
+ Stream* stream;
231
+ ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
185
232
186
233
std::shared_ptr<DataQueue> dataqueue;
187
234
if (GetDataQueueFromSource (env, args[0 ]).To (&dataqueue)) {
188
- Stream* stream;
189
- ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
190
235
stream->set_outbound (std::move (dataqueue));
191
236
}
192
237
}
193
238
239
+ // Immediately and forcefully destroys the stream.
194
240
JS_METHOD (Destroy) {
195
241
Stream* stream;
196
242
ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
@@ -204,6 +250,10 @@ struct Stream::Impl {
204
250
}
205
251
}
206
252
253
+ // Sends a block of headers to the peer. If the stream is not yet open,
254
+ // the headers will be queued and sent immediately when the stream is
255
+ // opened. If the application does not support sending headers on streams,
256
+ // they will be ignored and dropped on the floor.
207
257
JS_METHOD (SendHeaders) {
208
258
Stream* stream;
209
259
ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
@@ -233,7 +283,7 @@ struct Stream::Impl {
233
283
JS_METHOD (StopSending) {
234
284
Stream* stream;
235
285
ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
236
- uint64_t code = 0 ;
286
+ error_code code = 0 ;
237
287
CHECK_IMPLIES (!args[0 ]->IsUndefined (), args[0 ]->IsBigInt ());
238
288
if (!args[0 ]->IsUndefined ()) {
239
289
bool unused = false ; // not used but still necessary.
@@ -258,7 +308,7 @@ struct Stream::Impl {
258
308
JS_METHOD (ResetStream) {
259
309
Stream* stream;
260
310
ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
261
- uint64_t code = 0 ;
311
+ error_code code = 0 ;
262
312
CHECK_IMPLIES (!args[0 ]->IsUndefined (), args[0 ]->IsBigInt ());
263
313
if (!args[0 ]->IsUndefined ()) {
264
314
bool lossless = false ; // not used but still necessary.
@@ -315,6 +365,8 @@ struct Stream::Impl {
315
365
args.GetReturnValue ().Set (static_cast <uint32_t >(priority));
316
366
}
317
367
368
+ // Returns a Blob::Reader that can be used to read data that has been
369
+ // received on the stream.
318
370
JS_METHOD (GetReader) {
319
371
Stream* stream;
320
372
ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
@@ -758,7 +810,7 @@ Stream* Stream::From(void* stream_user_data) {
758
810
}
759
811
760
812
BaseObjectPtr<Stream> Stream::Create (Session* session,
761
- int64_t id,
813
+ stream_id id,
762
814
std::shared_ptr<DataQueue> source) {
763
815
DCHECK_GE (id, 0 );
764
816
DCHECK_NOT_NULL (session);
@@ -778,7 +830,7 @@ BaseObjectPtr<Stream> Stream::Create(Session* session,
778
830
779
831
Stream::Stream (BaseObjectWeakPtr<Session> session,
780
832
Local<Object> object,
781
- int64_t id,
833
+ stream_id id,
782
834
std::shared_ptr<DataQueue> source)
783
835
: AsyncWrap(session->env (), object, PROVIDER_QUIC_STREAM),
784
836
stats_(env()->isolate()),
@@ -787,6 +839,7 @@ Stream::Stream(BaseObjectWeakPtr<Session> session,
787
839
inbound_(DataQueue::Create()),
788
840
headers_(env()->isolate()) {
789
841
MakeWeak ();
842
+ DCHECK (id < kMaxStreamId );
790
843
state_->id = id;
791
844
state_->pending = 0 ;
792
845
// Allows us to be notified when data is actually read from the
@@ -818,7 +871,7 @@ Stream::Stream(BaseObjectWeakPtr<Session> session,
818
871
std::make_unique<PendingStream>(direction, this , session_)),
819
872
headers_(env()->isolate()) {
820
873
MakeWeak ();
821
- state_->id = - 1 ;
874
+ state_->id = kMaxStreamId ;
822
875
state_->pending = 1 ;
823
876
824
877
// Allows us to be notified when data is actually read from the
@@ -841,8 +894,9 @@ Stream::~Stream() {
841
894
DCHECK_NE (stats_->destroyed_at , 0 );
842
895
}
843
896
844
- void Stream::NotifyStreamOpened (int64_t id) {
897
+ void Stream::NotifyStreamOpened (stream_id id) {
845
898
CHECK (is_pending ());
899
+ DCHECK (id < kMaxStreamId );
846
900
Debug (this , " Pending stream opened with id %" PRIi64, id);
847
901
state_->pending = 0 ;
848
902
state_->id = id;
@@ -886,13 +940,13 @@ void Stream::NotifyStreamOpened(int64_t id) {
886
940
if (outbound_) session ().ResumeStream (id);
887
941
}
888
942
889
- void Stream::NotifyReadableEnded (uint64_t code) {
943
+ void Stream::NotifyReadableEnded (error_code code) {
890
944
CHECK (!is_pending ());
891
945
Session::SendPendingDataScope send_scope (&session ());
892
946
ngtcp2_conn_shutdown_stream_read (session (), 0 , id (), code);
893
947
}
894
948
895
- void Stream::NotifyWritableEnded (uint64_t code) {
949
+ void Stream::NotifyWritableEnded (error_code code) {
896
950
CHECK (!is_pending ());
897
951
Session::SendPendingDataScope send_scope (&session ());
898
952
ngtcp2_conn_shutdown_stream_write (session (), 0 , id (), code);
@@ -910,7 +964,7 @@ bool Stream::is_pending() const {
910
964
return state_->pending ;
911
965
}
912
966
913
- int64_t Stream::id () const {
967
+ stream_id Stream::id () const {
914
968
return state_->id ;
915
969
}
916
970
0 commit comments