Node.js  v8.x
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine
node_http2.cc
Go to the documentation of this file.
1 #include "node.h"
2 #include "node_buffer.h"
3 #include "node_http2.h"
4 
5 namespace node {
6 
7 using v8::ArrayBuffer;
8 using v8::Boolean;
9 using v8::Context;
10 using v8::Float64Array;
11 using v8::Function;
12 using v8::Integer;
13 using v8::String;
14 using v8::Uint32;
15 using v8::Uint32Array;
16 using v8::Undefined;
17 
18 namespace http2 {
19 
28 };
29 
41 };
42 
51 };
52 
60 };
61 
67 };
68 
69 struct http2_state {
70  // doubles first so that they are always sizeof(double)-aligned
76 };
77 
78 Freelist<nghttp2_data_chunk_t, FREELIST_MAX>
80 
81 Freelist<Nghttp2Stream, FREELIST_MAX> stream_free_list;
82 
83 Freelist<nghttp2_header_list, FREELIST_MAX> header_free_list;
84 
85 Freelist<nghttp2_data_chunks_t, FREELIST_MAX>
87 
88 Nghttp2Session::Callbacks Nghttp2Session::callback_struct_saved[2] = {
89  Callbacks(false),
90  Callbacks(true)};
91 
92 Http2Options::Http2Options(Environment* env) {
93  nghttp2_option_new(&options_);
94 
95  uint32_t* buffer = env->http2_state_buffer()->options_buffer;
96  uint32_t flags = buffer[IDX_OPTIONS_FLAGS];
97 
98  if (flags & (1 << IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE)) {
99  nghttp2_option_set_max_deflate_dynamic_table_size(
100  options_,
102  }
103 
104  if (flags & (1 << IDX_OPTIONS_MAX_RESERVED_REMOTE_STREAMS)) {
105  nghttp2_option_set_max_reserved_remote_streams(
106  options_,
108  }
109 
110  if (flags & (1 << IDX_OPTIONS_MAX_SEND_HEADER_BLOCK_LENGTH)) {
111  nghttp2_option_set_max_send_header_block_length(
112  options_,
114  }
115 
116  // Recommended default
117  nghttp2_option_set_peer_max_concurrent_streams(options_, 100);
118  if (flags & (1 << IDX_OPTIONS_PEER_MAX_CONCURRENT_STREAMS)) {
119  nghttp2_option_set_peer_max_concurrent_streams(
120  options_,
122  }
123 
124  if (flags & (1 << IDX_OPTIONS_PADDING_STRATEGY)) {
125  padding_strategy_type strategy =
126  static_cast<padding_strategy_type>(
128  SetPaddingStrategy(strategy);
129  }
130 }
131 
132 void Http2Session::OnFreeSession() {
133  ::delete this;
134 }
135 
136 ssize_t Http2Session::OnMaxFrameSizePadding(size_t frameLen,
137  size_t maxPayloadLen) {
138  DEBUG_HTTP2("Http2Session: using max frame size padding\n");
139  return maxPayloadLen;
140 }
141 
142 ssize_t Http2Session::OnCallbackPadding(size_t frameLen,
143  size_t maxPayloadLen) {
144  DEBUG_HTTP2("Http2Session: using callback padding\n");
145  Isolate* isolate = env()->isolate();
146  Local<Context> context = env()->context();
147 
148  HandleScope handle_scope(isolate);
149  Context::Scope context_scope(context);
150 
151  if (object()->Has(context, env()->ongetpadding_string()).FromJust()) {
152  uint32_t* buffer = env()->http2_state_buffer()->padding_buffer;
153  buffer[PADDING_BUF_FRAME_LENGTH] = frameLen;
154  buffer[PADDING_BUF_MAX_PAYLOAD_LENGTH] = maxPayloadLen;
155  MakeCallback(env()->ongetpadding_string(), 0, nullptr);
156  uint32_t retval = buffer[PADDING_BUF_RETURN_VALUE];
157  retval = retval <= maxPayloadLen ? retval : maxPayloadLen;
158  retval = retval >= frameLen ? retval : frameLen;
159  CHECK_GE(retval, frameLen);
160  CHECK_LE(retval, maxPayloadLen);
161  return retval;
162  }
163  return frameLen;
164 }
165 
166 void Http2Session::SetNextStreamID(const FunctionCallbackInfo<Value>& args) {
167  Environment* env = Environment::GetCurrent(args);
168  Http2Session* session;
169  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
170  nghttp2_session* s = session->session();
171  int32_t id = args[0]->Int32Value(env->context()).ToChecked();
172  DEBUG_HTTP2("Http2Session: setting next stream id to %d\n", id);
173  nghttp2_session_set_next_stream_id(s, id);
174 }
175 
176 void HttpErrorString(const FunctionCallbackInfo<Value>& args) {
177  Environment* env = Environment::GetCurrent(args);
178  uint32_t val = args[0]->Uint32Value(env->context()).ToChecked();
179  args.GetReturnValue().Set(
180  OneByteString(env->isolate(), nghttp2_strerror(val)));
181 }
182 
183 // Serializes the settings object into a Buffer instance that
184 // would be suitable, for instance, for creating the Base64
185 // output for an HTTP2-Settings header field.
186 void PackSettings(const FunctionCallbackInfo<Value>& args) {
187  Environment* env = Environment::GetCurrent(args);
188  HandleScope scope(env->isolate());
189 
190  std::vector<nghttp2_settings_entry> entries;
191  entries.reserve(6);
192 
193  uint32_t* buffer = env->http2_state_buffer()->settings_buffer;
194  uint32_t flags = buffer[IDX_SETTINGS_COUNT];
195 
196  if (flags & (1 << IDX_SETTINGS_HEADER_TABLE_SIZE)) {
197  DEBUG_HTTP2("Setting header table size: %d\n",
199  entries.push_back({NGHTTP2_SETTINGS_HEADER_TABLE_SIZE,
201  }
202 
203  if (flags & (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) {
204  DEBUG_HTTP2("Setting max concurrent streams: %d\n",
206  entries.push_back({NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
208  }
209 
210  if (flags & (1 << IDX_SETTINGS_MAX_FRAME_SIZE)) {
211  DEBUG_HTTP2("Setting max frame size: %d\n",
213  entries.push_back({NGHTTP2_SETTINGS_MAX_FRAME_SIZE,
214  buffer[IDX_SETTINGS_MAX_FRAME_SIZE]});
215  }
216 
217  if (flags & (1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE)) {
218  DEBUG_HTTP2("Setting initial window size: %d\n",
220  entries.push_back({NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,
222  }
223 
224  if (flags & (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE)) {
225  DEBUG_HTTP2("Setting max header list size: %d\n",
227  entries.push_back({NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
229  }
230 
231  if (flags & (1 << IDX_SETTINGS_ENABLE_PUSH)) {
232  DEBUG_HTTP2("Setting enable push: %d\n",
233  buffer[IDX_SETTINGS_ENABLE_PUSH]);
234  entries.push_back({NGHTTP2_SETTINGS_ENABLE_PUSH,
235  buffer[IDX_SETTINGS_ENABLE_PUSH]});
236  }
237 
238  const size_t len = entries.size() * 6;
239  MaybeStackBuffer<char> buf(len);
240  ssize_t ret =
241  nghttp2_pack_settings_payload(
242  reinterpret_cast<uint8_t*>(*buf), len, &entries[0], entries.size());
243  if (ret >= 0) {
244  args.GetReturnValue().Set(
245  Buffer::Copy(env, *buf, len).ToLocalChecked());
246  }
247 }
248 
249 // Used to fill in the spec defined initial values for each setting.
250 void RefreshDefaultSettings(const FunctionCallbackInfo<Value>& args) {
251  DEBUG_HTTP2("Http2Session: refreshing default settings\n");
252  Environment* env = Environment::GetCurrent(args);
253  uint32_t* buffer = env->http2_state_buffer()->settings_buffer;
255  DEFAULT_SETTINGS_HEADER_TABLE_SIZE;
256  buffer[IDX_SETTINGS_ENABLE_PUSH] =
257  DEFAULT_SETTINGS_ENABLE_PUSH;
259  DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE;
261  DEFAULT_SETTINGS_MAX_FRAME_SIZE;
262  buffer[IDX_SETTINGS_COUNT] =
264  (1 << IDX_SETTINGS_ENABLE_PUSH) |
267 }
268 
269 template <get_setting fn>
270 void RefreshSettings(const FunctionCallbackInfo<Value>& args) {
271  DEBUG_HTTP2("Http2Session: refreshing settings for session\n");
272  Environment* env = Environment::GetCurrent(args);
273  CHECK_EQ(args.Length(), 1);
274  CHECK(args[0]->IsObject());
275  Http2Session* session;
276  ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As<Object>());
277  nghttp2_session* s = session->session();
278 
279  uint32_t* buffer = env->http2_state_buffer()->settings_buffer;
281  fn(s, NGHTTP2_SETTINGS_HEADER_TABLE_SIZE);
283  fn(s, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
285  fn(s, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
287  fn(s, NGHTTP2_SETTINGS_MAX_FRAME_SIZE);
289  fn(s, NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE);
290  buffer[IDX_SETTINGS_ENABLE_PUSH] =
291  fn(s, NGHTTP2_SETTINGS_ENABLE_PUSH);
292 }
293 
294 // Used to fill in the spec defined initial values for each setting.
295 void RefreshSessionState(const FunctionCallbackInfo<Value>& args) {
296  DEBUG_HTTP2("Http2Session: refreshing session state\n");
297  Environment* env = Environment::GetCurrent(args);
298  CHECK_EQ(args.Length(), 1);
299  CHECK(args[0]->IsObject());
300  double* buffer = env->http2_state_buffer()->session_state_buffer;
301  Http2Session* session;
302  ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As<Object>());
303  nghttp2_session* s = session->session();
304 
306  nghttp2_session_get_effective_local_window_size(s);
308  nghttp2_session_get_effective_recv_data_length(s);
310  nghttp2_session_get_next_stream_id(s);
312  nghttp2_session_get_local_window_size(s);
314  nghttp2_session_get_last_proc_stream_id(s);
316  nghttp2_session_get_remote_window_size(s);
318  nghttp2_session_get_outbound_queue_size(s);
320  nghttp2_session_get_hd_deflate_dynamic_table_size(s);
322  nghttp2_session_get_hd_inflate_dynamic_table_size(s);
323 }
324 
325 void RefreshStreamState(const FunctionCallbackInfo<Value>& args) {
326  Environment* env = Environment::GetCurrent(args);
327  CHECK_EQ(args.Length(), 2);
328  CHECK(args[0]->IsObject());
329  CHECK(args[1]->IsNumber());
330  int32_t id = args[1]->Int32Value(env->context()).ToChecked();
331  DEBUG_HTTP2("Http2Session: refreshing stream %d state\n", id);
332  Http2Session* session;
333  ASSIGN_OR_RETURN_UNWRAP(&session, args[0].As<Object>());
334  nghttp2_session* s = session->session();
335  Nghttp2Stream* stream;
336 
337  double* buffer = env->http2_state_buffer()->stream_state_buffer;
338 
339  if ((stream = session->FindStream(id)) == nullptr) {
340  buffer[IDX_STREAM_STATE] = NGHTTP2_STREAM_STATE_IDLE;
341  buffer[IDX_STREAM_STATE_WEIGHT] =
346  return;
347  }
348  nghttp2_stream* str =
349  nghttp2_session_find_stream(s, stream->id());
350 
351  if (str == nullptr) {
352  buffer[IDX_STREAM_STATE] = NGHTTP2_STREAM_STATE_IDLE;
353  buffer[IDX_STREAM_STATE_WEIGHT] =
358  } else {
359  buffer[IDX_STREAM_STATE] =
360  nghttp2_stream_get_state(str);
361  buffer[IDX_STREAM_STATE_WEIGHT] =
362  nghttp2_stream_get_weight(str);
364  nghttp2_stream_get_sum_dependency_weight(str);
366  nghttp2_session_get_stream_local_close(s, id);
368  nghttp2_session_get_stream_remote_close(s, id);
370  nghttp2_session_get_stream_local_window_size(s, id);
371  }
372 }
373 
374 void Http2Session::New(const FunctionCallbackInfo<Value>& args) {
375  Environment* env = Environment::GetCurrent(args);
376  CHECK(args.IsConstructCall());
377 
378  int val = args[0]->IntegerValue(env->context()).ToChecked();
379  nghttp2_session_type type = static_cast<nghttp2_session_type>(val);
380  DEBUG_HTTP2("Http2Session: creating a session of type: %d\n", type);
381  new Http2Session(env, args.This(), type);
382 }
383 
384 
385 // Capture the stream that this session will use to send and receive data
386 void Http2Session::Consume(const FunctionCallbackInfo<Value>& args) {
387  Http2Session* session;
388  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
389  CHECK(args[0]->IsExternal());
390  session->Consume(args[0].As<External>());
391 }
392 
393 void Http2Session::Destroy(const FunctionCallbackInfo<Value>& args) {
394  Http2Session* session;
395  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
396  DEBUG_HTTP2("Http2Session: destroying session %d\n", session->type());
397  Environment* env = Environment::GetCurrent(args);
398  Local<Context> context = env->context();
399 
400  bool skipUnconsume = args[0]->BooleanValue(context).ToChecked();
401 
402  if (!skipUnconsume)
403  session->Unconsume();
404  session->Free();
405 }
406 
407 void Http2Session::Destroying(const FunctionCallbackInfo<Value>& args) {
408  Http2Session* session;
409  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
410  DEBUG_HTTP2("Http2Session: preparing to destroy session %d\n",
411  session->type());
412  session->MarkDestroying();
413 }
414 
415 void Http2Session::SubmitPriority(const FunctionCallbackInfo<Value>& args) {
416  Environment* env = Environment::GetCurrent(args);
417  Http2Session* session;
418  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
419  Local<Context> context = env->context();
420 
421  nghttp2_priority_spec spec;
422  int32_t id = args[0]->Int32Value(context).ToChecked();
423  int32_t parent = args[1]->Int32Value(context).ToChecked();
424  int32_t weight = args[2]->Int32Value(context).ToChecked();
425  bool exclusive = args[3]->BooleanValue(context).ToChecked();
426  bool silent = args[4]->BooleanValue(context).ToChecked();
427  DEBUG_HTTP2("Http2Session: submitting priority for stream %d: "
428  "parent: %d, weight: %d, exclusive: %d, silent: %d\n",
429  id, parent, weight, exclusive, silent);
430  CHECK_GT(id, 0);
431  CHECK_GE(parent, 0);
432  CHECK_GE(weight, 0);
433 
434  Nghttp2Stream* stream;
435  if (!(stream = session->FindStream(id))) {
436  // invalid stream
437  return args.GetReturnValue().Set(NGHTTP2_ERR_INVALID_STREAM_ID);
438  }
439  nghttp2_priority_spec_init(&spec, parent, weight, exclusive ? 1 : 0);
440 
441  args.GetReturnValue().Set(stream->SubmitPriority(&spec, silent));
442 }
443 
444 void Http2Session::SubmitSettings(const FunctionCallbackInfo<Value>& args) {
445  Http2Session* session;
446  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
447  Environment* env = session->env();
448 
449  uint32_t* buffer = env->http2_state_buffer()->settings_buffer;
450  uint32_t flags = buffer[IDX_SETTINGS_COUNT];
451 
452  std::vector<nghttp2_settings_entry> entries;
453  entries.reserve(6);
454 
455  if (flags & (1 << IDX_SETTINGS_HEADER_TABLE_SIZE)) {
456  DEBUG_HTTP2("Setting header table size: %d\n",
458  entries.push_back({NGHTTP2_SETTINGS_HEADER_TABLE_SIZE,
460  }
461 
462  if (flags & (1 << IDX_SETTINGS_MAX_CONCURRENT_STREAMS)) {
463  DEBUG_HTTP2("Setting max concurrent streams: %d\n",
465  entries.push_back({NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
467  }
468 
469  if (flags & (1 << IDX_SETTINGS_MAX_FRAME_SIZE)) {
470  DEBUG_HTTP2("Setting max frame size: %d\n",
472  entries.push_back({NGHTTP2_SETTINGS_MAX_FRAME_SIZE,
473  buffer[IDX_SETTINGS_MAX_FRAME_SIZE]});
474  }
475 
476  if (flags & (1 << IDX_SETTINGS_INITIAL_WINDOW_SIZE)) {
477  DEBUG_HTTP2("Setting initial window size: %d\n",
479  entries.push_back({NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE,
481  }
482 
483  if (flags & (1 << IDX_SETTINGS_MAX_HEADER_LIST_SIZE)) {
484  DEBUG_HTTP2("Setting max header list size: %d\n",
486  entries.push_back({NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE,
488  }
489 
490  if (flags & (1 << IDX_SETTINGS_ENABLE_PUSH)) {
491  DEBUG_HTTP2("Setting enable push: %d\n",
492  buffer[IDX_SETTINGS_ENABLE_PUSH]);
493  entries.push_back({NGHTTP2_SETTINGS_ENABLE_PUSH,
494  buffer[IDX_SETTINGS_ENABLE_PUSH]});
495  }
496 
497  if (entries.size() > 0) {
498  args.GetReturnValue().Set(
499  session->Nghttp2Session::SubmitSettings(&entries[0], entries.size()));
500  } else {
501  args.GetReturnValue().Set(
502  session->Nghttp2Session::SubmitSettings(nullptr, 0));
503  }
504 }
505 
506 void Http2Session::SubmitRstStream(const FunctionCallbackInfo<Value>& args) {
507  Environment* env = Environment::GetCurrent(args);
508  Local<Context> context = env->context();
509  CHECK(args[0]->IsNumber());
510  CHECK(args[1]->IsNumber());
511 
512  Http2Session* session;
513  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
514 
515  int32_t id = args[0]->Int32Value(context).ToChecked();
516  uint32_t code = args[1]->Uint32Value(context).ToChecked();
517 
518  Nghttp2Stream* stream;
519  if (!(stream = session->FindStream(id))) {
520  // invalid stream
521  return args.GetReturnValue().Set(NGHTTP2_ERR_INVALID_STREAM_ID);
522  }
523  DEBUG_HTTP2("Http2Session: sending rst_stream for stream %d, code: %d\n",
524  id, code);
525  args.GetReturnValue().Set(stream->SubmitRstStream(code));
526 }
527 
528 void Http2Session::SubmitRequest(const FunctionCallbackInfo<Value>& args) {
529  // args[0] Array of headers
530  // args[1] endStream boolean
531  // args[2] parentStream ID (for priority spec)
532  // args[3] weight (for priority spec)
533  // args[4] exclusive boolean (for priority spec)
534  CHECK(args[0]->IsArray());
535 
536  Http2Session* session;
537  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
538  Environment* env = session->env();
539  Local<Context> context = env->context();
540  Isolate* isolate = env->isolate();
541 
542  Local<Array> headers = args[0].As<Array>();
543  bool endStream = args[1]->BooleanValue(context).ToChecked();
544  int32_t parent = args[2]->Int32Value(context).ToChecked();
545  int32_t weight = args[3]->Int32Value(context).ToChecked();
546  bool exclusive = args[4]->BooleanValue(context).ToChecked();
547  bool getTrailers = args[5]->BooleanValue(context).ToChecked();
548 
549  DEBUG_HTTP2("Http2Session: submitting request: headers: %d, end-stream: %d, "
550  "parent: %d, weight: %d, exclusive: %d\n", headers->Length(),
551  endStream, parent, weight, exclusive);
552 
553  nghttp2_priority_spec prispec;
554  nghttp2_priority_spec_init(&prispec, parent, weight, exclusive ? 1 : 0);
555 
556  Headers list(isolate, context, headers);
557 
558  int32_t ret = session->Nghttp2Session::SubmitRequest(&prispec,
559  *list, list.length(),
560  nullptr, endStream,
561  getTrailers);
562  DEBUG_HTTP2("Http2Session: request submitted, response: %d\n", ret);
563  args.GetReturnValue().Set(ret);
564 }
565 
566 void Http2Session::SubmitResponse(const FunctionCallbackInfo<Value>& args) {
567  CHECK(args[0]->IsNumber());
568  CHECK(args[1]->IsArray());
569 
570  Http2Session* session;
571  Nghttp2Stream* stream;
572 
573  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
574  Environment* env = session->env();
575  Local<Context> context = env->context();
576  Isolate* isolate = env->isolate();
577 
578  int32_t id = args[0]->Int32Value(context).ToChecked();
579  Local<Array> headers = args[1].As<Array>();
580  bool endStream = args[2]->BooleanValue(context).ToChecked();
581  bool getTrailers = args[3]->BooleanValue(context).ToChecked();
582 
583  DEBUG_HTTP2("Http2Session: submitting response for stream %d: headers: %d, "
584  "end-stream: %d\n", id, headers->Length(), endStream);
585 
586  if (!(stream = session->FindStream(id))) {
587  return args.GetReturnValue().Set(NGHTTP2_ERR_INVALID_STREAM_ID);
588  }
589 
590  Headers list(isolate, context, headers);
591 
592  args.GetReturnValue().Set(
593  stream->SubmitResponse(*list, list.length(), endStream, getTrailers));
594 }
595 
596 void Http2Session::SubmitFile(const FunctionCallbackInfo<Value>& args) {
597  CHECK(args[0]->IsNumber()); // Stream ID
598  CHECK(args[1]->IsNumber()); // File Descriptor
599  CHECK(args[2]->IsArray()); // Headers
600  CHECK(args[3]->IsNumber()); // Offset
601  CHECK(args[4]->IsNumber()); // Length
602 
603  Http2Session* session;
604  Nghttp2Stream* stream;
605 
606  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
607  Environment* env = session->env();
608  Local<Context> context = env->context();
609  Isolate* isolate = env->isolate();
610 
611  int32_t id = args[0]->Int32Value(context).ToChecked();
612  int fd = args[1]->Int32Value(context).ToChecked();
613  Local<Array> headers = args[2].As<Array>();
614 
615  int64_t offset = args[3]->IntegerValue(context).ToChecked();
616  int64_t length = args[4]->IntegerValue(context).ToChecked();
617  bool getTrailers = args[5]->BooleanValue(context).ToChecked();
618 
619  CHECK_GE(offset, 0);
620 
621  DEBUG_HTTP2("Http2Session: submitting file %d for stream %d: headers: %d, "
622  "end-stream: %d\n", fd, id, headers->Length());
623 
624  if (!(stream = session->FindStream(id))) {
625  return args.GetReturnValue().Set(NGHTTP2_ERR_INVALID_STREAM_ID);
626  }
627 
628  Headers list(isolate, context, headers);
629 
630  args.GetReturnValue().Set(stream->SubmitFile(fd, *list, list.length(),
631  offset, length, getTrailers));
632 }
633 
634 void Http2Session::SendHeaders(const FunctionCallbackInfo<Value>& args) {
635  CHECK(args[0]->IsNumber());
636  CHECK(args[1]->IsArray());
637 
638  Http2Session* session;
639  Nghttp2Stream* stream;
640 
641  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
642  Environment* env = session->env();
643  Local<Context> context = env->context();
644  Isolate* isolate = env->isolate();
645 
646  int32_t id = args[0]->Int32Value(env->context()).ToChecked();
647  Local<Array> headers = args[1].As<Array>();
648 
649  DEBUG_HTTP2("Http2Session: sending informational headers for stream %d, "
650  "count: %d\n", id, headers->Length());
651 
652  if (!(stream = session->FindStream(id))) {
653  return args.GetReturnValue().Set(NGHTTP2_ERR_INVALID_STREAM_ID);
654  }
655 
656  Headers list(isolate, context, headers);
657 
658  args.GetReturnValue().Set(stream->SubmitInfo(*list, list.length()));
659 }
660 
661 void Http2Session::ShutdownStream(const FunctionCallbackInfo<Value>& args) {
662  Environment* env = Environment::GetCurrent(args);
663  CHECK(args[0]->IsNumber());
664  Http2Session* session;
665  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
666  Nghttp2Stream* stream;
667  int32_t id = args[0]->Int32Value(env->context()).ToChecked();
668  DEBUG_HTTP2("Http2Session: shutting down stream %d\n", id);
669  if (!(stream = session->FindStream(id))) {
670  return args.GetReturnValue().Set(NGHTTP2_ERR_INVALID_STREAM_ID);
671  }
672  stream->Shutdown();
673 }
674 
675 
676 void Http2Session::StreamReadStart(const FunctionCallbackInfo<Value>& args) {
677  Environment* env = Environment::GetCurrent(args);
678  CHECK(args[0]->IsNumber());
679  Http2Session* session;
680  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
681  Nghttp2Stream* stream;
682  int32_t id = args[0]->Int32Value(env->context()).ToChecked();
683  if (!(stream = session->FindStream(id))) {
684  return args.GetReturnValue().Set(NGHTTP2_ERR_INVALID_STREAM_ID);
685  }
686  stream->ReadStart();
687 }
688 
689 
690 void Http2Session::StreamReadStop(const FunctionCallbackInfo<Value>& args) {
691  Environment* env = Environment::GetCurrent(args);
692  CHECK(args[0]->IsNumber());
693  Http2Session* session;
694  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
695  Nghttp2Stream* stream;
696  int32_t id = args[0]->Int32Value(env->context()).ToChecked();
697  if (!(stream = session->FindStream(id))) {
698  return args.GetReturnValue().Set(NGHTTP2_ERR_INVALID_STREAM_ID);
699  }
700  stream->ReadStop();
701 }
702 
703 void Http2Session::SendShutdownNotice(
704  const FunctionCallbackInfo<Value>& args) {
705  Http2Session* session;
706  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
707  session->SubmitShutdownNotice();
708 }
709 
710 void Http2Session::SubmitGoaway(const FunctionCallbackInfo<Value>& args) {
711  Http2Session* session;
712  Environment* env = Environment::GetCurrent(args);
713  Local<Context> context = env->context();
714  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
715 
716  uint32_t errorCode = args[0]->Uint32Value(context).ToChecked();
717  int32_t lastStreamID = args[1]->Int32Value(context).ToChecked();
718  Local<Value> opaqueData = args[2];
719 
720  uint8_t* data = NULL;
721  size_t length = 0;
722 
723  if (opaqueData->BooleanValue(context).ToChecked()) {
724  THROW_AND_RETURN_UNLESS_BUFFER(env, opaqueData);
725  SPREAD_BUFFER_ARG(opaqueData, buf);
726  data = reinterpret_cast<uint8_t*>(buf_data);
727  length = buf_length;
728  }
729 
730  DEBUG_HTTP2("Http2Session: initiating immediate shutdown. "
731  "last-stream-id: %d, code: %d, opaque-data: %d\n",
732  lastStreamID, errorCode, length);
733  int status = nghttp2_submit_goaway(session->session(),
734  NGHTTP2_FLAG_NONE,
735  lastStreamID,
736  errorCode,
737  data, length);
738  args.GetReturnValue().Set(status);
739 }
740 
741 void Http2Session::DestroyStream(const FunctionCallbackInfo<Value>& args) {
742  Environment* env = Environment::GetCurrent(args);
743  Http2Session* session;
744  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
745 
746  CHECK_EQ(args.Length(), 1);
747  CHECK(args[0]->IsNumber());
748  int32_t id = args[0]->Int32Value(env->context()).ToChecked();
749  DEBUG_HTTP2("Http2Session: destroy stream %d\n", id);
750  Nghttp2Stream* stream;
751  if (!(stream = session->FindStream(id))) {
752  return args.GetReturnValue().Set(NGHTTP2_ERR_INVALID_STREAM_ID);
753  }
754  stream->Destroy();
755 }
756 
757 void Http2Session::SubmitPushPromise(const FunctionCallbackInfo<Value>& args) {
758  Http2Session* session;
759  Environment* env = Environment::GetCurrent(args);
760  Local<Context> context = env->context();
761  Isolate* isolate = env->isolate();
762  ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder());
763 
764  CHECK(args[0]->IsNumber()); // parent stream ID
765  CHECK(args[1]->IsArray()); // headers array
766 
767  Nghttp2Stream* parent;
768  int32_t id = args[0]->Int32Value(context).ToChecked();
769  Local<Array> headers = args[1].As<Array>();
770  bool endStream = args[2]->BooleanValue(context).ToChecked();
771 
772  DEBUG_HTTP2("Http2Session: submitting push promise for stream %d: "
773  "end-stream: %d, headers: %d\n", id, endStream,
774  headers->Length());
775 
776  if (!(parent = session->FindStream(id))) {
777  return args.GetReturnValue().Set(NGHTTP2_ERR_INVALID_STREAM_ID);
778  }
779 
780  Headers list(isolate, context, headers);
781 
782  int32_t ret = parent->SubmitPushPromise(*list, list.length(),
783  nullptr, endStream);
784  DEBUG_HTTP2("Http2Session: push promise submitted, ret: %d\n", ret);
785  args.GetReturnValue().Set(ret);
786 }
787 
788 int Http2Session::DoWrite(WriteWrap* req_wrap,
789  uv_buf_t* bufs,
790  size_t count,
791  uv_stream_t* send_handle) {
792  Environment* env = req_wrap->env();
793  Local<Object> req_wrap_obj = req_wrap->object();
794  Local<Context> context = env->context();
795 
796  Nghttp2Stream* stream;
797  {
798  Local<Value> val =
799  req_wrap_obj->Get(context, env->stream_string()).ToLocalChecked();
800  int32_t id = val->Int32Value(context).ToChecked();
801  if (!val->IsNumber() || !(stream = FindStream(id))) {
802  // invalid stream
803  req_wrap->Dispatched();
804  req_wrap->Done(0);
805  return NGHTTP2_ERR_INVALID_STREAM_ID;
806  }
807  }
808 
809  nghttp2_stream_write_t* req = new nghttp2_stream_write_t;
810  req->data = req_wrap;
811 
812  auto AfterWrite = [](nghttp2_stream_write_t* req, int status) {
813  WriteWrap* wrap = static_cast<WriteWrap*>(req->data);
814  wrap->Done(status);
815  delete req;
816  };
817  req_wrap->Dispatched();
818  stream->Write(req, bufs, count, AfterWrite);
819  return 0;
820 }
821 
822 void Http2Session::AllocateSend(size_t recommended, uv_buf_t* buf) {
823  buf->base = stream_alloc();
824  buf->len = kAllocBufferSize;
825 }
826 
827 void Http2Session::Send(uv_buf_t* buf, size_t length) {
828  DEBUG_HTTP2("Http2Session: Attempting to send data\n");
829  if (stream_ == nullptr || !stream_->IsAlive() || stream_->IsClosing()) {
830  return;
831  }
832  HandleScope scope(env()->isolate());
833  auto AfterWrite = [](WriteWrap* req_wrap, int status) {
834  req_wrap->Dispose();
835  };
836  Local<Object> req_wrap_obj =
837  env()->write_wrap_constructor_function()
838  ->NewInstance(env()->context()).ToLocalChecked();
839  WriteWrap* write_req = WriteWrap::New(env(),
840  req_wrap_obj,
841  this,
842  AfterWrite);
843 
844  uv_buf_t actual = uv_buf_init(buf->base, length);
845  if (stream_->DoWrite(write_req, &actual, 1, nullptr)) {
846  write_req->Dispose();
847  }
848 }
849 
850 void Http2Session::OnTrailers(Nghttp2Stream* stream,
851  const SubmitTrailers& submit_trailers) {
852  DEBUG_HTTP2("Http2Session: prompting for trailers on stream %d\n",
853  stream->id());
854  Local<Context> context = env()->context();
855  Isolate* isolate = env()->isolate();
856  HandleScope scope(isolate);
857  Context::Scope context_scope(context);
858 
859  if (object()->Has(context, env()->ontrailers_string()).FromJust()) {
860  Local<Value> argv[1] = {
861  Integer::New(isolate, stream->id())
862  };
863 
864  Local<Value> ret = MakeCallback(env()->ontrailers_string(),
865  arraysize(argv), argv).ToLocalChecked();
866  if (!ret.IsEmpty()) {
867  if (ret->IsArray()) {
868  Local<Array> headers = ret.As<Array>();
869  if (headers->Length() > 0) {
870  Headers trailers(isolate, context, headers);
871  submit_trailers.Submit(*trailers, trailers.length());
872  }
873  }
874  }
875  }
876 }
877 
878 void Http2Session::OnHeaders(Nghttp2Stream* stream,
879  nghttp2_header_list* headers,
880  nghttp2_headers_category cat,
881  uint8_t flags) {
882  Local<Context> context = env()->context();
883  Isolate* isolate = env()->isolate();
884  Context::Scope context_scope(context);
885  HandleScope scope(isolate);
886  Local<String> name_str;
887  Local<String> value_str;
888 
889  Local<Array> holder = Array::New(isolate);
890  Local<Function> fn = env()->push_values_to_array_function();
891  Local<Value> argv[NODE_PUSH_VAL_TO_ARRAY_MAX * 2];
892 
893  // The headers are passed in above as a linked list of nghttp2_header_list
894  // structs. The following converts that into a JS array with the structure:
895  // [name1, value1, name2, value2, name3, value3, name3, value4] and so on.
896  // That array is passed up to the JS layer and converted into an Object form
897  // like {name1: value1, name2: value2, name3: [value3, value4]}. We do it
898  // this way for performance reasons (it's faster to generate and pass an
899  // array than it is to generate and pass the object).
900  do {
901  size_t j = 0;
902  while (headers != nullptr && j < arraysize(argv) / 2) {
903  nghttp2_header_list* item = headers;
904  // The header name and value are passed as external one-byte strings
905  name_str =
906  ExternalHeader::New<true>(env(), item->name).ToLocalChecked();
907  value_str =
908  ExternalHeader::New<false>(env(), item->value).ToLocalChecked();
909  argv[j * 2] = name_str;
910  argv[j * 2 + 1] = value_str;
911  headers = item->next;
912  j++;
913  }
914  // For performance, we pass name and value pairs to array.protototype.push
915  // in batches of size NODE_PUSH_VAL_TO_ARRAY_MAX * 2 until there are no
916  // more items to push.
917  if (j > 0) {
918  fn->Call(env()->context(), holder, j * 2, argv).ToLocalChecked();
919  }
920  } while (headers != nullptr);
921 
922  if (object()->Has(context, env()->onheaders_string()).FromJust()) {
923  Local<Value> argv[4] = {
924  Integer::New(isolate, stream->id()),
925  Integer::New(isolate, cat),
926  Integer::New(isolate, flags),
927  holder
928  };
929  MakeCallback(env()->onheaders_string(), arraysize(argv), argv);
930  }
931 }
932 
933 
934 void Http2Session::OnStreamClose(int32_t id, uint32_t code) {
935  Isolate* isolate = env()->isolate();
936  Local<Context> context = env()->context();
937  HandleScope scope(isolate);
938  Context::Scope context_scope(context);
939  if (object()->Has(context, env()->onstreamclose_string()).FromJust()) {
940  Local<Value> argv[2] = {
941  Integer::New(isolate, id),
942  Integer::NewFromUnsigned(isolate, code)
943  };
944  MakeCallback(env()->onstreamclose_string(), arraysize(argv), argv);
945  }
946 }
947 
948 void FreeDataChunk(char* data, void* hint) {
949  nghttp2_data_chunk_t* item = reinterpret_cast<nghttp2_data_chunk_t*>(hint);
950  delete[] data;
951  data_chunk_free_list.push(item);
952 }
953 
954 void Http2Session::OnDataChunk(
955  Nghttp2Stream* stream,
956  nghttp2_data_chunk_t* chunk) {
957  Isolate* isolate = env()->isolate();
958  Local<Context> context = env()->context();
959  HandleScope scope(isolate);
960  Local<Object> obj = Object::New(isolate);
961  obj->Set(context,
962  env()->id_string(),
963  Integer::New(isolate, stream->id())).FromJust();
964  ssize_t len = -1;
965  Local<Object> buf;
966  if (chunk != nullptr) {
967  len = chunk->buf.len;
968  buf = Buffer::New(isolate,
969  chunk->buf.base, len,
971  chunk).ToLocalChecked();
972  }
973  EmitData(len, buf, obj);
974 }
975 
976 void Http2Session::OnSettings(bool ack) {
977  Local<Context> context = env()->context();
978  Isolate* isolate = env()->isolate();
979  HandleScope scope(isolate);
980  Context::Scope context_scope(context);
981  if (object()->Has(context, env()->onsettings_string()).FromJust()) {
982  Local<Value> argv[1] = { Boolean::New(isolate, ack) };
983  MakeCallback(env()->onsettings_string(), arraysize(argv), argv);
984  }
985 }
986 
987 void Http2Session::OnFrameError(int32_t id, uint8_t type, int error_code) {
988  Local<Context> context = env()->context();
989  Isolate* isolate = env()->isolate();
990  HandleScope scope(isolate);
991  Context::Scope context_scope(context);
992  if (object()->Has(context, env()->onframeerror_string()).FromJust()) {
993  Local<Value> argv[3] = {
994  Integer::New(isolate, id),
995  Integer::New(isolate, type),
996  Integer::New(isolate, error_code)
997  };
998  MakeCallback(env()->onframeerror_string(), arraysize(argv), argv);
999  }
1000 }
1001 
1002 void Http2Session::OnPriority(int32_t stream,
1003  int32_t parent,
1004  int32_t weight,
1005  int8_t exclusive) {
1006  Local<Context> context = env()->context();
1007  Isolate* isolate = env()->isolate();
1008  HandleScope scope(isolate);
1009  Context::Scope context_scope(context);
1010  if (object()->Has(context, env()->onpriority_string()).FromJust()) {
1011  Local<Value> argv[4] = {
1012  Integer::New(isolate, stream),
1013  Integer::New(isolate, parent),
1014  Integer::New(isolate, weight),
1015  Boolean::New(isolate, exclusive)
1016  };
1017  MakeCallback(env()->onpriority_string(), arraysize(argv), argv);
1018  }
1019 }
1020 
1021 void Http2Session::OnGoAway(int32_t lastStreamID,
1022  uint32_t errorCode,
1023  uint8_t* data,
1024  size_t length) {
1025  Local<Context> context = env()->context();
1026  Isolate* isolate = env()->isolate();
1027  HandleScope scope(isolate);
1028  Context::Scope context_scope(context);
1029  if (object()->Has(context, env()->ongoawaydata_string()).FromJust()) {
1030  Local<Value> argv[3] = {
1031  Integer::NewFromUnsigned(isolate, errorCode),
1032  Integer::New(isolate, lastStreamID),
1033  Undefined(isolate)
1034  };
1035 
1036  if (length > 0) {
1037  argv[2] = Buffer::Copy(isolate,
1038  reinterpret_cast<char*>(data),
1039  length).ToLocalChecked();
1040  }
1041 
1042  MakeCallback(env()->ongoawaydata_string(), arraysize(argv), argv);
1043  }
1044 }
1045 
1046 void Http2Session::OnStreamAllocImpl(size_t suggested_size,
1047  uv_buf_t* buf,
1048  void* ctx) {
1049  Http2Session* session = static_cast<Http2Session*>(ctx);
1050  buf->base = session->stream_alloc();
1051  buf->len = kAllocBufferSize;
1052 }
1053 
1054 
1055 void Http2Session::OnStreamReadImpl(ssize_t nread,
1056  const uv_buf_t* bufs,
1057  uv_handle_type pending,
1058  void* ctx) {
1059  Http2Session* session = static_cast<Http2Session*>(ctx);
1060  if (nread < 0) {
1061  uv_buf_t tmp_buf;
1062  tmp_buf.base = nullptr;
1063  tmp_buf.len = 0;
1064  session->prev_read_cb_.fn(nread,
1065  &tmp_buf,
1066  pending,
1067  session->prev_read_cb_.ctx);
1068  return;
1069  }
1070  if (nread > 0) {
1071  // Only pass data on if nread > 0
1072  uv_buf_t buf[] { uv_buf_init((*bufs).base, nread) };
1073  ssize_t ret = session->Write(buf, 1);
1074  if (ret < 0) {
1075  DEBUG_HTTP2("Http2Session: fatal error receiving data: %d\n", ret);
1076  nghttp2_session_terminate_session(session->session(),
1077  NGHTTP2_PROTOCOL_ERROR);
1078  }
1079  }
1080 }
1081 
1082 
1083 void Http2Session::Consume(Local<External> external) {
1084  DEBUG_HTTP2("Http2Session: consuming socket\n");
1085  CHECK(prev_alloc_cb_.is_empty());
1086  StreamBase* stream = static_cast<StreamBase*>(external->Value());
1087  CHECK_NE(stream, nullptr);
1088  stream->Consume();
1089  stream_ = stream;
1090  prev_alloc_cb_ = stream->alloc_cb();
1091  prev_read_cb_ = stream->read_cb();
1092  stream->set_alloc_cb({ Http2Session::OnStreamAllocImpl, this });
1093  stream->set_read_cb({ Http2Session::OnStreamReadImpl, this });
1094 }
1095 
1096 
1097 void Http2Session::Unconsume() {
1098  DEBUG_HTTP2("Http2Session: unconsuming socket\n");
1099  if (prev_alloc_cb_.is_empty())
1100  return;
1101  stream_->set_alloc_cb(prev_alloc_cb_);
1102  stream_->set_read_cb(prev_read_cb_);
1103  prev_alloc_cb_.clear();
1104  prev_read_cb_.clear();
1105  stream_ = nullptr;
1106 }
1107 
1108 
1109 Headers::Headers(Isolate* isolate,
1110  Local<Context> context,
1111  Local<Array> headers) {
1112  CHECK_EQ(headers->Length(), 2);
1113  Local<Value> header_string = headers->Get(context, 0).ToLocalChecked();
1114  Local<Value> header_count = headers->Get(context, 1).ToLocalChecked();
1115  CHECK(header_string->IsString());
1116  CHECK(header_count->IsUint32());
1117  count_ = header_count.As<Uint32>()->Value();
1118  int header_string_len = header_string.As<String>()->Length();
1119 
1120  if (count_ == 0) {
1121  CHECK_EQ(header_string_len, 0);
1122  return;
1123  }
1124 
1125  // Allocate a single buffer with count_ nghttp2_nv structs, followed
1126  // by the raw header data as passed from JS. This looks like:
1127  // | possible padding | nghttp2_nv | nghttp2_nv | ... | header contents |
1128  buf_.AllocateSufficientStorage((alignof(nghttp2_nv) - 1) +
1129  count_ * sizeof(nghttp2_nv) +
1130  header_string_len);
1131  // Make sure the start address is aligned appropriately for an nghttp2_nv*.
1132  char* start = reinterpret_cast<char*>(
1133  ROUND_UP(reinterpret_cast<uintptr_t>(*buf_), alignof(nghttp2_nv)));
1134  char* header_contents = start + (count_ * sizeof(nghttp2_nv));
1135  nghttp2_nv* const nva = reinterpret_cast<nghttp2_nv*>(start);
1136 
1137  CHECK_LE(header_contents + header_string_len, *buf_ + buf_.length());
1138  CHECK_EQ(header_string.As<String>()
1139  ->WriteOneByte(reinterpret_cast<uint8_t*>(header_contents),
1140  0, header_string_len,
1141  String::NO_NULL_TERMINATION),
1142  header_string_len);
1143 
1144  size_t n = 0;
1145  char* p;
1146  for (p = header_contents; p < header_contents + header_string_len; n++) {
1147  if (n >= count_) {
1148  // This can happen if a passed header contained a null byte. In that
1149  // case, just provide nghttp2 with an invalid header to make it reject
1150  // the headers list.
1151  static uint8_t zero = '\0';
1152  nva[0].name = nva[0].value = &zero;
1153  nva[0].namelen = nva[0].valuelen = 1;
1154  count_ = 1;
1155  return;
1156  }
1157 
1158  nva[n].flags = NGHTTP2_NV_FLAG_NONE;
1159  nva[n].name = reinterpret_cast<uint8_t*>(p);
1160  nva[n].namelen = strlen(p);
1161  p += nva[n].namelen + 1;
1162  nva[n].value = reinterpret_cast<uint8_t*>(p);
1163  nva[n].valuelen = strlen(p);
1164  p += nva[n].valuelen + 1;
1165  }
1166 
1167  CHECK_EQ(p, header_contents + header_string_len);
1168  CHECK_EQ(n, count_);
1169 }
1170 
1171 
1172 void Initialize(Local<Object> target,
1173  Local<Value> unused,
1174  Local<Context> context,
1175  void* priv) {
1176  Environment* env = Environment::GetCurrent(context);
1177  Isolate* isolate = env->isolate();
1178  HandleScope scope(isolate);
1179 
1180  http2_state* state = Calloc<http2_state>(1);
1181  env->set_http2_state_buffer(state);
1182  auto state_ab = ArrayBuffer::New(isolate, state, sizeof(*state));
1183 
1184 #define SET_STATE_TYPEDARRAY(name, type, field) \
1185  target->Set(context, \
1186  FIXED_ONE_BYTE_STRING(isolate, (name)), \
1187  type::New(state_ab, \
1188  offsetof(http2_state, field), \
1189  arraysize(state->field))) \
1190  .FromJust()
1191 
1192  // Initialize the buffer used for padding callbacks
1193  SET_STATE_TYPEDARRAY("paddingBuffer", Uint32Array, padding_buffer);
1194  // Initialize the buffer used to store the session state
1195  SET_STATE_TYPEDARRAY("sessionState", Float64Array, session_state_buffer);
1196  // Initialize the buffer used to store the stream state
1197  SET_STATE_TYPEDARRAY("streamState", Float64Array, stream_state_buffer);
1198  SET_STATE_TYPEDARRAY("settingsBuffer", Uint32Array, settings_buffer);
1199  SET_STATE_TYPEDARRAY("optionsBuffer", Uint32Array, options_buffer);
1200 #undef SET_STATE_TYPEDARRAY
1201 
1205 
1206  // Method to fetch the nghttp2 string description of an nghttp2 error code
1207  env->SetMethod(target, "nghttp2ErrorString", HttpErrorString);
1208 
1209  Local<String> http2SessionClassName =
1210  FIXED_ONE_BYTE_STRING(isolate, "Http2Session");
1211 
1212  Local<FunctionTemplate> session =
1213  env->NewFunctionTemplate(Http2Session::New);
1214  session->SetClassName(http2SessionClassName);
1215  session->InstanceTemplate()->SetInternalFieldCount(1);
1216  AsyncWrap::AddWrapMethods(env, session);
1217  env->SetProtoMethod(session, "consume",
1218  Http2Session::Consume);
1219  env->SetProtoMethod(session, "destroy",
1220  Http2Session::Destroy);
1221  env->SetProtoMethod(session, "destroying",
1222  Http2Session::Destroying);
1223  env->SetProtoMethod(session, "sendHeaders",
1224  Http2Session::SendHeaders);
1225  env->SetProtoMethod(session, "submitShutdownNotice",
1226  Http2Session::SendShutdownNotice);
1227  env->SetProtoMethod(session, "submitGoaway",
1228  Http2Session::SubmitGoaway);
1229  env->SetProtoMethod(session, "submitSettings",
1230  Http2Session::SubmitSettings);
1231  env->SetProtoMethod(session, "submitPushPromise",
1232  Http2Session::SubmitPushPromise);
1233  env->SetProtoMethod(session, "submitRstStream",
1234  Http2Session::SubmitRstStream);
1235  env->SetProtoMethod(session, "submitResponse",
1236  Http2Session::SubmitResponse);
1237  env->SetProtoMethod(session, "submitFile",
1238  Http2Session::SubmitFile);
1239  env->SetProtoMethod(session, "submitRequest",
1240  Http2Session::SubmitRequest);
1241  env->SetProtoMethod(session, "submitPriority",
1242  Http2Session::SubmitPriority);
1243  env->SetProtoMethod(session, "shutdownStream",
1244  Http2Session::ShutdownStream);
1245  env->SetProtoMethod(session, "streamReadStart",
1246  Http2Session::StreamReadStart);
1247  env->SetProtoMethod(session, "streamReadStop",
1248  Http2Session::StreamReadStop);
1249  env->SetProtoMethod(session, "setNextStreamID",
1250  Http2Session::SetNextStreamID);
1251  env->SetProtoMethod(session, "destroyStream",
1252  Http2Session::DestroyStream);
1253  StreamBase::AddMethods<Http2Session>(env, session,
1254  StreamBase::kFlagHasWritev |
1255  StreamBase::kFlagNoShutdown);
1256  target->Set(context,
1257  http2SessionClassName,
1258  session->GetFunction()).FromJust();
1259 
1260  Local<Object> constants = Object::New(isolate);
1261  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SESSION_SERVER);
1262  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SESSION_CLIENT);
1263  NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_IDLE);
1264  NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_OPEN);
1265  NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_RESERVED_LOCAL);
1266  NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_RESERVED_REMOTE);
1267  NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_HALF_CLOSED_LOCAL);
1268  NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_HALF_CLOSED_REMOTE);
1269  NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_STATE_CLOSED);
1270  NODE_DEFINE_CONSTANT(constants, NGHTTP2_NO_ERROR);
1271  NODE_DEFINE_CONSTANT(constants, NGHTTP2_PROTOCOL_ERROR);
1272  NODE_DEFINE_CONSTANT(constants, NGHTTP2_INTERNAL_ERROR);
1273  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLOW_CONTROL_ERROR);
1274  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_TIMEOUT);
1275  NODE_DEFINE_CONSTANT(constants, NGHTTP2_STREAM_CLOSED);
1276  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FRAME_SIZE_ERROR);
1277  NODE_DEFINE_CONSTANT(constants, NGHTTP2_REFUSED_STREAM);
1278  NODE_DEFINE_CONSTANT(constants, NGHTTP2_CANCEL);
1279  NODE_DEFINE_CONSTANT(constants, NGHTTP2_COMPRESSION_ERROR);
1280  NODE_DEFINE_CONSTANT(constants, NGHTTP2_CONNECT_ERROR);
1281  NODE_DEFINE_CONSTANT(constants, NGHTTP2_ENHANCE_YOUR_CALM);
1282  NODE_DEFINE_CONSTANT(constants, NGHTTP2_INADEQUATE_SECURITY);
1283  NODE_DEFINE_CONSTANT(constants, NGHTTP2_HTTP_1_1_REQUIRED);
1284 
1285  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_HCAT_REQUEST);
1286  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_HCAT_RESPONSE);
1287  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_HCAT_PUSH_RESPONSE);
1288  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_HCAT_HEADERS);
1289  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_NV_FLAG_NONE);
1290  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_NV_FLAG_NO_INDEX);
1291  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_ERR_DEFERRED);
1292  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_ERR_NOMEM);
1293  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE);
1294  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_ERR_INVALID_ARGUMENT);
1295  NODE_DEFINE_HIDDEN_CONSTANT(constants, NGHTTP2_ERR_STREAM_CLOSED);
1296  NODE_DEFINE_CONSTANT(constants, NGHTTP2_ERR_FRAME_SIZE_ERROR);
1297 
1298  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_NONE);
1299  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_END_STREAM);
1300  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_END_HEADERS);
1301  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_ACK);
1302  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_PADDED);
1303  NODE_DEFINE_CONSTANT(constants, NGHTTP2_FLAG_PRIORITY);
1304 
1305  NODE_DEFINE_CONSTANT(constants, DEFAULT_SETTINGS_HEADER_TABLE_SIZE);
1306  NODE_DEFINE_CONSTANT(constants, DEFAULT_SETTINGS_ENABLE_PUSH);
1307  NODE_DEFINE_CONSTANT(constants, DEFAULT_SETTINGS_INITIAL_WINDOW_SIZE);
1308  NODE_DEFINE_CONSTANT(constants, DEFAULT_SETTINGS_MAX_FRAME_SIZE);
1309  NODE_DEFINE_CONSTANT(constants, MAX_MAX_FRAME_SIZE);
1310  NODE_DEFINE_CONSTANT(constants, MIN_MAX_FRAME_SIZE);
1311  NODE_DEFINE_CONSTANT(constants, MAX_INITIAL_WINDOW_SIZE);
1312  NODE_DEFINE_CONSTANT(constants, NGHTTP2_DEFAULT_WEIGHT);
1313 
1314  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_HEADER_TABLE_SIZE);
1315  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_ENABLE_PUSH);
1316  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
1317  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE);
1318  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_MAX_FRAME_SIZE);
1319  NODE_DEFINE_CONSTANT(constants, NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE);
1320 
1321  NODE_DEFINE_CONSTANT(constants, PADDING_STRATEGY_NONE);
1322  NODE_DEFINE_CONSTANT(constants, PADDING_STRATEGY_MAX);
1323  NODE_DEFINE_CONSTANT(constants, PADDING_STRATEGY_CALLBACK);
1324 
1325 #define STRING_CONSTANT(NAME, VALUE) \
1326  NODE_DEFINE_STRING_CONSTANT(constants, "HTTP2_HEADER_" # NAME, VALUE);
1327 HTTP_KNOWN_HEADERS(STRING_CONSTANT)
1328 #undef STRING_CONSTANT
1329 
1330 #define STRING_CONSTANT(NAME, VALUE) \
1331  NODE_DEFINE_STRING_CONSTANT(constants, "HTTP2_METHOD_" # NAME, VALUE);
1332 HTTP_KNOWN_METHODS(STRING_CONSTANT)
1333 #undef STRING_CONSTANT
1334 
1335 #define V(name, _) NODE_DEFINE_CONSTANT(constants, HTTP_STATUS_##name);
1336 HTTP_STATUS_CODES(V)
1337 #undef V
1338 
1339  env->SetMethod(target, "refreshLocalSettings",
1340  RefreshSettings<nghttp2_session_get_local_settings>);
1341  env->SetMethod(target, "refreshRemoteSettings",
1342  RefreshSettings<nghttp2_session_get_remote_settings>);
1343  env->SetMethod(target, "refreshDefaultSettings", RefreshDefaultSettings);
1344  env->SetMethod(target, "refreshSessionState", RefreshSessionState);
1345  env->SetMethod(target, "refreshStreamState", RefreshStreamState);
1346  env->SetMethod(target, "packSettings", PackSettings);
1347 
1348  target->Set(context,
1349  FIXED_ONE_BYTE_STRING(isolate, "constants"),
1350  constants).FromJust();
1351 }
1352 } // namespace http2
1353 } // namespace node
1354 
StreamResource::Callback< StreamResource::AllocCb > prev_alloc_cb_
Freelist< Nghttp2Stream, FREELIST_MAX > stream_free_list
Definition: node_http2.cc:81
void HttpErrorString(const FunctionCallbackInfo< Value > &args)
Definition: node_http2.cc:176
unsigned char * buf
Definition: cares_wrap.cc:483
uint32_t options_buffer[IDX_OPTIONS_FLAGS+1]
Definition: node_http2.cc:74
NODE_MODULE_CONTEXT_AWARE_BUILTIN(inspector, node::inspector::Agent::InitInspector)
void RefreshSettings(const FunctionCallbackInfo< Value > &args)
Definition: node_http2.cc:270
int len
Definition: cares_wrap.cc:485
QueryWrap * wrap
Definition: cares_wrap.cc:478
#define V(name, _)
uint32_t padding_buffer[PADDING_BUF_FIELD_COUNT]
Definition: node_http2.cc:73
int status
Definition: cares_wrap.cc:479
union node::cares_wrap::@8::CaresAsyncData::@0 data
double stream_state_buffer[IDX_STREAM_STATE_COUNT]
Definition: node_http2.cc:72
void RefreshDefaultSettings(const FunctionCallbackInfo< Value > &args)
Definition: node_http2.cc:250
Http2SessionStateIndex
Definition: node_http2.cc:30
void FreeDataChunk(char *data, void *hint)
Definition: node_http2.cc:948
double session_state_buffer[IDX_SESSION_STATE_COUNT]
Definition: node_http2.cc:71
void RefreshStreamState(const FunctionCallbackInfo< Value > &args)
Definition: node_http2.cc:325
size_t Length(Local< Value > val)
Definition: node_buffer.cc:227
#define STRING_CONSTANT(NAME, VALUE)
void PackSettings(const FunctionCallbackInfo< Value > &args)
Definition: node_http2.cc:186
void RefreshSessionState(const FunctionCallbackInfo< Value > &args)
Definition: node_http2.cc:295
void Initialize(Local< Object > target, Local< Value > unused, Local< Context > context, void *priv)
Definition: node_http2.cc:1172
Freelist< nghttp2_data_chunks_t, FREELIST_MAX > data_chunks_free_list
Definition: node_http2.cc:86
dtrace p
Definition: v8ustack.d:615
#define SET_STATE_TYPEDARRAY(name, type, field)
dtrace s
Definition: v8ustack.d:615
#define NODE_DEFINE_HIDDEN_CONSTANT(target, constant)
Definition: node.h:256
Http2PaddingBufferFields
Definition: node_http2.cc:62
uv_fs_t req
Definition: node_file.cc:374
MaybeLocal< Object > New(Isolate *isolate, Local< String > string, enum encoding enc)
Definition: node_buffer.cc:241
MaybeLocal< Value > MakeCallback(Isolate *isolate, Local< Object > recv, Local< Function > callback, int argc, Local< Value > *argv, async_id asyncId, async_id triggerAsyncId)
Definition: async-wrap.cc:802
uint32_t settings_buffer[IDX_SETTINGS_COUNT+1]
Definition: node_http2.cc:75
dtrace n
Definition: v8ustack.d:531
Freelist< nghttp2_header_list, FREELIST_MAX > header_free_list
Definition: node_http2.cc:83
this ctx
Definition: v8ustack.d:369
#define NODE_DEFINE_CONSTANT(target, constant)
Definition: node.h:239
MaybeLocal< Object > Copy(Isolate *isolate, const char *data, size_t length)
Definition: node_buffer.cc:320
StreamResource::Callback< StreamResource::ReadCb > prev_read_cb_
Freelist< nghttp2_data_chunk_t, FREELIST_MAX > data_chunk_free_list
Definition: node_http2.cc:79