Node.js  v8.x
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine
node_http2_core.h
Go to the documentation of this file.
1 #ifndef SRC_NODE_HTTP2_CORE_H_
2 #define SRC_NODE_HTTP2_CORE_H_
3 
4 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5 
6 #include "util.h"
7 #include "util-inl.h"
8 #include "uv.h"
9 #include "nghttp2/nghttp2.h"
10 
11 #include <stdio.h>
12 #include <unordered_map>
13 
14 namespace node {
15 namespace http2 {
16 
17 #ifdef NODE_DEBUG_HTTP2
18 
19 // Adapted from nghttp2 own debug printer
20 static inline void _debug_vfprintf(const char *fmt, va_list args) {
21  vfprintf(stderr, fmt, args);
22 }
23 
24 void inline debug_vfprintf(const char *format, ...) {
25  va_list args;
26  va_start(args, format);
27  _debug_vfprintf(format, args);
28  va_end(args);
29 }
30 
31 #define DEBUG_HTTP2(...) debug_vfprintf(__VA_ARGS__);
32 #else
33 #define DEBUG_HTTP2(...) \
34  do { \
35  } while (0)
36 #endif
37 
38 class Nghttp2Session;
39 class Nghttp2Stream;
40 
41 struct nghttp2_stream_write_t;
42 struct nghttp2_data_chunk_t;
43 struct nghttp2_data_chunks_t;
44 
45 #define MAX_BUFFER_COUNT 10
46 #define SEND_BUFFER_RECOMMENDED_SIZE 4096
47 
48 enum nghttp2_session_type {
49  NGHTTP2_SESSION_SERVER,
50  NGHTTP2_SESSION_CLIENT
51 };
52 
53 enum nghttp2_shutdown_flags {
54  NGHTTP2_SHUTDOWN_FLAG_GRACEFUL
55 };
56 
57 enum nghttp2_stream_flags {
58  NGHTTP2_STREAM_FLAG_NONE = 0x0,
59  // Writable side has ended
60  NGHTTP2_STREAM_FLAG_SHUT = 0x1,
61  // Reading has started
62  NGHTTP2_STREAM_FLAG_READ_START = 0x2,
63  // Reading is paused
64  NGHTTP2_STREAM_FLAG_READ_PAUSED = 0x4,
65  // Stream is closed
66  NGHTTP2_STREAM_FLAG_CLOSED = 0x8,
67  // Stream is destroyed
68  NGHTTP2_STREAM_FLAG_DESTROYED = 0x10
69 };
70 
71 
72 // Callbacks
73 typedef void (*nghttp2_stream_write_cb)(
74  nghttp2_stream_write_t* req,
75  int status);
76 
77 struct nghttp2_stream_write_queue {
78  unsigned int nbufs = 0;
79  nghttp2_stream_write_t* req = nullptr;
80  nghttp2_stream_write_cb cb = nullptr;
81  nghttp2_stream_write_queue* next = nullptr;
82  MaybeStackBuffer<uv_buf_t, MAX_BUFFER_COUNT> bufs;
83 };
84 
85 struct nghttp2_header_list {
86  nghttp2_rcbuf* name = nullptr;
87  nghttp2_rcbuf* value = nullptr;
88  nghttp2_header_list* next = nullptr;
89 };
90 
91 // Handle Types
92 class Nghttp2Session {
93  public:
94  // Initializes the session instance
95  inline int Init(
96  uv_loop_t*,
97  const nghttp2_session_type type = NGHTTP2_SESSION_SERVER,
98  nghttp2_option* options = nullptr,
99  nghttp2_mem* mem = nullptr);
100 
101  // Frees this session instance
102  inline int Free();
103  inline void MarkDestroying();
104  bool IsDestroying() {
105  return destroying_;
106  }
107 
108  inline const char* TypeName(nghttp2_session_type type) {
109  switch (type) {
110  case NGHTTP2_SESSION_SERVER: return "server";
111  case NGHTTP2_SESSION_CLIENT: return "client";
112  default:
113  // This should never happen
114  ABORT();
115  }
116  }
117 
118  // Returns the pointer to the identified stream, or nullptr if
119  // the stream does not exist
120  inline Nghttp2Stream* FindStream(int32_t id);
121 
122  // Submits a new request. If the request is a success, assigned
123  // will be a pointer to the Nghttp2Stream instance assigned.
124  // This only works if the session is a client session.
125  inline int32_t SubmitRequest(
126  nghttp2_priority_spec* prispec,
127  nghttp2_nv* nva,
128  size_t len,
129  Nghttp2Stream** assigned = nullptr,
130  bool emptyPayload = true,
131  bool getTrailers = false);
132 
133  // Submits a notice to the connected peer that the session is in the
134  // process of shutting down.
135  inline void SubmitShutdownNotice();
136 
137  // Submits a SETTINGS frame to the connected peer.
138  inline int SubmitSettings(const nghttp2_settings_entry iv[], size_t niv);
139 
140  // Write data to the session
141  inline ssize_t Write(const uv_buf_t* bufs, unsigned int nbufs);
142 
143  // Returns the nghttp2 library session
144  inline nghttp2_session* session() { return session_; }
145 
146  nghttp2_session_type type() {
147  return session_type_;
148  }
149 
150  protected:
151  // Adds a stream instance to this session
152  inline void AddStream(Nghttp2Stream* stream);
153 
154  // Removes a stream instance from this session
155  inline void RemoveStream(int32_t id);
156 
157  virtual void Send(uv_buf_t* buf, size_t length) {}
158  virtual void OnHeaders(Nghttp2Stream* stream,
159  nghttp2_header_list* headers,
160  nghttp2_headers_category cat,
161  uint8_t flags) {}
162  virtual void OnStreamClose(int32_t id, uint32_t code) {}
163  virtual void OnDataChunk(Nghttp2Stream* stream,
164  nghttp2_data_chunk_t* chunk) {}
165  virtual void OnSettings(bool ack) {}
166  virtual void OnPriority(int32_t id,
167  int32_t parent,
168  int32_t weight,
169  int8_t exclusive) {}
170  virtual void OnGoAway(int32_t lastStreamID,
171  uint32_t errorCode,
172  uint8_t* data,
173  size_t length) {}
174  virtual void OnFrameError(int32_t id,
175  uint8_t type,
176  int error_code) {}
177  virtual ssize_t GetPadding(size_t frameLength,
178  size_t maxFrameLength) { return 0; }
179  virtual void OnFreeSession() {}
180  virtual void AllocateSend(size_t suggested_size, uv_buf_t* buf) = 0;
181 
182  virtual bool HasGetPaddingCallback() { return false; }
183 
184  class SubmitTrailers {
185  public:
186  inline void Submit(nghttp2_nv* trailers, size_t length) const;
187 
188  private:
189  inline SubmitTrailers(Nghttp2Session* handle,
190  Nghttp2Stream* stream,
191  uint32_t* flags);
192 
193  Nghttp2Session* const handle_;
194  Nghttp2Stream* const stream_;
195  uint32_t* const flags_;
196 
197  friend class Nghttp2Session;
198  };
199 
200  virtual void OnTrailers(Nghttp2Stream* stream,
201  const SubmitTrailers& submit_trailers) {}
202 
203  private:
204  inline void SendPendingData();
205  inline void HandleHeadersFrame(const nghttp2_frame* frame);
206  inline void HandlePriorityFrame(const nghttp2_frame* frame);
207  inline void HandleDataFrame(const nghttp2_frame* frame);
208  inline void HandleGoawayFrame(const nghttp2_frame* frame);
209 
210  static inline void GetTrailers(nghttp2_session* session,
211  Nghttp2Session* handle,
212  Nghttp2Stream* stream,
213  uint32_t* flags);
214 
215  /* callbacks for nghttp2 */
216 #ifdef NODE_DEBUG_HTTP2
217  static inline int OnNghttpError(nghttp2_session* session,
218  const char* message,
219  size_t len,
220  void* user_data);
221 #endif
222 
223  static inline int OnBeginHeadersCallback(nghttp2_session* session,
224  const nghttp2_frame* frame,
225  void* user_data);
226  static inline int OnHeaderCallback(nghttp2_session* session,
227  const nghttp2_frame* frame,
228  nghttp2_rcbuf* name,
229  nghttp2_rcbuf* value,
230  uint8_t flags,
231  void* user_data);
232  static inline int OnFrameReceive(nghttp2_session* session,
233  const nghttp2_frame* frame,
234  void* user_data);
235  static inline int OnFrameNotSent(nghttp2_session* session,
236  const nghttp2_frame* frame,
237  int error_code,
238  void* user_data);
239  static inline int OnStreamClose(nghttp2_session* session,
240  int32_t id,
241  uint32_t code,
242  void* user_data);
243  static inline int OnInvalidHeader(nghttp2_session* session,
244  const nghttp2_frame* frame,
245  nghttp2_rcbuf* name,
246  nghttp2_rcbuf* value,
247  uint8_t flags,
248  void* user_data);
249  static inline int OnDataChunkReceived(nghttp2_session* session,
250  uint8_t flags,
251  int32_t id,
252  const uint8_t *data,
253  size_t len,
254  void* user_data);
255  static inline ssize_t OnStreamReadFD(nghttp2_session* session,
256  int32_t id,
257  uint8_t* buf,
258  size_t length,
259  uint32_t* flags,
260  nghttp2_data_source* source,
261  void* user_data);
262  static inline ssize_t OnStreamRead(nghttp2_session* session,
263  int32_t id,
264  uint8_t* buf,
265  size_t length,
266  uint32_t* flags,
267  nghttp2_data_source* source,
268  void* user_data);
269  static inline ssize_t OnSelectPadding(nghttp2_session* session,
270  const nghttp2_frame* frame,
271  size_t maxPayloadLen,
272  void* user_data);
273 
274  struct Callbacks {
275  inline explicit Callbacks(bool kHasGetPaddingCallback);
276  inline ~Callbacks();
277 
278  nghttp2_session_callbacks* callbacks;
279  };
280 
281  /* Use callback_struct_saved[kHasGetPaddingCallback ? 1 : 0] */
282  static Callbacks callback_struct_saved[2];
283 
284  nghttp2_session* session_;
285  uv_loop_t* loop_;
286  uv_prepare_t prep_;
287  nghttp2_session_type session_type_;
288  std::unordered_map<int32_t, Nghttp2Stream*> streams_;
289  bool destroying_ = false;
290 
291  friend class Nghttp2Stream;
292 };
293 
294 
295 
296 class Nghttp2Stream {
297  public:
298  static inline Nghttp2Stream* Init(
299  int32_t id,
300  Nghttp2Session* session,
301  nghttp2_headers_category category = NGHTTP2_HCAT_HEADERS,
302  bool getTrailers = false);
303 
304  inline ~Nghttp2Stream() {
305  CHECK_EQ(session_, nullptr);
306  CHECK_EQ(queue_head_, nullptr);
307  CHECK_EQ(queue_tail_, nullptr);
308  CHECK_EQ(data_chunks_head_, nullptr);
309  CHECK_EQ(data_chunks_tail_, nullptr);
310  CHECK_EQ(current_headers_head_, nullptr);
311  CHECK_EQ(current_headers_tail_, nullptr);
312  DEBUG_HTTP2("Nghttp2Stream %d: freed\n", id_);
313  }
314 
315  inline void FlushDataChunks(bool done = false);
316 
317  // Resets the state of the stream instance to defaults
318  inline void ResetState(
319  int32_t id,
320  Nghttp2Session* session,
321  nghttp2_headers_category category = NGHTTP2_HCAT_HEADERS,
322  bool getTrailers = false);
323 
324  // Destroy this stream instance and free all held memory.
325  // Note that this will free queued outbound and inbound
326  // data chunks and inbound headers, so it's important not
327  // to call this until those are fully consumed.
328  //
329  // Also note: this does not actually destroy the instance.
330  // instead, it frees the held memory, removes the stream
331  // from the parent session, and returns the instance to
332  // the FreeList so that it can be reused.
333  inline void Destroy();
334 
335  // Returns true if this stream has been destroyed
336  inline bool IsDestroyed() const {
337  return flags_ & NGHTTP2_STREAM_FLAG_DESTROYED;
338  }
339 
340  // Queue outbound chunks of data to be sent on this stream
341  inline int Write(
342  nghttp2_stream_write_t* req,
343  const uv_buf_t bufs[],
344  unsigned int nbufs,
345  nghttp2_stream_write_cb cb);
346 
347  // Initiate a response on this stream.
348  inline int SubmitResponse(nghttp2_nv* nva,
349  size_t len,
350  bool emptyPayload = false,
351  bool getTrailers = false);
352 
353  // Send data read from a file descriptor as the response on this stream.
354  inline int SubmitFile(int fd,
355  nghttp2_nv* nva, size_t len,
356  int64_t offset,
357  int64_t length,
358  bool getTrailers = false);
359 
360  // Submit informational headers for this stream
361  inline int SubmitInfo(nghttp2_nv* nva, size_t len);
362 
363  // Submit a PRIORITY frame for this stream
364  inline int SubmitPriority(nghttp2_priority_spec* prispec,
365  bool silent = false);
366 
367  // Submits an RST_STREAM frame using the given code
368  inline int SubmitRstStream(const uint32_t code);
369 
370  // Submits a PUSH_PROMISE frame with this stream as the parent.
371  inline int SubmitPushPromise(
372  nghttp2_nv* nva,
373  size_t len,
374  Nghttp2Stream** assigned = nullptr,
375  bool writable = true);
376 
377  // Marks the Writable side of the stream as being shutdown
378  inline void Shutdown() {
379  flags_ |= NGHTTP2_STREAM_FLAG_SHUT;
380  nghttp2_session_resume_data(session_->session(), id_);
381  }
382 
383  // Returns true if this stream is writable.
384  inline bool IsWritable() const {
385  return !(flags_ & NGHTTP2_STREAM_FLAG_SHUT);
386  }
387 
388  // Start Reading. If there are queued data chunks, they are pushed into
389  // the session to be emitted at the JS side
390  inline void ReadStart();
391 
392  // Stop/Pause Reading.
393  inline void ReadStop();
394 
395  // Returns true if reading is paused
396  inline bool IsPaused() const {
397  return flags_ & NGHTTP2_STREAM_FLAG_READ_PAUSED;
398  }
399 
400  inline bool GetTrailers() const {
401  return getTrailers_;
402  }
403 
404  // Returns true if this stream is in the reading state, which occurs when
405  // the NGHTTP2_STREAM_FLAG_READ_START flag has been set and the
406  // NGHTTP2_STREAM_FLAG_READ_PAUSED flag is *not* set.
407  inline bool IsReading() const {
408  return flags_ & NGHTTP2_STREAM_FLAG_READ_START &&
409  !(flags_ & NGHTTP2_STREAM_FLAG_READ_PAUSED);
410  }
411 
412  inline void Close(int32_t code) {
413  DEBUG_HTTP2("Nghttp2Stream %d: closing with code %d\n", id_, code);
414  flags_ |= NGHTTP2_STREAM_FLAG_CLOSED;
415  code_ = code;
416  session_->OnStreamClose(id_, code);
417  DEBUG_HTTP2("Nghttp2Stream %d: closed\n", id_);
418  }
419 
420  // Returns true if this stream has been closed either by receiving or
421  // sending an RST_STREAM frame.
422  inline bool IsClosed() const {
423  return flags_ & NGHTTP2_STREAM_FLAG_CLOSED;
424  }
425 
426  // Returns the RST_STREAM code used to close this stream
427  inline int32_t code() const {
428  return code_;
429  }
430 
431  // Returns the stream identifier for this stream
432  inline int32_t id() const {
433  return id_;
434  }
435 
436  inline nghttp2_header_list* headers() const {
437  return current_headers_head_;
438  }
439 
440  inline nghttp2_headers_category headers_category() const {
441  return current_headers_category_;
442  }
443 
444  inline void FreeHeaders();
445 
446  void StartHeaders(nghttp2_headers_category category) {
447  DEBUG_HTTP2("Nghttp2Stream %d: starting headers, category: %d\n",
448  id_, category);
449  // We shouldn't be in the middle of a headers block already.
450  // Something bad happened if this fails
451  CHECK_EQ(current_headers_head_, nullptr);
452  CHECK_EQ(current_headers_tail_, nullptr);
453  current_headers_category_ = category;
454  }
455 
456  private:
457  // The Parent HTTP/2 Session
458  Nghttp2Session* session_ = nullptr;
459 
460  // The Stream Identifier
461  int32_t id_ = 0;
462 
463  // Internal state flags
464  int flags_ = 0;
465 
466  // Outbound Data... This is the data written by the JS layer that is
467  // waiting to be written out to the socket.
468  nghttp2_stream_write_queue* queue_head_ = nullptr;
469  nghttp2_stream_write_queue* queue_tail_ = nullptr;
470  unsigned int queue_head_index_ = 0;
471  size_t queue_head_offset_ = 0;
472  int64_t fd_offset_ = 0;
473  int64_t fd_length_ = -1;
474 
475  // The Current Headers block... As headers are received for this stream,
476  // they are temporarily stored here until the OnFrameReceived is called
477  // signalling the end of the HEADERS frame
478  nghttp2_header_list* current_headers_head_ = nullptr;
479  nghttp2_header_list* current_headers_tail_ = nullptr;
480  nghttp2_headers_category current_headers_category_ = NGHTTP2_HCAT_HEADERS;
481 
482  // Inbound Data... This is the data received via DATA frames for this stream.
483  nghttp2_data_chunk_t* data_chunks_head_ = nullptr;
484  nghttp2_data_chunk_t* data_chunks_tail_ = nullptr;
485 
486  // The RST_STREAM code used to close this stream
487  int32_t code_ = NGHTTP2_NO_ERROR;
488 
489  int32_t prev_local_window_size_ = 65535;
490 
491  // True if this stream will have outbound trailers
492  bool getTrailers_ = false;
493 
494  friend class Nghttp2Session;
495 };
496 
497 struct nghttp2_stream_write_t {
498  void* data;
499  int status;
500  Nghttp2Stream* handle;
501  nghttp2_stream_write_queue* item;
502 };
503 
504 struct nghttp2_data_chunk_t {
505  uv_buf_t buf;
506  nghttp2_data_chunk_t* next = nullptr;
507 };
508 
509 struct nghttp2_data_chunks_t {
510  unsigned int nbufs = 0;
511  uv_buf_t buf[MAX_BUFFER_COUNT];
512 
513  inline ~nghttp2_data_chunks_t();
514 };
515 
516 } // namespace http2
517 } // namespace node
518 
519 #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
520 
521 #endif // SRC_NODE_HTTP2_CORE_H_
unsigned char * buf
Definition: cares_wrap.cc:483
int len
Definition: cares_wrap.cc:485
std::string source
Definition: module_wrap.cc:306
int status
Definition: cares_wrap.cc:479
union node::cares_wrap::@8::CaresAsyncData::@0 data
this done
Definition: v8ustack.d:366
uv_fs_t req
Definition: node_file.cc:374
void Init(int *argc, const char **argv, int *exec_argc, const char ***exec_argv)
Definition: node.cc:4351