[Edge 2016] HTTP/2 and Asynchronous APIs

[Edge 2016] HTTP/2 and Asynchronous APIs

HTTP/2 (H2) is coming, and along with it a whole new way of communicating over the web. Connection re-use, prioritization, multiplexing, and server push are just some of the features in H2.

In this talk we'll look at how to handle HTTP/2 Server Push programmatically, and how it can affect your application architecture.

Fee39f0c0ffb29d9ac21607ed188be6b?s=128

Davey Shafik

October 19, 2016
Tweet

Transcript

  1. H T T P/ 2 A N D A SY

    N C H R O N O U S A P I S
  2. F E TC H I N G A B LO

    G P O ST + CO M M E N TS CC-BY: John Trainor
  3. Ȑ

  4. Ȑ

  5. Ȑ GET /post/1

  6. Ȑ GET /post/1 200 OK application/json

  7. { "type": "post", "id": "1", "title": "JSON API paints my

    bikeshed!", "tags": ["json", "api", "relationships"], "author": "http://example.com/posts/1/author", "comments": "http://example.com/posts/1/comments" }
  8. { "type": "post", "id": "1", "title": "JSON API paints my

    bikeshed!", "tags": ["json", "api", "relationships"], "author": "http://example.com/posts/1/author", "comments": "http://example.com/posts/1/comments" }
  9. Ȑ

  10. Ȑ

  11. Ȑ GET /posts/1/comments

  12. Ȑ GET /posts/1/comments 200 OK application/json

  13. [ "http://example.org/post/1/comment/1", "http://example.org/post/1/comment/2", "http://example.org/post/1/comment/3", "http://example.org/post/1/comment/4" ]

  14. Ȑ

  15. Ȑ

  16. Ȑ GET /post/example/comments/1

  17. Ȑ GET /post/example/comments/ 200 OK application/json 1

  18. 2 Ȑ GET /post/example/comments/ 200 OK application/json 1

  19. 2 3 Ȑ GET /post/example/comments/ 200 OK application/json 1

  20. 2 3 4 Ȑ GET /post/example/comments/ 200 OK application/json 1

  21. { "type": "comment", "id": "1", "title": "FIRST!", "author": "http://example.com/post/1/comment/1/author" }

  22. 6 requests (possibly connections)

  23. 14 requests (possibly connections)

  24. L E T ’ S M U LT I P

    L E X !
  25. M U LT I P L E X E D

    Ȑ
  26. M U LT I P L E X E D

    Ȑ GET /post/example/comments/3
  27. M U LT I P L E X E D

    Ȑ GET /post/example/comments/3 GET /post/example/comments/1
  28. M U LT I P L E X E D

    Ȑ GET /post/example/comments/3 GET /post/example/comments/1 GET /post/example/comments/2
  29. M U LT I P L E X E D

    Ȑ GET /post/example/comments/3 GET /post/example/comments/1 GET /post/example/comments/2 200 OK application/json
  30. M U LT I P L E X E D

    Ȑ GET /post/example/comments/3 GET /post/example/comments/1 GET /post/example/comments/2 200 OK application/json 200 OK application/json
  31. M U LT I P L E X E D

    Ȑ GET /post/example/comments/3 GET /post/example/comments/1 GET /post/example/comments/2 200 OK application/json 200 OK application/json 200 OK application/json
  32. S T I L L M A K I N

    G L O T S O F R E Q U E S T S
  33. D E P E N D E N C I

    E S
  34. { "type": "post", "id": "1", "title": "JSON API paints my

    bikeshed!", "tags": ["json", "api", "relationships"], "author": { "type": "author", "id": 1, "name": "Davey Shafik", "twitter": "@dshafik", "gravatar": "fee39f0c0ffb29d9ac21607ed188be6b" }, "comments": [ { "type": "comment", "id": "1", "content": "FIRST!", "author": { "type": "author", "id": 2, "name": "Jill Random", "gravatar": "706b16b2fb732ab6079a10fea61d078b" } }, { "type": "comment", "id": "2", "content": "second", "author": { "type": "author", "id": 3, "name": "John Person", "gravatar": "8e2279479945ca4778eb3cb8d88cda58" } }, { "type": "comment", "id": "3", "content": "third", "author": { "type": "author", "id": 1, "name": "Davey Shafik", "twitter": "@dshafik", "gravatar": "fee39f0c0ffb29d9ac21607ed188be6b" } }, { "type": "comment", "id": "4", "content": "fourth", "author": { "type": "author", "id": 2, "name": "Jill Random", "gravatar": "706b16b2fb732ab6079a10fea61d078b" } } ] }
  35. { "type": "post", "id": "1", "title": "JSON API paints my

    bikeshed!", "tags": ["json", "api", "relationships"], "author": { "type": "author", "id": 1, "name": "Davey Shafik", "twitter": "@dshafik", "gravatar": "fee39f0c0ffb29d9ac21607ed188be6b" }, "comments": [ { "type": "comment", "id": "1",
  36. { "type": "post", "id": "1", "title": "JSON API paints my

    bikeshed!", "tags": ["json", "api", "relationships"], "author": { "type": "author", "id": 1, "name": "Davey Shafik", "twitter": "@dshafik", "gravatar": "fee39f0c0ffb29d9ac21607ed188be6b" }, "comments": [ { "type": "comment", "id": "1",
  37. "gravatar": "fee39f0c0ffb29d9ac21607ed188be6b" }, "comments": [ { "type": "comment", "id": "1",

    "content": "FIRST!", "author": { "type": "author", "id": 2, "name": "Jill Random", "gravatar": "706b16b2fb732ab6079a10fea61d078b" } }, { "type": "comment", "id": "2",
  38. } }, { "type": "comment", "id": "3", "content": "third", "author":

    { "type": "author", "id": 1, "name": "Davey Shafik", "twitter": "@dshafik", "gravatar": "fee39f0c0ffb29d9ac21607ed188be6b" } }, { "type": "comment", "id": "4",
  39. } }, { "type": "comment", "id": "3", "content": "third", "author":

    { "type": "author", "id": 1, "name": "Davey Shafik", "twitter": "@dshafik", "gravatar": "fee39f0c0ffb29d9ac21607ed188be6b" } }, { "type": "comment", "id": "4",
  40. } }, { "type": "comment", "id": "4", "content": "fourth", "author":

    { "type": "author", "id": 2, "name": "Jill Random", "gravatar": "706b16b2fb732ab6079a10fea61d078b" } } ] }
  41. S E R V E R P US H CC-BY:

    Steven Depolo
  42. Ȑ

  43. Ȑ

  44. Ȑ GET /post/1

  45. Ȑ GET /post/1 200 OK application/json

  46. GET /post/1/comments Ȑ GET /post/1 200 OK application/json

  47. GET /post/1/comments GET /post/1/comment/1 Ȑ GET /post/1 200 OK application/json

  48. Ȑ GET /post/1/comments GET /post/1/comment/1

  49. Ȑ GET /post/1/comment/2 GET /post/1/comment/3 GET /post/1/comment/4 GET /post/1/comments GET

    /post/1/comment/1
  50. Ȑ GET /post/1/comment/2 GET /post/1/comment/3 GET /post/1/comment/4 GET /post/1/comment/1/author GET

    /post/1/comment/2/author GET /post/1/comment/3/author GET /post/1/comment/4/author GET /post/1/comments GET /post/1/comment/1
  51. Ȑ GET /post/1/comment/2 GET /post/1/comment/3 GET /post/1/comment/4 GET /post/1/comment/1/author GET

    /post/1/comment/2/author GET /post/1/comment/3/author GET /post/1/comment/4/author GET /post/1/comment/1/author/avatar.png GET /post/1/comment/2/author/avatar.png GET /post/1/comment/3/author/avatar.png GET /post/1/comment/4/author/avatar.png GET /post/1/comments GET /post/1/comment/1
  52. D E P E N D E N C I

    E S CC-BY-SA 2.0: David Gamez
  53. A P I R E Q U E ST Ɇ

    ɂ Ȑ
  54. A P I R E Q U E ST Ɇ

    ɂ Ȑ
  55. A P I R E Q U E ST GET

    /post/1 Ɇ ɂ Ȑ
  56. A P I R E Q U E ST GET

    /post/1 200 OK application/json Ɇ ɂ Ȑ
  57. S U B - R E S O U R

    C E P US H W I T H D E P E N D E N C I E S Ɇ Ȑ GET /post/1 200 OK application/json
  58. S U B - R E S O U R

    C E P US H W I T H D E P E N D E N C I E S Ɇ Ȑ GET /post/1 200 OK application/json /post/1/comments /author/1
  59. S U B - R E S O U R

    C E P US H W I T H D E P E N D E N C I E S Ɇ Ȑ GET /post/1 200 OK application/json /post/1/comments /author/1 /comment/1 /comment/…
  60. S U B - R E S O U R

    C E P US H W I T H D E P E N D E N C I E S Ɇ Ȑ GET /post/1 200 OK application/json /post/1/comments /author/1 /comment/1 /images/author/1 /comment/…
  61. S U B - R E S O U R

    C E P US H W I T H D E P E N D E N C I E S Ɇ Ȑ GET /post/1 200 OK application/json /post/1/comments /author/1 /comment/1 /author/2 /images/author/1 /comment/…
  62. S U B - R E S O U R

    C E P US H W I T H D E P E N D E N C I E S Ɇ Ȑ GET /post/1 200 OK application/json /post/1/comments /author/1 /comment/1 /author/2 /images/author/1 /comment/… /author/…
  63. CO D E

  64. $numRequests = 378;

  65. None
  66. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i); curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } H T T P/ 1 . 1 : SY N C H R O N O US
  67. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i); curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } H T T P/ 1 . 1 : SY N C H R O N O US
  68. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i); curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } H T T P/ 1 . 1 : SY N C H R O N O US
  69. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i); curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } H T T P/ 1 . 1 : SY N C H R O N O US
  70. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i); curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } H T T P/ 1 . 1 : SY N C H R O N O US
  71. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i); curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } H T T P/ 1 . 1 : SY N C H R O N O US
  72. 4 7 . 6 7 
 s e co n

    d s CC-BY: Hernán Piñera
  73. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i);
 curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } H T T P/ 2 : SY N C H R O N O US
  74. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i);
 curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } $opts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2; H T T P/ 2 : SY N C H R O N O US
  75. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i);
 curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } H T T P/ 2 : SY N C H R O N O US
  76. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i);
 curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } $opts[CURLOPT_HTTP_VERSION] = HTTP_VERSION_2_0; H T T P/ 2 : SY N C H R O N O US
  77. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i);
 curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } H T T P/ 2 : SY N C H R O N O US
  78. $url = 'https://http2.akamai.com/demo/tile-%d.png'; for ($i = 0; $i <= $numRequests;

    $i++) { $ch = curl_init();
 $opts[CURLOPT_URL] = sprintf($url, $i);
 curl_setopt_array($ch, $opts); curl_exec($ch); curl_close($ch); } $opts[CURLOPT_HTTP_VERSION] = HTTP_VERSION_2_0; H T T P/ 2 : SY N C H R O N O US
  79. 6 2 . 1 9 
 s e co n

    d s CC-BY-NC: Scott Beckner
  80. $mh = curl_multi_init();
 $url = 'https://http2.akamai.com/demo/tile-%d.png';
 for ($i = 0;

    $i <= $numRequests; $i++) { $handles[] = $ch = curl_init(); $opts[CURLOPT_URL] = sprintf($url, '%d');
 curl_setopt_array($ch, $opts); curl_multi_add_handle($mh, $ch); } H T T P/ 1 . 1 : CO N CU R R E N T
  81. $mh = curl_multi_init();
 $url = 'https://http2.akamai.com/demo/tile-%d.png';
 for ($i = 0;

    $i <= $numRequests; $i++) { $handles[] = $ch = curl_init(); $opts[CURLOPT_URL] = sprintf($url, '%d');
 curl_setopt_array($ch, $opts); curl_multi_add_handle($mh, $ch); } H T T P/ 1 . 1 : CO N CU R R E N T
  82. $mh = curl_multi_init();
 $url = 'https://http2.akamai.com/demo/tile-%d.png';
 for ($i = 0;

    $i <= $numRequests; $i++) { $handles[] = $ch = curl_init(); $opts[CURLOPT_URL] = sprintf($url, '%d');
 curl_setopt_array($ch, $opts); curl_multi_add_handle($mh, $ch); } H T T P/ 1 . 1 : CO N CU R R E N T
  83. $mh = curl_multi_init();
 $url = 'https://http2.akamai.com/demo/tile-%d.png';
 for ($i = 0;

    $i <= $numRequests; $i++) { $handles[] = $ch = curl_init(); $opts[CURLOPT_URL] = sprintf($url, '%d');
 curl_setopt_array($ch, $opts); curl_multi_add_handle($mh, $ch); } H T T P/ 1 . 1 : CO N CU R R E N T
  84. H T T P/ 1 . 1 : CO N

    CU R R E N T ( CO N T. ) do { $exec = curl_multi_exec($mh, $running); } while ($exec == CURLM_CALL_MULTI_PERFORM); while ($running && $exec == CURLM_OK) { $ready = curl_multi_select($mh); if ($ready != -1) { do { $exec = curl_multi_exec($mh, $running);
 } while ($exec == CURLM_CALL_MULTI_PERFORM); } }
  85. 8 . 6 6 
 s e co n d

    s
  86. $url = 'https://http2.akamai.com/demo/tile-%d.png';
 for ($i = 0; $i <= $numRequests;

    $i++) { $handles[] = $ch = curl_init(); $opts[CURLOPT_URL] = sprintf($url, '%d'); curl_setopt_array($ch, $opts); curl_multi_add_handle($mh, $ch); } H T T P/ 2 : M U LT I P L E X E D $mh = curl_multi_init();
  87. $url = 'https://http2.akamai.com/demo/tile-%d.png';
 for ($i = 0; $i <= $numRequests;

    $i++) { $handles[] = $ch = curl_init(); $opts[CURLOPT_URL] = sprintf($url, '%d'); curl_setopt_array($ch, $opts); curl_multi_add_handle($mh, $ch); } H T T P/ 2 : M U LT I P L E X E D $mh = curl_multi_init(); $opts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2;
  88. $opts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2; $url = 'https://http2.akamai.com/demo/tile-%d.png';
 for ($i = 0;

    $i <= $numRequests; $i++) { $handles[] = $ch = curl_init(); $opts[CURLOPT_URL] = sprintf($url, '%d'); curl_setopt_array($ch, $opts); curl_multi_add_handle($mh, $ch); } $mh = curl_multi_init(); curl_multi_setopt(
 $mh, 
 CURLMOPT_PIPELINING, 
 CURLPIPE_MULTIPLEX
 );
  89. $opts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2; $url = 'https://http2.akamai.com/demo/tile-%d.png';
 for ($i = 0;

    $i <= $numRequests; $i++) { $handles[] = $ch = curl_init(); $opts[CURLOPT_URL] = sprintf($url, '%d'); curl_setopt_array($ch, $opts); curl_multi_add_handle($mh, $ch); } $mh = curl_multi_init(); curl_multi_setopt(
 $mh, 
 CURLMOPT_PIPELINING, 
 CURLPIPE_MULTIPLEX
 );
  90. H T T P/ 2 : M U LT I

    P L E X E D ( CO N T. ) do { $exec = curl_multi_exec($mh, $running); } while ($exec == CURLM_CALL_MULTI_PERFORM); while ($running && $exec == CURLM_OK) { $ready = curl_multi_select($mh); if ($ready != -1) { do { $exec = curl_multi_exec($mh, $running);
 } while ($exec == CURLM_CALL_MULTI_PERFORM); } }
  91. 2 . 1 4 
 s e co n d

    s CC-BY: motoracereports
  92. H A N D L I N G S E

    R V E R P US H
  93. $transfers = 1; $cb = function( $parent, $pushed, $headers )

    use (&$transfers) { $transfers++; // increment to keep track of return CURL_PUSH_OK; }; $mh = curl_multi_init(); curl_multi_setopt($mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, $cb); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://localhost:8080/index.html"); curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_multi_add_handle($mh, $ch); $active = null; do { $status = curl_multi_exec($mh, $active);
  94. $transfers = 1; $cb = function( $parent, $pushed, $headers )

    use (&$transfers) { $transfers++; // increment to keep track of return CURL_PUSH_OK; }; $mh = curl_multi_init();
  95. $transfers = 1; $cb = function( $parent, $pushed, $headers )

    use (&$transfers) { $transfers++; // increment to keep track of return CURL_PUSH_OK; }; $mh = curl_multi_init();
  96. $transfers = 1; $cb = function( $parent, $pushed, $headers )

    use (&$transfers) { $transfers++; // increment to keep track of return CURL_PUSH_OK; }; $mh = curl_multi_init();
  97. $transfers = 1; $cb = function( $parent, $pushed, $headers )

    use (&$transfers) { $transfers++; // increment to keep track of return CURL_PUSH_OK; }; $mh = curl_multi_init();
  98. $transfers = 1; $cb = function( $parent, $pushed, $headers )

    use (&$transfers) { $transfers++; // increment to keep track of return CURL_PUSH_OK; }; $mh = curl_multi_init();
  99. $transfers = 1; $cb = function( $parent, $pushed, $headers )

    use (&$transfers) { $transfers++; // increment to keep track of return CURL_PUSH_OK; }; $mh = curl_multi_init();
  100. $transfers = 1; $cb = function( $parent, $pushed, $headers )

    use (&$transfers) { $transfers++; // increment to keep track of return CURL_PUSH_OK; }; $mh = curl_multi_init();
  101. $mh = curl_multi_init(); curl_multi_setopt($mh, 
 CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX
 ); curl_multi_setopt($mh, 


    CURLMOPT_PUSHFUNCTION, $cb
 ); $ch = curl_init(); curl_setopt($ch, 
 CURLOPT_URL, "https://localhost/"
 );
  102. $mh = curl_multi_init(); curl_multi_setopt($mh, 
 CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX
 ); curl_multi_setopt($mh, 


    CURLMOPT_PUSHFUNCTION, $cb
 ); $ch = curl_init(); curl_setopt($ch, 
 CURLOPT_URL, "https://localhost/"
 );
  103. $mh = curl_multi_init(); curl_multi_setopt($mh, 
 CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX
 ); curl_multi_setopt($mh, 


    CURLMOPT_PUSHFUNCTION, $cb
 ); $ch = curl_init(); curl_setopt($ch, 
 CURLOPT_URL, "https://localhost/"
 );
  104. $mh = curl_multi_init(); curl_multi_setopt($mh, 
 CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX
 ); curl_multi_setopt($mh, 


    CURLMOPT_PUSHFUNCTION, $cb
 ); $ch = curl_init(); curl_setopt($ch, 
 CURLOPT_URL, "https://localhost/"
 );
  105. $mh = curl_multi_init(); curl_multi_setopt($mh, 
 CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX
 ); curl_multi_setopt($mh, 


    CURLMOPT_PUSHFUNCTION, $cb
 ); $ch = curl_init(); curl_setopt($ch, 
 CURLOPT_URL, "https://localhost/"
 );
  106. curl_setopt($ch, 
 CURLOPT_URL, "https://localhost/"
 ); curl_setopt($ch, 
 CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2
 );

    curl_setopt($ch, 
 CURLOPT_RETURNTRANSFER, 1
 ); curl_multi_add_handle($mh, $ch); $active = null; do {
  107. curl_setopt($ch, 
 CURLOPT_URL, "https://localhost/"
 ); curl_setopt($ch, 
 CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2
 );

    curl_setopt($ch, 
 CURLOPT_RETURNTRANSFER, 1
 ); curl_multi_add_handle($mh, $ch); $active = null; do {
  108. curl_setopt($ch, 
 CURLOPT_URL, "https://localhost/"
 ); curl_setopt($ch, 
 CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2
 );

    curl_setopt($ch, 
 CURLOPT_RETURNTRANSFER, 1
 ); curl_multi_add_handle($mh, $ch); $active = null; do {
  109. do { $status = curl_multi_exec($mh, $active); do { $info =

    curl_multi_info_read($mh); if (false !== $info && 
 $info['msg'] == CURLMSG_DONE) { $handle = $info['handle']; if ($handle !== null) { $transfers--; // remaining requests $out = curl_multi_getcontent(
 $info['handle']
 ); // Response body
  110. do { $status = curl_multi_exec($mh, $active); do { $info =

    curl_multi_info_read($mh); if (false !== $info && 
 $info['msg'] == CURLMSG_DONE) { $handle = $info['handle']; if ($handle !== null) { $transfers--; // remaining requests $out = curl_multi_getcontent(
 $info['handle']
 ); // Response body
  111. do { $status = curl_multi_exec($mh, $active); do { $info =

    curl_multi_info_read($mh); if (false !== $info && 
 $info['msg'] == CURLMSG_DONE) { $handle = $info['handle']; if ($handle !== null) { $transfers--; // remaining requests $out = curl_multi_getcontent(
 $info['handle']
 ); // Response body
  112. do { $status = curl_multi_exec($mh, $active); do { $info =

    curl_multi_info_read($mh); if (false !== $info && 
 $info['msg'] == CURLMSG_DONE) { $handle = $info['handle']; if ($handle !== null) { $transfers--; // remaining requests $out = curl_multi_getcontent(
 $info['handle']
 ); // Response body
  113. do { $status = curl_multi_exec($mh, $active); do { $info =

    curl_multi_info_read($mh); if (false !== $info && 
 $info['msg'] == CURLMSG_DONE) { $handle = $info['handle']; if ($handle !== null) { $transfers--; // remaining requests $out = curl_multi_getcontent(
 $info['handle']
 ); // Response body
  114. do { $status = curl_multi_exec($mh, $active); do { $info =

    curl_multi_info_read($mh); if (false !== $info && 
 $info['msg'] == CURLMSG_DONE) { $handle = $info['handle']; if ($handle !== null) { $transfers--; // remaining requests $out = curl_multi_getcontent(
 $info['handle']
 ); // Response body
  115. $handle = $info['handle']; if ($handle !== null) { $transfers--; //

    remaining requests $out = curl_multi_getcontent(
 $info['handle']
 ); // Response body curl_multi_remove_handle($mh, $handle); curl_close($handle); } } } while ($info); } while ($transfers); curl_multi_close($mh);
  116. $handle = $info['handle']; if ($handle !== null) { $transfers--; //

    remaining requests $out = curl_multi_getcontent(
 $info['handle']
 ); // Response body curl_multi_remove_handle($mh, $handle); curl_close($handle); } } } while ($info); } while ($transfers); curl_multi_close($mh);
  117. $handle = $info['handle']; if ($handle !== null) { $transfers--; //

    remaining requests $out = curl_multi_getcontent(
 $info['handle']
 ); // Response body curl_multi_remove_handle($mh, $handle); curl_close($handle); } } } while ($info); } while ($transfers); curl_multi_close($mh);
  118. $handle = $info['handle']; if ($handle !== null) { $transfers--; //

    remaining requests $out = curl_multi_getcontent(
 $info['handle']
 ); // Response body curl_multi_remove_handle($mh, $handle); curl_close($handle); } } } while ($info); } while ($transfers); curl_multi_close($mh);
  119. B LO G A P I + S E R

    V E R P U S H
  120. class H2PushCache { static $cache = []; static $pushHandles =

    []; static function addPushHandle($headers, $handle) { foreach ($headers as $header) { if (strpos($header, ':path:') === 0) { $path = substr($header, 6); $url = curl_getinfo($handle)['url']; $url = str_replace( parse_url($url, PHP_URL_PATH), $path, $url ); self::$pushHandles[$url] = $handle; } } } static function add($handle) {
  121. class H2PushCache { static $cache = []; static $pushHandles =

    []; static function addPushHandle($headers, $handle) { foreach ($headers as $header) { if (strpos($header, ':path:') === 0) { $path = substr($header, 6); $url = curl_getinfo($handle)['url']; $url = str_replace( parse_url($url, PHP_URL_PATH), $path,
  122. class H2PushCache { static $cache = []; static $pushHandles =

    []; static function addPushHandle($headers, $handle) { foreach ($headers as $header) { if (strpos($header, ':path:') === 0) { $path = substr($header, 6); $url = curl_getinfo($handle)['url']; $url = str_replace( parse_url($url, PHP_URL_PATH), $path,
  123. class H2PushCache { static $cache = []; static $pushHandles =

    []; static function addPushHandle($headers, $handle) { foreach ($headers as $header) { if (strpos($header, ':path:') === 0) { $path = substr($header, 6); $url = curl_getinfo($handle)['url']; $url = str_replace( parse_url($url, PHP_URL_PATH), $path,
  124. class H2PushCache { static $cache = []; static $pushHandles =

    []; static function addPushHandle($headers, $handle) { foreach ($headers as $header) { if (strpos($header, ':path:') === 0) { $path = substr($header, 6); $url = curl_getinfo($handle)['url']; $url = str_replace( parse_url($url, PHP_URL_PATH), $path,
  125. static function addPushHandle($headers, $handle) { foreach ($headers as $header) {

    if (strpos($header, ':path:') === 0) { $path = substr($header, 6); $url = curl_getinfo($handle)['url']; $url = str_replace( parse_url($url, PHP_URL_PATH), $path, $url ); self::$pushHandles[$url] = $handle; } }
  126. static function addPushHandle($headers, $handle) { foreach ($headers as $header) {

    if (strpos($header, ':path:') === 0) { $path = substr($header, 6); $url = curl_getinfo($handle)['url']; $url = str_replace( parse_url($url, PHP_URL_PATH), $path, $url ); self::$pushHandles[$url] = $handle; } }
  127. static function add($handle) { $found = false; foreach (self::$pushHandles as

    $url => $h) { if ($handle == $h) { $found = $url; } } if (!$found) { $found = curl_getinfo($handle)['url']; }
  128. static function add($handle) { $found = false; foreach (self::$pushHandles as

    $url => $h) { if ($handle == $h) { $found = $url; } } if (!$found) { $found = curl_getinfo($handle)['url']; }
  129. static function add($handle) { $found = false; foreach (self::$pushHandles as

    $url => $h) { if ($handle == $h) { $found = $url; } } if (!$found) { $found = curl_getinfo($handle)['url']; }
  130. self::$cache[$found] = 
 curl_multi_getcontent($handle); } static function exists($url) { if

    (isset(self::$cache[$url])) { return true; } return false; }
  131. self::$cache[$found] = 
 curl_multi_getcontent($handle); } static function exists($url) { if

    (isset(self::$cache[$url])) { return true; } return false; }
  132. { if (isset(self::$cache[$url])) { return true; } return false; }

    static function get($url) { return self::$cache[$url]; } }
  133. function get_request($url) { if (H2PushCache::exists($url)) { return H2PushCache::get($url); } $transfers

    = 1; $cb = function ($parent, $pushed, $headers) use (&$transfers) { $transfers++; // increment to keep track of the number of concurrent requests H2PushCache::addPushHandle($headers, $pushed); return CURL_PUSH_OK; }; $mh = curl_multi_init(); curl_multi_setopt($mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX ); curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, $cb ); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url ); curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2 ); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1
  134. function get_request($url) { if (H2PushCache::exists($url)) { return H2PushCache::get($url); } $transfers

    = 1; $cb = function ($parent, $pushed, $headers) use (&$transfers) { $transfers++; // increment to keep track of the number of concurrent requests H2PushCache::addPushHandle($headers,
  135. function get_request($url) { if (H2PushCache::exists($url)) { return H2PushCache::get($url); } $transfers

    = 1; $cb = function ($parent, $pushed, $headers) use (&$transfers) { $transfers++; // increment to keep track of the number of concurrent requests H2PushCache::addPushHandle($headers,
  136. function get_request($url) { if (H2PushCache::exists($url)) { return H2PushCache::get($url); } $transfers

    = 1; $cb = function ($parent, $pushed, $headers) use (&$transfers) { $transfers++; // increment to keep track of the number of concurrent requests H2PushCache::addPushHandle($headers,
  137. function get_request($url) { if (H2PushCache::exists($url)) { return H2PushCache::get($url); } $transfers

    = 1; $cb = function ($parent, $pushed, $headers) use (&$transfers) { $transfers++; // increment to keep track of the number of concurrent requests H2PushCache::addPushHandle($headers,
  138. $transfers = 1; $cb = function ($parent, $pushed, $headers) use

    (&$transfers) { $transfers++; H2PushCache::addPushHandle($headers, $pushed); return CURL_PUSH_OK; }; $mh = curl_multi_init(); curl_multi_setopt($mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX
  139. $transfers = 1; $cb = function ($parent, $pushed, $headers) use

    (&$transfers) { $transfers++; H2PushCache::addPushHandle($headers, $pushed); return CURL_PUSH_OK; }; $mh = curl_multi_init(); curl_multi_setopt($mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX
  140. do { $status = curl_multi_exec($mh, $active); do { $info =

    curl_multi_info_read($mh); if (false !== $info && $info['msg'] == CURLMSG_DONE) { $handle = $info['handle']; if ($handle !== null) { $transfers--; // decrement remaining H2PushCache::add($handle); curl_multi_remove_handle($mh,
  141. do { $status = curl_multi_exec($mh, $active); do { $info =

    curl_multi_info_read($mh); if (false !== $info && $info['msg'] == CURLMSG_DONE) { $handle = $info['handle']; if ($handle !== null) { $transfers--; // decrement remaining H2PushCache::add($handle); curl_multi_remove_handle($mh,
  142. $transfers--; // decrement remaining H2PushCache::add($handle); curl_multi_remove_handle($mh, $handle); curl_close($handle); } }

    } while ($info); } while ($transfers); curl_multi_close($mh); return H2PushCache::get($url); }
  143. $transfers--; // decrement remaining H2PushCache::add($handle); curl_multi_remove_handle($mh, $handle); curl_close($handle); } }

    } while ($info); } while ($transfers); curl_multi_close($mh); return H2PushCache::get($url); }
  144. $url = 'https://example/post/1'; $response = get_request($url); $post = json_decode($response); $response

    = get_request($post->comments); $comments = json_decode($reponse); $response = get_request($post->author); $author = json_decode($response);
  145. $url = 'https://example/post/1'; $response = get_request($url); $post = json_decode($response); $response

    = get_request($post->comments); $comments = json_decode($reponse); $response = get_request($post->author); $author = json_decode($response);
  146. $url = 'https://example/post/1'; $response = get_request($url); $post = json_decode($response); $response

    = get_request($post->comments); $comments = json_decode($reponse); $response = get_request($post->author); $author = json_decode($response);
  147. $url = 'https://example/post/1'; $response = get_request($url); $post = json_decode($response); $response

    = get_request($post->comments); $comments = json_decode($reponse); $response = get_request($post->author); $author = json_decode($response);
  148. $url = 'https://example/post/1'; $response = get_request($url); $post = json_decode($response); $response

    = get_request($post->comments); $comments = json_decode($reponse); $response = get_request($post->author); $author = json_decode($response);
  149. $url = 'https://example/post/1'; $response = get_request($url); $post = json_decode($response); $response

    = get_request($post->comments); $comments = json_decode($reponse); $response = get_request($post->author); $author = json_decode($response);
  150. T H A N K S !