• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2015 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "asio_client_session_impl.h"
26 
27 #include <iostream>
28 
29 #include "asio_client_stream.h"
30 #include "asio_client_request_impl.h"
31 #include "asio_client_response_impl.h"
32 #include "asio_common.h"
33 #include "template.h"
34 #include "util.h"
35 #include "http2.h"
36 
37 namespace nghttp2 {
38 namespace asio_http2 {
39 namespace client {
40 
session_impl(boost::asio::io_service & io_service,const boost::posix_time::time_duration & connect_timeout)41 session_impl::session_impl(
42     boost::asio::io_service &io_service,
43     const boost::posix_time::time_duration &connect_timeout)
44     : wblen_(0),
45       io_service_(io_service),
46       resolver_(io_service),
47       deadline_(io_service),
48       connect_timeout_(connect_timeout),
49       read_timeout_(boost::posix_time::seconds(60)),
50       ping_(io_service),
51       session_(nullptr),
52       data_pending_(nullptr),
53       data_pendinglen_(0),
54       writing_(false),
55       inside_callback_(false),
56       stopped_(false) {}
57 
~session_impl()58 session_impl::~session_impl() {
59   // finish up all active stream
60   for (auto &p : streams_) {
61     auto &strm = p.second;
62     auto &req = strm->request().impl();
63     req.call_on_close(NGHTTP2_INTERNAL_ERROR);
64   }
65 
66   nghttp2_session_del(session_);
67 }
68 
start_resolve(const std::string & host,const std::string & service)69 void session_impl::start_resolve(const std::string &host,
70                                  const std::string &service) {
71   deadline_.expires_from_now(connect_timeout_);
72 
73   auto self = shared_from_this();
74 
75   resolver_.async_resolve({host, service},
76                           [self](const boost::system::error_code &ec,
77                                  tcp::resolver::iterator endpoint_it) {
78                             if (ec) {
79                               self->not_connected(ec);
80                               return;
81                             }
82 
83                             self->start_connect(endpoint_it);
84                           });
85 
86   deadline_.async_wait(std::bind(&session_impl::handle_deadline, self));
87 }
88 
handle_deadline()89 void session_impl::handle_deadline() {
90   if (stopped_) {
91     return;
92   }
93 
94   if (deadline_.expires_at() <=
95       boost::asio::deadline_timer::traits_type::now()) {
96     call_error_cb(boost::asio::error::timed_out);
97     stop();
98     deadline_.expires_at(boost::posix_time::pos_infin);
99     return;
100   }
101 
102   deadline_.async_wait(
103       std::bind(&session_impl::handle_deadline, this->shared_from_this()));
104 }
105 
handle_ping2(const boost::system::error_code & ec,int)106 void handle_ping2(const boost::system::error_code &ec, int) {}
107 
start_ping()108 void session_impl::start_ping() {
109   ping_.expires_from_now(boost::posix_time::seconds(30));
110   ping_.async_wait(std::bind(&session_impl::handle_ping, shared_from_this(),
111                              std::placeholders::_1));
112 }
113 
handle_ping(const boost::system::error_code & ec)114 void session_impl::handle_ping(const boost::system::error_code &ec) {
115   if (stopped_ || ec == boost::asio::error::operation_aborted ||
116       !streams_.empty()) {
117     return;
118   }
119 
120   nghttp2_submit_ping(session_, NGHTTP2_FLAG_NONE, nullptr);
121 
122   signal_write();
123 
124   start_ping();
125 }
126 
connected(tcp::resolver::iterator endpoint_it)127 void session_impl::connected(tcp::resolver::iterator endpoint_it) {
128   if (!setup_session()) {
129     return;
130   }
131 
132   socket().set_option(boost::asio::ip::tcp::no_delay(true));
133 
134   do_write();
135   do_read();
136 
137   start_ping();
138 
139   auto &connect_cb = on_connect();
140   if (connect_cb) {
141     connect_cb(endpoint_it);
142   }
143 }
144 
not_connected(const boost::system::error_code & ec)145 void session_impl::not_connected(const boost::system::error_code &ec) {
146   call_error_cb(ec);
147   stop();
148 }
149 
on_connect(connect_cb cb)150 void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); }
151 
on_error(error_cb cb)152 void session_impl::on_error(error_cb cb) { error_cb_ = std::move(cb); }
153 
on_connect() const154 const connect_cb &session_impl::on_connect() const { return connect_cb_; }
155 
on_error() const156 const error_cb &session_impl::on_error() const { return error_cb_; }
157 
call_error_cb(const boost::system::error_code & ec)158 void session_impl::call_error_cb(const boost::system::error_code &ec) {
159   if (stopped_) {
160     return;
161   }
162   auto &error_cb = on_error();
163   if (!error_cb) {
164     return;
165   }
166   error_cb(ec);
167 }
168 
169 namespace {
on_begin_headers_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)170 int on_begin_headers_callback(nghttp2_session *session,
171                               const nghttp2_frame *frame, void *user_data) {
172   if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
173     return 0;
174   }
175 
176   auto sess = static_cast<session_impl *>(user_data);
177   sess->create_push_stream(frame->push_promise.promised_stream_id);
178 
179   return 0;
180 }
181 } // namespace
182 
183 namespace {
on_header_callback(nghttp2_session * session,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen,uint8_t flags,void * user_data)184 int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
185                        const uint8_t *name, size_t namelen,
186                        const uint8_t *value, size_t valuelen, uint8_t flags,
187                        void *user_data) {
188   auto sess = static_cast<session_impl *>(user_data);
189   stream *strm;
190 
191   switch (frame->hd.type) {
192   case NGHTTP2_HEADERS: {
193     strm = sess->find_stream(frame->hd.stream_id);
194     if (!strm) {
195       return 0;
196     }
197 
198     // ignore trailers
199     if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
200         !strm->expect_final_response()) {
201       return 0;
202     }
203 
204     auto token = http2::lookup_token(name, namelen);
205 
206     auto &res = strm->response().impl();
207     if (token == http2::HD__STATUS) {
208       res.status_code(util::parse_uint(value, valuelen));
209     } else {
210       if (res.header_buffer_size() + namelen + valuelen > 64_k) {
211         nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
212                                   frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR);
213         break;
214       }
215       res.update_header_buffer_size(namelen + valuelen);
216 
217       if (token == http2::HD_CONTENT_LENGTH) {
218         res.content_length(util::parse_uint(value, valuelen));
219       }
220 
221       res.header().emplace(
222           std::string(name, name + namelen),
223           header_value{std::string(value, value + valuelen),
224                        (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
225     }
226     break;
227   }
228   case NGHTTP2_PUSH_PROMISE: {
229     strm = sess->find_stream(frame->push_promise.promised_stream_id);
230     if (!strm) {
231       return 0;
232     }
233 
234     auto &req = strm->request().impl();
235     auto &uri = req.uri();
236 
237     switch (http2::lookup_token(name, namelen)) {
238     case http2::HD__METHOD:
239       req.method(std::string(value, value + valuelen));
240       break;
241     case http2::HD__SCHEME:
242       uri.scheme.assign(value, value + valuelen);
243       break;
244     case http2::HD__PATH:
245       split_path(uri, value, value + valuelen);
246       break;
247     case http2::HD__AUTHORITY:
248       uri.host.assign(value, value + valuelen);
249       break;
250     case http2::HD_HOST:
251       if (uri.host.empty()) {
252         uri.host.assign(value, value + valuelen);
253       }
254     // fall through
255     default:
256       if (req.header_buffer_size() + namelen + valuelen > 64_k) {
257         nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
258                                   frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR);
259         break;
260       }
261       req.update_header_buffer_size(namelen + valuelen);
262 
263       req.header().emplace(
264           std::string(name, name + namelen),
265           header_value{std::string(value, value + valuelen),
266                        (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
267     }
268 
269     break;
270   }
271   default:
272     return 0;
273   }
274 
275   return 0;
276 }
277 } // namespace
278 
279 namespace {
on_frame_recv_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)280 int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
281                            void *user_data) {
282   auto sess = static_cast<session_impl *>(user_data);
283   auto strm = sess->find_stream(frame->hd.stream_id);
284 
285   switch (frame->hd.type) {
286   case NGHTTP2_DATA: {
287     if (!strm) {
288       return 0;
289     }
290     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
291       strm->response().impl().call_on_data(nullptr, 0);
292     }
293     break;
294   }
295   case NGHTTP2_HEADERS: {
296     if (!strm) {
297       return 0;
298     }
299 
300     // ignore trailers
301     if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
302         !strm->expect_final_response()) {
303       return 0;
304     }
305 
306     if (strm->expect_final_response()) {
307       // wait for final response
308       return 0;
309     }
310 
311     auto &req = strm->request().impl();
312     req.call_on_response(strm->response());
313     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
314       strm->response().impl().call_on_data(nullptr, 0);
315     }
316     break;
317   }
318   case NGHTTP2_PUSH_PROMISE: {
319     if (!strm) {
320       return 0;
321     }
322 
323     auto push_strm = sess->find_stream(frame->push_promise.promised_stream_id);
324     if (!push_strm) {
325       return 0;
326     }
327 
328     strm->request().impl().call_on_push(push_strm->request());
329 
330     break;
331   }
332   }
333   return 0;
334 }
335 } // namespace
336 
337 namespace {
on_data_chunk_recv_callback(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * user_data)338 int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
339                                 int32_t stream_id, const uint8_t *data,
340                                 size_t len, void *user_data) {
341   auto sess = static_cast<session_impl *>(user_data);
342   auto strm = sess->find_stream(stream_id);
343   if (!strm) {
344     return 0;
345   }
346 
347   auto &res = strm->response().impl();
348   res.call_on_data(data, len);
349 
350   return 0;
351 }
352 } // namespace
353 
354 namespace {
on_stream_close_callback(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * user_data)355 int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
356                              uint32_t error_code, void *user_data) {
357   auto sess = static_cast<session_impl *>(user_data);
358   auto strm = sess->pop_stream(stream_id);
359   if (!strm) {
360     return 0;
361   }
362 
363   strm->request().impl().call_on_close(error_code);
364 
365   return 0;
366 }
367 } // namespace
368 
setup_session()369 bool session_impl::setup_session() {
370   nghttp2_session_callbacks *callbacks;
371   nghttp2_session_callbacks_new(&callbacks);
372   auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
373 
374   nghttp2_session_callbacks_set_on_begin_headers_callback(
375       callbacks, on_begin_headers_callback);
376   nghttp2_session_callbacks_set_on_header_callback(callbacks,
377                                                    on_header_callback);
378   nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
379                                                        on_frame_recv_callback);
380   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
381       callbacks, on_data_chunk_recv_callback);
382   nghttp2_session_callbacks_set_on_stream_close_callback(
383       callbacks, on_stream_close_callback);
384 
385   auto rv = nghttp2_session_client_new(&session_, callbacks, this);
386   if (rv != 0) {
387     call_error_cb(make_error_code(static_cast<nghttp2_error>(rv)));
388     return false;
389   }
390 
391   const uint32_t window_size = 256_m;
392 
393   std::array<nghttp2_settings_entry, 2> iv{
394       {{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100},
395        // typically client is just a *sink* and just process data as
396        // much as possible.  Use large window size by default.
397        {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, window_size}}};
398   nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, iv.data(), iv.size());
399   // increase connection window size up to window_size
400   nghttp2_session_set_local_window_size(session_, NGHTTP2_FLAG_NONE, 0,
401                                         window_size);
402   return true;
403 }
404 
write_trailer(stream & strm,header_map h)405 int session_impl::write_trailer(stream &strm, header_map h) {
406   int rv;
407   auto nva = std::vector<nghttp2_nv>();
408   nva.reserve(h.size());
409   for (auto &hd : h) {
410     nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
411                                           hd.second.sensitive));
412   }
413 
414   rv = nghttp2_submit_trailer(session_, strm.stream_id(), nva.data(),
415                               nva.size());
416 
417   if (rv != 0) {
418     return -1;
419   }
420 
421   signal_write();
422 
423   return 0;
424 }
425 
cancel(stream & strm,uint32_t error_code)426 void session_impl::cancel(stream &strm, uint32_t error_code) {
427   if (stopped_) {
428     return;
429   }
430 
431   nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, strm.stream_id(),
432                             error_code);
433   signal_write();
434 }
435 
resume(stream & strm)436 void session_impl::resume(stream &strm) {
437   if (stopped_) {
438     return;
439   }
440 
441   nghttp2_session_resume_data(session_, strm.stream_id());
442   signal_write();
443 }
444 
find_stream(int32_t stream_id)445 stream *session_impl::find_stream(int32_t stream_id) {
446   auto it = streams_.find(stream_id);
447   if (it == std::end(streams_)) {
448     return nullptr;
449   }
450   return (*it).second.get();
451 }
452 
pop_stream(int32_t stream_id)453 std::unique_ptr<stream> session_impl::pop_stream(int32_t stream_id) {
454   auto it = streams_.find(stream_id);
455   if (it == std::end(streams_)) {
456     return nullptr;
457   }
458   auto strm = std::move((*it).second);
459   streams_.erase(it);
460   if (streams_.empty()) {
461     start_ping();
462   }
463   return strm;
464 }
465 
create_push_stream(int32_t stream_id)466 stream *session_impl::create_push_stream(int32_t stream_id) {
467   auto strm = create_stream();
468   strm->stream_id(stream_id);
469   auto p = streams_.emplace(stream_id, std::move(strm));
470   assert(p.second);
471   ping_.cancel();
472   return (*p.first).second.get();
473 }
474 
create_stream()475 std::unique_ptr<stream> session_impl::create_stream() {
476   return std::make_unique<stream>(this);
477 }
478 
submit(boost::system::error_code & ec,const std::string & method,const std::string & uri,generator_cb cb,header_map h,priority_spec prio)479 const request *session_impl::submit(boost::system::error_code &ec,
480                                     const std::string &method,
481                                     const std::string &uri, generator_cb cb,
482                                     header_map h, priority_spec prio) {
483   ec.clear();
484 
485   if (stopped_) {
486     ec = make_error_code(static_cast<nghttp2_error>(NGHTTP2_INTERNAL_ERROR));
487     return nullptr;
488   }
489 
490   http_parser_url u{};
491   // TODO Handle CONNECT method
492   if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
493     ec = make_error_code(boost::system::errc::invalid_argument);
494     return nullptr;
495   }
496 
497   if ((u.field_set & (1 << UF_SCHEMA)) == 0 ||
498       (u.field_set & (1 << UF_HOST)) == 0) {
499     ec = make_error_code(boost::system::errc::invalid_argument);
500     return nullptr;
501   }
502 
503   auto strm = create_stream();
504   auto &req = strm->request().impl();
505   auto &uref = req.uri();
506 
507   http2::copy_url_component(uref.scheme, &u, UF_SCHEMA, uri.c_str());
508   http2::copy_url_component(uref.host, &u, UF_HOST, uri.c_str());
509   http2::copy_url_component(uref.raw_path, &u, UF_PATH, uri.c_str());
510   http2::copy_url_component(uref.raw_query, &u, UF_QUERY, uri.c_str());
511 
512   if (util::ipv6_numeric_addr(uref.host.c_str())) {
513     uref.host = "[" + uref.host;
514     uref.host += ']';
515   }
516   if (u.field_set & (1 << UF_PORT)) {
517     uref.host += ':';
518     uref.host += util::utos(u.port);
519   }
520 
521   if (uref.raw_path.empty()) {
522     uref.raw_path = "/";
523   }
524 
525   uref.path = percent_decode(uref.raw_path);
526 
527   auto path = uref.raw_path;
528   if (u.field_set & (1 << UF_QUERY)) {
529     path += '?';
530     path += uref.raw_query;
531   }
532 
533   auto nva = std::vector<nghttp2_nv>();
534   nva.reserve(4 + h.size());
535   nva.push_back(http2::make_nv_ls(":method", method));
536   nva.push_back(http2::make_nv_ls(":scheme", uref.scheme));
537   nva.push_back(http2::make_nv_ls(":path", path));
538   nva.push_back(http2::make_nv_ls(":authority", uref.host));
539   for (auto &kv : h) {
540     nva.push_back(
541         http2::make_nv(kv.first, kv.second.value, kv.second.sensitive));
542   }
543 
544   req.header(std::move(h));
545 
546   nghttp2_data_provider *prdptr = nullptr;
547   nghttp2_data_provider prd;
548 
549   if (cb) {
550     strm->request().impl().on_read(std::move(cb));
551     prd.source.ptr = strm.get();
552     prd.read_callback = [](nghttp2_session *session, int32_t stream_id,
553                            uint8_t *buf, size_t length, uint32_t *data_flags,
554                            nghttp2_data_source *source,
555                            void *user_data) -> ssize_t {
556       auto strm = static_cast<stream *>(source->ptr);
557       return strm->request().impl().call_on_read(buf, length, data_flags);
558     };
559     prdptr = &prd;
560   }
561 
562   auto stream_id = nghttp2_submit_request(session_, prio.get(), nva.data(),
563                                           nva.size(), prdptr, strm.get());
564   if (stream_id < 0) {
565     ec = make_error_code(static_cast<nghttp2_error>(stream_id));
566     return nullptr;
567   }
568 
569   signal_write();
570 
571   strm->stream_id(stream_id);
572 
573   auto p = streams_.emplace(stream_id, std::move(strm));
574   assert(p.second);
575   ping_.cancel();
576   return &(*p.first).second->request();
577 }
578 
shutdown()579 void session_impl::shutdown() {
580   if (stopped_) {
581     return;
582   }
583 
584   nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
585   signal_write();
586 }
587 
io_service()588 boost::asio::io_service &session_impl::io_service() { return io_service_; }
589 
signal_write()590 void session_impl::signal_write() {
591   if (!inside_callback_) {
592     do_write();
593   }
594 }
595 
should_stop() const596 bool session_impl::should_stop() const {
597   return !writing_ && !nghttp2_session_want_read(session_) &&
598          !nghttp2_session_want_write(session_);
599 }
600 
601 namespace {
602 struct callback_guard {
callback_guardnghttp2::asio_http2::client::__anonb941e1e30811::callback_guard603   callback_guard(session_impl &sess) : sess(sess) { sess.enter_callback(); }
~callback_guardnghttp2::asio_http2::client::__anonb941e1e30811::callback_guard604   ~callback_guard() { sess.leave_callback(); }
605 
606   session_impl &sess;
607 };
608 } // namespace
609 
enter_callback()610 void session_impl::enter_callback() {
611   assert(!inside_callback_);
612   inside_callback_ = true;
613 }
614 
leave_callback()615 void session_impl::leave_callback() {
616   assert(inside_callback_);
617   inside_callback_ = false;
618 }
619 
do_read()620 void session_impl::do_read() {
621   if (stopped_) {
622     return;
623   }
624 
625   deadline_.expires_from_now(read_timeout_);
626 
627   auto self = this->shared_from_this();
628 
629   read_socket([self](const boost::system::error_code &ec,
630                      std::size_t bytes_transferred) {
631     if (ec) {
632       if (!self->should_stop()) {
633         self->call_error_cb(ec);
634       }
635       self->stop();
636       return;
637     }
638 
639     {
640       callback_guard cg(*self);
641 
642       auto rv = nghttp2_session_mem_recv(self->session_, self->rb_.data(),
643                                          bytes_transferred);
644 
645       if (rv != static_cast<ssize_t>(bytes_transferred)) {
646         self->call_error_cb(make_error_code(
647             static_cast<nghttp2_error>(rv < 0 ? rv : NGHTTP2_ERR_PROTO)));
648         self->stop();
649         return;
650       }
651     }
652 
653     self->do_write();
654 
655     if (self->should_stop()) {
656       self->stop();
657       return;
658     }
659 
660     self->do_read();
661   });
662 }
663 
do_write()664 void session_impl::do_write() {
665   if (stopped_) {
666     return;
667   }
668 
669   if (writing_) {
670     return;
671   }
672 
673   if (data_pending_) {
674     std::copy_n(data_pending_, data_pendinglen_, std::begin(wb_) + wblen_);
675 
676     wblen_ += data_pendinglen_;
677 
678     data_pending_ = nullptr;
679     data_pendinglen_ = 0;
680   }
681 
682   {
683     callback_guard cg(*this);
684 
685     for (;;) {
686       const uint8_t *data;
687       auto n = nghttp2_session_mem_send(session_, &data);
688       if (n < 0) {
689         call_error_cb(make_error_code(static_cast<nghttp2_error>(n)));
690         stop();
691         return;
692       }
693 
694       if (n == 0) {
695         break;
696       }
697 
698       if (wblen_ + n > wb_.size()) {
699         data_pending_ = data;
700         data_pendinglen_ = n;
701 
702         break;
703       }
704 
705       std::copy_n(data, n, std::begin(wb_) + wblen_);
706 
707       wblen_ += n;
708     }
709   }
710 
711   if (wblen_ == 0) {
712     if (should_stop()) {
713       stop();
714     }
715     return;
716   }
717 
718   writing_ = true;
719 
720   // Reset read deadline here, because normally client is sending
721   // something, it does not expect timeout while doing it.
722   deadline_.expires_from_now(read_timeout_);
723 
724   auto self = this->shared_from_this();
725 
726   write_socket([self](const boost::system::error_code &ec, std::size_t n) {
727     if (ec) {
728       self->call_error_cb(ec);
729       self->stop();
730       return;
731     }
732 
733     self->wblen_ = 0;
734     self->writing_ = false;
735 
736     self->do_write();
737   });
738 }
739 
stop()740 void session_impl::stop() {
741   if (stopped_) {
742     return;
743   }
744 
745   shutdown_socket();
746   deadline_.cancel();
747   ping_.cancel();
748   stopped_ = true;
749 }
750 
stopped() const751 bool session_impl::stopped() const { return stopped_; }
752 
read_timeout(const boost::posix_time::time_duration & t)753 void session_impl::read_timeout(const boost::posix_time::time_duration &t) {
754   read_timeout_ = t;
755 }
756 
757 } // namespace client
758 } // namespace asio_http2
759 } // namespace nghttp2
760