@@ -35,7 +35,7 @@ using v8::Value;
3535namespace quic {
3636
3737#define STREAM_STATE (V ) \
38- V (ID, id, int64_t ) \
38+ V (ID, id, stream_id) \
3939 V (PENDING, pending, uint8_t ) \
4040 V (FIN_SENT, fin_sent, uint8_t ) \
4141 V (FIN_RECEIVED, fin_received, uint8_t ) \
@@ -107,7 +107,7 @@ PendingStream::~PendingStream() {
107107 }
108108}
109109
110- void PendingStream::fulfill (int64_t id) {
110+ void PendingStream::fulfill (stream_id id) {
111111 CHECK (waiting_);
112112 waiting_ = false ;
113113 stream_->NotifyStreamOpened (id);
@@ -145,31 +145,76 @@ Maybe<std::shared_ptr<DataQueue>> Stream::GetDataQueueFromSource(
145145 DCHECK_IMPLIES (!value->IsUndefined (), value->IsObject ());
146146 std::vector<std::unique_ptr<DataQueue::Entry>> entries;
147147 if (value->IsUndefined ()) {
148+ // Return an empty DataQueue.
148149 return Just (std::shared_ptr<DataQueue>());
149150 } else if (value->IsArrayBuffer ()) {
151+ // DataQueue is created from an ArrayBuffer.
150152 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+ }
151168 entries.push_back (DataQueue::CreateInMemoryEntryFromBackingStore (
152- buffer-> GetBackingStore ( ), 0 , buffer-> ByteLength () ));
169+ std::move (backing ), offset, length ));
153170 return Just (DataQueue::CreateIdempotent (std::move (entries)));
154171 } 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>>();
159179 } 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 ()) {
163192 THROW_ERR_INVALID_ARG_TYPE (env, " Data source not detachable" );
164193 return Nothing<std::shared_ptr<DataQueue>>();
165194 }
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));
167204 return Just (DataQueue::CreateIdempotent (std::move (entries)));
168205 } else if (Blob::HasInstance (env, value)) {
169206 Blob* blob;
170207 ASSIGN_OR_RETURN_UNWRAP (
171208 &blob, value, Nothing<std::shared_ptr<DataQueue>>());
172209 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)));
173218 }
174219 // TODO(jasnell): Add streaming sources...
175220 THROW_ERR_INVALID_ARG_TYPE (env, " Invalid data source type" );
@@ -182,15 +227,16 @@ struct Stream::Impl {
182227 // Attaches an outbound data source to the stream.
183228 JS_METHOD (AttachSource) {
184229 Environment* env = Environment::GetCurrent (args);
230+ Stream* stream;
231+ ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
185232
186233 std::shared_ptr<DataQueue> dataqueue;
187234 if (GetDataQueueFromSource (env, args[0 ]).To (&dataqueue)) {
188- Stream* stream;
189- ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
190235 stream->set_outbound (std::move (dataqueue));
191236 }
192237 }
193238
239+ // Immediately and forcefully destroys the stream.
194240 JS_METHOD (Destroy) {
195241 Stream* stream;
196242 ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
@@ -204,6 +250,10 @@ struct Stream::Impl {
204250 }
205251 }
206252
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.
207257 JS_METHOD (SendHeaders) {
208258 Stream* stream;
209259 ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
@@ -233,7 +283,7 @@ struct Stream::Impl {
233283 JS_METHOD (StopSending) {
234284 Stream* stream;
235285 ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
236- uint64_t code = 0 ;
286+ error_code code = 0 ;
237287 CHECK_IMPLIES (!args[0 ]->IsUndefined (), args[0 ]->IsBigInt ());
238288 if (!args[0 ]->IsUndefined ()) {
239289 bool unused = false ; // not used but still necessary.
@@ -258,7 +308,7 @@ struct Stream::Impl {
258308 JS_METHOD (ResetStream) {
259309 Stream* stream;
260310 ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
261- uint64_t code = 0 ;
311+ error_code code = 0 ;
262312 CHECK_IMPLIES (!args[0 ]->IsUndefined (), args[0 ]->IsBigInt ());
263313 if (!args[0 ]->IsUndefined ()) {
264314 bool lossless = false ; // not used but still necessary.
@@ -315,6 +365,8 @@ struct Stream::Impl {
315365 args.GetReturnValue ().Set (static_cast <uint32_t >(priority));
316366 }
317367
368+ // Returns a Blob::Reader that can be used to read data that has been
369+ // received on the stream.
318370 JS_METHOD (GetReader) {
319371 Stream* stream;
320372 ASSIGN_OR_RETURN_UNWRAP (&stream, args.This ());
@@ -758,7 +810,7 @@ Stream* Stream::From(void* stream_user_data) {
758810}
759811
760812BaseObjectPtr<Stream> Stream::Create (Session* session,
761- int64_t id,
813+ stream_id id,
762814 std::shared_ptr<DataQueue> source) {
763815 DCHECK_GE (id, 0 );
764816 DCHECK_NOT_NULL (session);
@@ -778,7 +830,7 @@ BaseObjectPtr<Stream> Stream::Create(Session* session,
778830
779831Stream::Stream (BaseObjectWeakPtr<Session> session,
780832 Local<Object> object,
781- int64_t id,
833+ stream_id id,
782834 std::shared_ptr<DataQueue> source)
783835 : AsyncWrap(session->env (), object, PROVIDER_QUIC_STREAM),
784836 stats_(env()->isolate()),
@@ -787,6 +839,7 @@ Stream::Stream(BaseObjectWeakPtr<Session> session,
787839 inbound_(DataQueue::Create()),
788840 headers_(env()->isolate()) {
789841 MakeWeak ();
842+ DCHECK (id < kMaxStreamId );
790843 state_->id = id;
791844 state_->pending = 0 ;
792845 // Allows us to be notified when data is actually read from the
@@ -818,7 +871,7 @@ Stream::Stream(BaseObjectWeakPtr<Session> session,
818871 std::make_unique<PendingStream>(direction, this , session_)),
819872 headers_(env()->isolate()) {
820873 MakeWeak ();
821- state_->id = - 1 ;
874+ state_->id = kMaxStreamId ;
822875 state_->pending = 1 ;
823876
824877 // Allows us to be notified when data is actually read from the
@@ -841,8 +894,9 @@ Stream::~Stream() {
841894 DCHECK_NE (stats_->destroyed_at , 0 );
842895}
843896
844- void Stream::NotifyStreamOpened (int64_t id) {
897+ void Stream::NotifyStreamOpened (stream_id id) {
845898 CHECK (is_pending ());
899+ DCHECK (id < kMaxStreamId );
846900 Debug (this , " Pending stream opened with id %" PRIi64, id);
847901 state_->pending = 0 ;
848902 state_->id = id;
@@ -886,13 +940,13 @@ void Stream::NotifyStreamOpened(int64_t id) {
886940 if (outbound_) session ().ResumeStream (id);
887941}
888942
889- void Stream::NotifyReadableEnded (uint64_t code) {
943+ void Stream::NotifyReadableEnded (error_code code) {
890944 CHECK (!is_pending ());
891945 Session::SendPendingDataScope send_scope (&session ());
892946 ngtcp2_conn_shutdown_stream_read (session (), 0 , id (), code);
893947}
894948
895- void Stream::NotifyWritableEnded (uint64_t code) {
949+ void Stream::NotifyWritableEnded (error_code code) {
896950 CHECK (!is_pending ());
897951 Session::SendPendingDataScope send_scope (&session ());
898952 ngtcp2_conn_shutdown_stream_write (session (), 0 , id (), code);
@@ -910,7 +964,7 @@ bool Stream::is_pending() const {
910964 return state_->pending ;
911965}
912966
913- int64_t Stream::id () const {
967+ stream_id Stream::id () const {
914968 return state_->id ;
915969}
916970
0 commit comments