shrpx_http2_session.cc 40.5 KB
Newer Older
1
/*
2
 * nghttp2 - HTTP/2 C Library
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * Copyright (c) 2012 Tatsuhiro Tsujikawa
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
25
#include "shrpx_http2_session.h"
26

27
#include <netinet/tcp.h>
28 29 30 31 32 33 34 35 36 37 38
#include <unistd.h>
#include <vector>

#include <openssl/err.h>

#include <event2/bufferevent_ssl.h>

#include "shrpx_upstream.h"
#include "shrpx_downstream.h"
#include "shrpx_config.h"
#include "shrpx_error.h"
39
#include "shrpx_http2_downstream_connection.h"
40
#include "shrpx_client_handler.h"
41
#include "shrpx_ssl.h"
42
#include "shrpx_http.h"
43
#include "http2.h"
44
#include "util.h"
45
#include "base64.h"
46

Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
47
using namespace nghttp2;
48 49 50

namespace shrpx {

51
Http2Session::Http2Session(event_base *evbase, SSL_CTX *ssl_ctx)
52 53
  : evbase_(evbase),
    ssl_ctx_(ssl_ctx),
54 55 56 57 58
    ssl_(nullptr),
    session_(nullptr),
    bev_(nullptr),
    wrbev_(nullptr),
    rdbev_(nullptr),
59 60 61 62 63
    settings_timerev_(nullptr),
    fd_(-1),
    state_(DISCONNECTED),
    notified_(false),
    flow_control_(false)
64 65
{}

66
Http2Session::~Http2Session()
67 68 69 70
{
  disconnect();
}

71
int Http2Session::disconnect()
72
{
73
  if(LOG_ENABLED(INFO)) {
74
    SSLOG(INFO, this) << "Disconnecting";
75
  }
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
76
  nghttp2_session_del(session_);
77
  session_ = nullptr;
78

79 80 81 82 83
  if(settings_timerev_) {
    event_free(settings_timerev_);
    settings_timerev_ = nullptr;
  }

84
  if(ssl_) {
85
    SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN);
86 87 88
    SSL_shutdown(ssl_);
  }
  if(bev_) {
89
    int fd = bufferevent_getfd(bev_);
90 91
    bufferevent_disable(bev_, EV_READ | EV_WRITE);
    bufferevent_free(bev_);
92
    bev_ = nullptr;
93 94 95 96 97 98 99 100 101
    if(fd != -1) {
      if(fd_ == -1) {
        fd_ = fd;
      } else if(fd != fd_) {
        SSLOG(WARNING, this) << "fd in bev_ != fd_";
        shutdown(fd, SHUT_WR);
        close(fd);
      }
    }
102 103 104 105
  }
  if(ssl_) {
    SSL_free(ssl_);
  }
106
  ssl_ = nullptr;
107

108
  if(fd_ != -1) {
109 110 111
    if(LOG_ENABLED(INFO)) {
      SSLOG(INFO, this) << "Closing fd=" << fd_;
    }
112 113 114 115 116 117
    shutdown(fd_, SHUT_WR);
    close(fd_);
    fd_ = -1;
  }

  if(proxy_htp_) {
118
    proxy_htp_.reset();
119 120 121 122 123 124
  }

  notified_ = false;
  state_ = DISCONNECTED;

  // Delete all client handler associated to Downstream. When deleting
125
  // Http2DownstreamConnection, it calls this object's
126
  // remove_downstream_connection(). The multiple
127
  // Http2DownstreamConnection objects belong to the same ClientHandler
128 129
  // object. So first dump ClientHandler objects and delete them once
  // and for all.
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
130 131
  std::vector<Http2DownstreamConnection*> vec(std::begin(dconns_),
                                              std::end(dconns_));
132
  std::set<ClientHandler*> handlers;
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
133 134
  for(auto dc : vec) {
    handlers.insert(dc->get_client_handler());
135
  }
136 137
  for(auto& h : handlers) {
    delete h;
138 139
  }

140
  dconns_.clear();
141 142
  for(auto& s : streams_) {
    delete s;
143 144 145 146 147 148 149 150 151
  }
  streams_.clear();
  return 0;
}

namespace {
void notify_readcb(bufferevent *bev, void *arg)
{
  int rv;
152
  auto http2session = static_cast<Http2Session*>(arg);
153 154 155 156
  http2session->clear_notify();
  switch(http2session->get_state()) {
  case Http2Session::DISCONNECTED:
    rv = http2session->initiate_connection();
157
    if(rv != 0) {
158
      SSLOG(FATAL, http2session)
159
        << "Could not initiate backend connection";
160
      http2session->disconnect();
161 162
    }
    break;
163 164
  case Http2Session::CONNECTED:
    rv = http2session->send();
165
    if(rv != 0) {
166
      http2session->disconnect();
167 168 169 170 171 172 173 174 175
    }
    break;
  }
}
} // namespace

namespace {
void notify_eventcb(bufferevent *bev, short events, void *arg)
{
176
  auto http2session = static_cast<Http2Session*>(arg);
177 178
  // TODO should DIE()?
  if(events & BEV_EVENT_EOF) {
179
    SSLOG(ERROR, http2session) << "Notification connection lost: EOF";
180 181
  }
  if(events & BEV_EVENT_TIMEOUT) {
182
    SSLOG(ERROR, http2session) << "Notification connection lost: timeout";
183 184
  }
  if(events & BEV_EVENT_ERROR) {
185
    SSLOG(ERROR, http2session) << "Notification connection lost: network error";
186 187 188 189
  }
}
} // namespace

190
int Http2Session::init_notification()
191 192 193 194 195
{
  int rv;
  int sockpair[2];
  rv = socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair);
  if(rv == -1) {
196
    SSLOG(FATAL, this) << "socketpair() failed: errno=" << errno;
197 198
    return -1;
  }
199 200
  evutil_make_socket_nonblocking(sockpair[0]);
  evutil_make_socket_nonblocking(sockpair[1]);
201 202 203 204
  wrbev_ = bufferevent_socket_new(evbase_, sockpair[0],
                                  BEV_OPT_CLOSE_ON_FREE|
                                  BEV_OPT_DEFER_CALLBACKS);
  if(!wrbev_) {
205
    SSLOG(FATAL, this) << "bufferevent_socket_new() failed";
206 207 208 209 210 211 212 213 214
    for(int i = 0; i < 2; ++i) {
      close(sockpair[i]);
    }
    return -1;
  }
  rdbev_ = bufferevent_socket_new(evbase_, sockpair[1],
                                  BEV_OPT_CLOSE_ON_FREE|
                                  BEV_OPT_DEFER_CALLBACKS);
  if(!rdbev_) {
215
    SSLOG(FATAL, this) << "bufferevent_socket_new() failed";
216 217 218 219
    close(sockpair[1]);
    return -1;
  }
  bufferevent_enable(rdbev_, EV_READ);
220
  bufferevent_setcb(rdbev_, notify_readcb, nullptr, notify_eventcb, this);
221 222 223 224 225 226 227
  return 0;
}

namespace {
void readcb(bufferevent *bev, void *ptr)
{
  int rv;
228
  auto http2session = static_cast<Http2Session*>(ptr);
229
  rv = http2session->on_read();
230
  if(rv != 0) {
231
    http2session->disconnect();
232 233 234 235 236 237 238
  }
}
} // namespace

namespace {
void writecb(bufferevent *bev, void *ptr)
{
239 240 241
  if(evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
    return;
  }
242
  int rv;
243
  auto http2session = static_cast<Http2Session*>(ptr);
244
  rv = http2session->on_write();
245
  if(rv != 0) {
246
    http2session->disconnect();
247 248 249 250 251 252 253
  }
}
} // namespace

namespace {
void eventcb(bufferevent *bev, short events, void *ptr)
{
254
  auto http2session = static_cast<Http2Session*>(ptr);
255
  if(events & BEV_EVENT_CONNECTED) {
256
    if(LOG_ENABLED(INFO)) {
257
      SSLOG(INFO, http2session) << "Connection established";
258
    }
259
    http2session->set_state(Http2Session::CONNECTED);
260
    if((!get_config()->downstream_no_tls &&
261 262 263
        !get_config()->insecure && http2session->check_cert() != 0) ||
       http2session->on_connect() != 0) {
      http2session->disconnect();
264 265
      return;
    }
266 267
    int fd = bufferevent_getfd(bev);
    int val = 1;
268 269
    if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
                  reinterpret_cast<char *>(&val), sizeof(val)) == -1) {
270 271
      SSLOG(WARNING, http2session)
        << "Setting option TCP_NODELAY failed: errno=" << errno;
272
    }
273
  } else if(events & BEV_EVENT_EOF) {
274
    if(LOG_ENABLED(INFO)) {
275
      SSLOG(INFO, http2session) << "EOF";
276
    }
277
    http2session->disconnect();
278
  } else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
279
    if(LOG_ENABLED(INFO)) {
280
      if(events & BEV_EVENT_ERROR) {
281
        SSLOG(INFO, http2session) << "Network error";
282
      } else {
283
        SSLOG(INFO, http2session) << "Timeout";
284
      }
285
    }
286
    http2session->disconnect();
287 288 289 290
  }
}
} // namespace

291 292 293
namespace {
void proxy_readcb(bufferevent *bev, void *ptr)
{
294
  auto http2session = static_cast<Http2Session*>(ptr);
295 296 297
  if(http2session->on_read_proxy() == 0) {
    switch(http2session->get_state()) {
    case Http2Session::PROXY_CONNECTED:
298
      // The current bufferevent is no longer necessary, so delete it
299
      // here. But we keep fd_ inside it.
300
      http2session->unwrap_free_bev();
301
      // Initiate SSL/TLS handshake through established tunnel.
302 303
      if(http2session->initiate_connection() != 0) {
        http2session->disconnect();
304
      }
305
      break;
306 307
    case Http2Session::PROXY_FAILED:
      http2session->disconnect();
308 309 310
      break;
    }
  } else {
311
    http2session->disconnect();
312 313 314 315 316 317 318
  }
}
} // namespace

namespace {
void proxy_eventcb(bufferevent *bev, short events, void *ptr)
{
319
  auto http2session = static_cast<Http2Session*>(ptr);
320 321
  if(events & BEV_EVENT_CONNECTED) {
    if(LOG_ENABLED(INFO)) {
322
      SSLOG(INFO, http2session) << "Connected to the proxy";
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    }
    std::string req = "CONNECT ";
    req += get_config()->downstream_hostport;
    req += " HTTP/1.1\r\nHost: ";
    req += get_config()->downstream_host;
    req += "\r\n";
    if(get_config()->downstream_http_proxy_userinfo) {
      req += "Proxy-Authorization: Basic ";
      size_t len = strlen(get_config()->downstream_http_proxy_userinfo);
      req += base64::encode(get_config()->downstream_http_proxy_userinfo,
                            get_config()->downstream_http_proxy_userinfo+len);
      req += "\r\n";
    }
    req += "\r\n";
    if(LOG_ENABLED(INFO)) {
338
      SSLOG(INFO, http2session) << "HTTP proxy request headers\n" << req;
339 340
    }
    if(bufferevent_write(bev, req.c_str(), req.size()) != 0) {
341 342
      SSLOG(ERROR, http2session) << "bufferevent_write() failed";
      http2session->disconnect();
343 344 345
    }
  } else if(events & BEV_EVENT_EOF) {
    if(LOG_ENABLED(INFO)) {
346
      SSLOG(INFO, http2session) << "Proxy EOF";
347
    }
348
    http2session->disconnect();
349 350 351
  } else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) {
    if(LOG_ENABLED(INFO)) {
      if(events & BEV_EVENT_ERROR) {
352
        SSLOG(INFO, http2session) << "Network error";
353
      } else {
354
        SSLOG(INFO, http2session) << "Timeout";
355 356
      }
    }
357
    http2session->disconnect();
358 359 360 361
  }
}
} // namespace

362
int Http2Session::check_cert()
363 364 365 366
{
  return ssl::check_cert(ssl_);
}

367
int Http2Session::initiate_connection()
368
{
369
  int rv = 0;
370 371 372 373 374 375 376
  if(get_config()->downstream_http_proxy_host && state_ == DISCONNECTED) {
    if(LOG_ENABLED(INFO)) {
      SSLOG(INFO, this) << "Connecting to the proxy "
                        << get_config()->downstream_http_proxy_host << ":"
                        << get_config()->downstream_http_proxy_port;
    }
    bev_ = bufferevent_socket_new(evbase_, -1, BEV_OPT_DEFER_CALLBACKS);
377 378 379 380
    if(!bev_) {
      SSLOG(ERROR, this) << "bufferevent_socket_new() failed";
      return SHRPX_ERR_NETWORK;
    }
381 382 383 384 385 386
    bufferevent_enable(bev_, EV_READ);
    bufferevent_set_timeouts(bev_, &get_config()->downstream_read_timeout,
                             &get_config()->downstream_write_timeout);

    // No need to set writecb because we write the request when
    // connected at once.
387
    bufferevent_setcb(bev_, proxy_readcb, nullptr, proxy_eventcb, this);
388 389 390 391 392 393 394 395 396 397
    rv = bufferevent_socket_connect
      (bev_,
       const_cast<sockaddr*>(&get_config()->downstream_http_proxy_addr.sa),
       get_config()->downstream_http_proxy_addrlen);
    if(rv != 0) {
      SSLOG(ERROR, this) << "Failed to connect to the proxy "
                         << get_config()->downstream_http_proxy_host << ":"
                         << get_config()->downstream_http_proxy_port;
      return SHRPX_ERR_NETWORK;
    }
398 399
    proxy_htp_ = util::make_unique<http_parser>();
    http_parser_init(proxy_htp_.get(), HTTP_RESPONSE);
400
    proxy_htp_->data = this;
401

402 403 404 405 406
    state_ = PROXY_CONNECTING;
  } else if(state_ == DISCONNECTED || state_ == PROXY_CONNECTED) {
    if(LOG_ENABLED(INFO)) {
      SSLOG(INFO, this) << "Connecting to downstream server";
    }
407 408 409 410 411 412 413 414
    if(ssl_ctx_) {
      // We are establishing TLS connection.
      ssl_ = SSL_new(ssl_ctx_);
      if(!ssl_) {
        SSLOG(ERROR, this) << "SSL_new() failed: "
                           << ERR_error_string(ERR_get_error(), NULL);
        return -1;
      }
415

416
      const char *sni_name = nullptr;
417 418 419 420 421 422 423
      if ( get_config()->backend_tls_sni_name ) {
        sni_name = get_config()->backend_tls_sni_name;
      }
      else {
        sni_name = get_config()->downstream_host;
      }

424
      if(!util::numeric_host(sni_name)) {
425 426 427
        // TLS extensions: SNI. There is no documentation about the return
        // code for this function (actually this is macro wrapping SSL_ctrl
        // at the time of this writing).
428
        SSL_set_tlsext_host_name(ssl_, sni_name);
429 430 431 432 433 434
      }
      // If state_ == PROXY_CONNECTED, we has connected to the proxy
      // using fd_ and tunnel has been established.
      bev_ = bufferevent_openssl_socket_new(evbase_, fd_, ssl_,
                                            BUFFEREVENT_SSL_CONNECTING,
                                            BEV_OPT_DEFER_CALLBACKS);
435 436 437 438
      if(!bev_) {
        SSLOG(ERROR, this) << "bufferevent_socket_new() failed";
        return SHRPX_ERR_NETWORK;
      }
439 440 441 442 443 444 445 446
      rv = bufferevent_socket_connect
        (bev_,
         // TODO maybe not thread-safe?
         const_cast<sockaddr*>(&get_config()->downstream_addr.sa),
         get_config()->downstream_addrlen);
    } else if(state_ == DISCONNECTED) {
      // Without TLS and proxy.
      bev_ = bufferevent_socket_new(evbase_, -1, BEV_OPT_DEFER_CALLBACKS);
447 448 449 450
      if(!bev_) {
        SSLOG(ERROR, this) << "bufferevent_socket_new() failed";
        return SHRPX_ERR_NETWORK;
      }
451 452 453 454 455 456 457 458
      rv = bufferevent_socket_connect
        (bev_,
         const_cast<sockaddr*>(&get_config()->downstream_addr.sa),
         get_config()->downstream_addrlen);
    } else {
      assert(state_ == PROXY_CONNECTED);
      // Without TLS but with proxy.
      bev_ = bufferevent_socket_new(evbase_, fd_, BEV_OPT_DEFER_CALLBACKS);
459 460 461 462
      if(!bev_) {
        SSLOG(ERROR, this) << "bufferevent_socket_new() failed";
        return SHRPX_ERR_NETWORK;
      }
463 464 465 466 467 468 469
      // Connection already established.
      eventcb(bev_, BEV_EVENT_CONNECTED, this);
      // eventcb() has no return value. Check state_ to whether it was
      // failed or not.
      if(state_ == DISCONNECTED) {
        return -1;
      }
470 471 472 473 474 475 476 477
    }
    if(rv != 0) {
      return SHRPX_ERR_NETWORK;
    }

    bufferevent_setwatermark(bev_, EV_READ, 0, SHRPX_READ_WARTER_MARK);
    bufferevent_enable(bev_, EV_READ);
    bufferevent_setcb(bev_, readcb, writecb, eventcb, this);
478
    // Set timeout for HTTP2 session
479 480
    bufferevent_set_timeouts(bev_, &get_config()->downstream_read_timeout,
                             &get_config()->downstream_write_timeout);
481

482 483 484 485
    // We have been already connected when no TLS and proxy is used.
    if(state_ != CONNECTED) {
      state_ = CONNECTING;
    }
486 487 488
  } else {
    // Unreachable
    DIE();
489
  }
490 491
  return 0;
}
492

493
void Http2Session::unwrap_free_bev()
494
{
495 496
  assert(fd_ == -1);
  fd_ = bufferevent_getfd(bev_);
497
  bufferevent_free(bev_);
498
  bev_ = nullptr;
499
}
500

501 502 503
namespace {
int htp_hdrs_completecb(http_parser *htp)
{
504
  auto http2session = static_cast<Http2Session*>(htp->data);
505 506 507
  // We just check status code here
  if(htp->status_code == 200) {
    if(LOG_ENABLED(INFO)) {
508
      SSLOG(INFO, http2session) << "Tunneling success";
509
    }
510
    http2session->set_state(Http2Session::PROXY_CONNECTED);
511
  } else {
512 513
    SSLOG(WARNING, http2session) << "Tunneling failed";
    http2session->set_state(Http2Session::PROXY_FAILED);
514
  }
515 516
  return 0;
}
517 518 519 520
} // namespace

namespace {
http_parser_settings htp_hooks = {
521 522 523 524 525
  nullptr, /*http_cb      on_message_begin;*/
  nullptr, /*http_data_cb on_url;*/
  nullptr, /*http_cb on_status_complete */
  nullptr, /*http_data_cb on_header_field;*/
  nullptr, /*http_data_cb on_header_value;*/
526
  htp_hdrs_completecb, /*http_cb      on_headers_complete;*/
527 528
  nullptr, /*http_data_cb on_body;*/
  nullptr  /*http_cb      on_message_complete;*/
529 530
};
} // namespace
531

532
int Http2Session::on_read_proxy()
533
{
534 535
  auto input = bufferevent_get_input(bev_);
  auto mem = evbuffer_pullup(input, -1);
536

537
  size_t nread = http_parser_execute(proxy_htp_.get(), &htp_hooks,
538 539 540
                                     reinterpret_cast<const char*>(mem),
                                     evbuffer_get_length(input));

541 542 543 544
  if(evbuffer_drain(input, nread) != 0) {
    SSLOG(FATAL, this) << "evbuffer_drain() failed";
    return -1;
  }
545
  auto htperr = HTTP_PARSER_ERRNO(proxy_htp_.get());
546 547 548 549 550
  if(htperr == HPE_OK) {
    return 0;
  } else {
    return -1;
  }
551 552
}

553
void Http2Session::add_downstream_connection(Http2DownstreamConnection *dconn)
554 555 556 557
{
  dconns_.insert(dconn);
}

558 559
void Http2Session::remove_downstream_connection
(Http2DownstreamConnection *dconn)
560 561 562 563 564
{
  dconns_.erase(dconn);
  dconn->detach_stream_data();
}

565
void Http2Session::remove_stream_data(StreamData *sd)
566 567 568 569 570 571 572 573
{
  streams_.erase(sd);
  if(sd->dconn) {
    sd->dconn->detach_stream_data();
  }
  delete sd;
}

574
int Http2Session::submit_request(Http2DownstreamConnection *dconn,
575
                                 int32_t pri,
576 577
                                 const nghttp2_nv *nva, size_t nvlen,
                                 const nghttp2_data_provider *data_prd)
578 579
{
  assert(state_ == CONNECTED);
580
  auto sd = util::make_unique<StreamData>();
581 582
  // TODO Specify nullptr to pri_spec for now
  int rv = nghttp2_submit_request(session_, nullptr, nva, nvlen,
583
                                  data_prd, sd.get());
584
  if(rv == 0) {
585 586
    dconn->attach_stream_data(sd.get());
    streams_.insert(sd.release());
587
  } else {
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
588 589
    SSLOG(FATAL, this) << "nghttp2_submit_request() failed: "
                       << nghttp2_strerror(rv);
590 591 592 593 594
    return -1;
  }
  return 0;
}

595
int Http2Session::submit_rst_stream(int32_t stream_id,
596
                                   nghttp2_error_code error_code)
597 598
{
  assert(state_ == CONNECTED);
599 600 601 602 603 604
  if(LOG_ENABLED(INFO)) {
    SSLOG(INFO, this) << "RST_STREAM stream_id="
                      << stream_id
                      << " with error_code="
                      << error_code;
  }
605 606
  int rv = nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE,
                                     stream_id, error_code);
607
  if(rv != 0) {
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
608 609
    SSLOG(FATAL, this) << "nghttp2_submit_rst_stream() failed: "
                       << nghttp2_strerror(rv);
610 611 612 613 614
    return -1;
  }
  return 0;
}

615
int Http2Session::submit_window_update(Http2DownstreamConnection *dconn,
616 617 618 619
                                      int32_t amount)
{
  assert(state_ == CONNECTED);
  int rv;
620 621 622 623 624 625
  int32_t stream_id;
  if(dconn) {
    stream_id = dconn->get_downstream()->get_downstream_stream_id();
  } else {
    stream_id = 0;
  }
626 627
  rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE,
                                    stream_id, amount);
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
628 629 630
  if(rv < NGHTTP2_ERR_FATAL) {
    SSLOG(FATAL, this) << "nghttp2_submit_window_update() failed: "
                       << nghttp2_strerror(rv);
631 632 633 634 635
    return -1;
  }
  return 0;
}

636 637 638 639 640 641 642 643
int Http2Session::submit_priority(Http2DownstreamConnection *dconn,
                                  int32_t pri)
{
  assert(state_ == CONNECTED);
  if(!dconn) {
    return 0;
  }
  int rv;
644 645 646 647 648 649 650 651 652

  // TODO Disabled temporarily

  // rv = nghttp2_submit_priority(session_, NGHTTP2_FLAG_NONE,
  //                              dconn->get_downstream()->
  //                              get_downstream_stream_id(), pri);

  rv = 0;

653 654 655 656 657 658 659 660
  if(rv < NGHTTP2_ERR_FATAL) {
    SSLOG(FATAL, this) << "nghttp2_submit_priority() failed: "
                       << nghttp2_strerror(rv);
    return -1;
  }
  return 0;
}

661
nghttp2_session* Http2Session::get_session() const
662
{
663
  return session_;
664 665
}

666
bool Http2Session::get_flow_control() const
667 668 669 670
{
  return flow_control_;
}

671
int Http2Session::resume_data(Http2DownstreamConnection *dconn)
672 673
{
  assert(state_ == CONNECTED);
674
  auto downstream = dconn->get_downstream();
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
675
  int rv = nghttp2_session_resume_data(session_,
676 677 678
                                       downstream->get_downstream_stream_id());
  switch(rv) {
  case 0:
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
679
  case NGHTTP2_ERR_INVALID_ARGUMENT:
680 681
    return 0;
  default:
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
682 683
    SSLOG(FATAL, this) << "nghttp2_resume_session() failed: "
                       << nghttp2_strerror(rv);
684 685 686 687 688
    return -1;
  }
}

namespace {
689
void call_downstream_readcb(Http2Session *http2session, Downstream *downstream)
690
{
691
  auto upstream = downstream->get_upstream();
692 693
  if(upstream) {
    (upstream->get_downstream_readcb())
694
      (http2session->get_bev(),
695 696 697 698 699 700
       downstream->get_downstream_connection());
  }
}
} // namespace

namespace {
701
int on_stream_close_callback
702
(nghttp2_session *session, int32_t stream_id, nghttp2_error_code error_code,
703 704 705
 void *user_data)
{
  int rv;
706
  auto http2session = static_cast<Http2Session*>(user_data);
707
  if(LOG_ENABLED(INFO)) {
708 709
    SSLOG(INFO, http2session) << "Stream stream_id=" << stream_id
                              << " is being closed";
710
  }
711
  auto sd = static_cast<StreamData*>
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
712
    (nghttp2_session_get_stream_user_data(session, stream_id));
713 714 715
  if(sd == 0) {
    // We might get this close callback when pushed streams are
    // closed.
716
    return 0;
717
  }
718
  auto dconn = sd->dconn;
719
  if(dconn) {
720
    auto downstream = dconn->get_downstream();
721
    if(downstream && downstream->get_downstream_stream_id() == stream_id) {
722 723
      auto upstream = downstream->get_upstream();
      if(error_code == NGHTTP2_NO_ERROR) {
724 725 726 727 728 729 730 731
        downstream->set_response_state(Downstream::MSG_COMPLETE);
        rv = upstream->on_downstream_body_complete(downstream);
        if(rv != 0) {
          downstream->set_response_state(Downstream::MSG_RESET);
        }
      } else {
        downstream->set_response_state(Downstream::MSG_RESET);
      }
732
      call_downstream_readcb(http2session, downstream);
733 734 735 736
      // dconn may be deleted
    }
  }
  // The life time of StreamData ends here
737
  http2session->remove_stream_data(sd);
738
  return 0;
739 740 741
}
} // namespace

742 743 744
namespace {
void settings_timeout_cb(evutil_socket_t fd, short what, void *arg)
{
745
  auto http2session = static_cast<Http2Session*>(arg);
746
  SSLOG(INFO, http2session) << "SETTINGS timeout";
747
  if(http2session->terminate_session(NGHTTP2_SETTINGS_TIMEOUT) != 0) {
748
    http2session->disconnect();
749 750
    return;
  }
751 752
  if(http2session->send() != 0) {
    http2session->disconnect();
753 754 755 756
  }
}
} // namespace

757
int Http2Session::start_settings_timer()
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
{
  int rv;
  // We submit SETTINGS only once
  if(settings_timerev_) {
    return 0;
  }
  settings_timerev_ = evtimer_new(evbase_, settings_timeout_cb, this);
  if(settings_timerev_ == nullptr) {
    return -1;
  }
  // SETTINGS ACK timeout is 10 seconds for now
  timeval settings_timeout = { 10, 0 };
  rv = evtimer_add(settings_timerev_, &settings_timeout);
  if(rv == -1) {
    return -1;
  }
  return 0;
}

777
void Http2Session::stop_settings_timer()
778 779 780 781 782 783 784 785
{
  if(settings_timerev_ == nullptr) {
    return;
  }
  event_free(settings_timerev_);
  settings_timerev_ = nullptr;
}

786 787 788 789 790 791 792 793 794 795 796
namespace {
int on_header_callback(nghttp2_session *session,
                       const nghttp2_frame *frame,
                       const uint8_t *name, size_t namelen,
                       const uint8_t *value, size_t valuelen,
                       void *user_data)
{
  if(frame->hd.type != NGHTTP2_HEADERS ||
     frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
    return 0;
  }
797
  auto sd = static_cast<StreamData*>
798 799 800 801 802 803 804 805
    (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
  if(!sd || !sd->dconn) {
    return 0;
  }
  auto downstream = sd->dconn->get_downstream();
  if(!downstream) {
    return 0;
  }
806 807 808 809
  if(downstream->get_response_headers_sum() > Downstream::MAX_HEADERS_SUM) {
    if(LOG_ENABLED(INFO)) {
      DLOG(INFO, downstream) << "Too large header block size="
                             << downstream->get_response_headers_sum();
810
    }
811
    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
812
  }
813 814 815
  if(!http2::check_nv(name, namelen, value, valuelen)) {
    return 0;
  }
816 817 818 819 820 821
  downstream->split_add_response_header(name, namelen, value, valuelen);
  return 0;
}
} // namespace

namespace {
822 823 824
int on_begin_headers_callback(nghttp2_session *session,
                              const nghttp2_frame *frame,
                              void *user_data)
825
{
826 827 828 829 830
  auto http2session = static_cast<Http2Session*>(user_data);
  if(frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
    // server sends request HEADERS
    http2session->submit_rst_stream(frame->hd.stream_id,
                                    NGHTTP2_REFUSED_STREAM);
831 832
    return 0;
  }
833 834 835 836 837 838 839 840
  if(frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
    return 0;
  }
  auto sd = static_cast<StreamData*>
    (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
  if(!sd || !sd->dconn) {
    http2session->submit_rst_stream(frame->hd.stream_id,
                                    NGHTTP2_INTERNAL_ERROR);
841 842
    return 0;
  }
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
  auto downstream = sd->dconn->get_downstream();
  if(!downstream ||
     downstream->get_downstream_stream_id() != frame->hd.stream_id) {
    http2session->submit_rst_stream(frame->hd.stream_id,
                                    NGHTTP2_INTERNAL_ERROR);
    return 0;
  }
  return 0;
}
} // namespace

namespace {
int on_response_headers(Http2Session *http2session,
                        nghttp2_session *session,
                        const nghttp2_frame *frame)
{
859
  int rv;
860
  auto sd = static_cast<StreamData*>
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
    (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
  if(!sd || !sd->dconn) {
    return 0;
  }
  auto downstream = sd->dconn->get_downstream();
  if(!downstream) {
    return 0;
  }
  downstream->normalize_response_headers();
  auto& nva = downstream->get_response_headers();

  if(!http2::check_http2_headers(nva)) {
    http2session->submit_rst_stream(frame->hd.stream_id,
                                    NGHTTP2_PROTOCOL_ERROR);
    downstream->set_response_state(Downstream::MSG_RESET);
    call_downstream_readcb(http2session, downstream);
    return 0;
  }

  auto status = http2::get_unique_header(nva, ":status");
  if(!status || http2::value_lws(status)) {
    http2session->submit_rst_stream(frame->hd.stream_id,
                                    NGHTTP2_PROTOCOL_ERROR);
    downstream->set_response_state(Downstream::MSG_RESET);
    call_downstream_readcb(http2session, downstream);
    return 0;
  }
  downstream->set_response_http_status(strtoul(status->second.c_str(),
                                               nullptr, 10));
890 891
  downstream->set_response_major(2);
  downstream->set_response_minor(0);
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956

  auto content_length = http2::get_header(nva, "content-length");
  if(!content_length && downstream->get_request_method() != "HEAD" &&
     downstream->get_request_method() != "CONNECT") {
    unsigned int status;
    status = downstream->get_response_http_status();
    if(!((100 <= status && status <= 199) || status == 204 ||
         status == 304)) {
      // Here we have response body but Content-Length is not known
      // in advance.
      if(downstream->get_request_major() <= 0 ||
         downstream->get_request_minor() <= 0) {
        // We simply close connection for pre-HTTP/1.1 in this case.
        downstream->set_response_connection_close(true);
      } else {
        // Otherwise, use chunked encoding to keep upstream
        // connection open.  In HTTP2, we are supporsed not to
        // receive transfer-encoding.
        downstream->add_response_header("transfer-encoding", "chunked");
      }
    }
  }

  if(LOG_ENABLED(INFO)) {
    std::stringstream ss;
    for(auto& nv : nva) {
      ss << TTY_HTTP_HD << nv.first << TTY_RST << ": " << nv.second << "\n";
    }
    SSLOG(INFO, http2session) << "HTTP response headers. stream_id="
                              << frame->hd.stream_id
                              << "\n" << ss.str();
  }

  auto upstream = downstream->get_upstream();
  downstream->set_response_state(Downstream::HEADER_COMPLETE);
  downstream->check_upgrade_fulfilled();
  if(downstream->get_upgraded()) {
    downstream->set_response_connection_close(true);
    // On upgrade sucess, both ends can send data
    if(upstream->resume_read(SHRPX_MSG_BLOCK, downstream) != 0) {
      // If resume_read fails, just drop connection. Not ideal.
      delete upstream->get_client_handler();
      return 0;
    }
    downstream->set_request_state(Downstream::HEADER_COMPLETE);
    if(LOG_ENABLED(INFO)) {
      SSLOG(INFO, http2session) << "HTTP upgrade success. stream_id="
                                << frame->hd.stream_id;
    }
  } else if(downstream->get_request_method() == "CONNECT") {
    // If request is CONNECT, terminate request body to avoid for
    // stream to stall.
    downstream->end_upload_data();
  }
  rv = upstream->on_downstream_header_complete(downstream);
  if(rv != 0) {
    http2session->submit_rst_stream(frame->hd.stream_id,
                                    NGHTTP2_PROTOCOL_ERROR);
    downstream->set_response_state(Downstream::MSG_RESET);
  }
  call_downstream_readcb(http2session, downstream);
  return 0;
}
} // namespace

957
namespace {
958
int on_frame_recv_callback
959
(nghttp2_session *session, const nghttp2_frame *frame, void *user_data)
960
{
961
  auto http2session = static_cast<Http2Session*>(user_data);
962
  switch(frame->hd.type) {
963 964
  case NGHTTP2_HEADERS:
    return on_response_headers(http2session, session, frame);
965
  case NGHTTP2_RST_STREAM: {
966
    auto sd = static_cast<StreamData*>
967 968 969 970 971
      (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
    if(sd && sd->dconn) {
      auto downstream = sd->dconn->get_downstream();
      if(downstream &&
         downstream->get_downstream_stream_id() == frame->hd.stream_id) {
972
        if(downstream->get_upgraded() &&
973 974 975 976 977
           downstream->get_response_state() == Downstream::HEADER_COMPLETE) {
          // For tunneled connection, we has to submit RST_STREAM to
          // upstream *after* whole response body is sent. We just set
          // MSG_COMPLETE here. Upstream will take care of that.
          if(LOG_ENABLED(INFO)) {
978 979 980
            SSLOG(INFO, http2session) << "RST_STREAM against tunneled stream "
                                      << "stream_id="
                                      << frame->hd.stream_id;
981 982 983 984 985 986 987 988 989 990
          }
          downstream->get_upstream()->on_downstream_body_complete(downstream);
          downstream->set_response_state(Downstream::MSG_COMPLETE);
        } else {
          // If we got RST_STREAM, just flag MSG_RESET to indicate
          // upstream connection must be terminated.
          downstream->set_response_state(Downstream::MSG_RESET);
        }
        downstream->set_response_rst_stream_error_code
          (frame->rst_stream.error_code);
991
        call_downstream_readcb(http2session, downstream);
992 993 994 995
      }
    }
    break;
  }
996 997 998 999
  case NGHTTP2_SETTINGS:
    if((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
      break;
    }
1000
    http2session->stop_settings_timer();
1001
    break;
1002 1003
  case NGHTTP2_PUSH_PROMISE:
    if(LOG_ENABLED(INFO)) {
1004
      SSLOG(INFO, http2session)
1005 1006
        << "Received downstream PUSH_PROMISE stream_id=" << frame->hd.stream_id
        << ", promised_stream_id=" << frame->push_promise.promised_stream_id;
1007 1008
    }
    // We just respond with RST_STREAM.
1009
    http2session->submit_rst_stream(frame->push_promise.promised_stream_id,
1010
                                    NGHTTP2_REFUSED_STREAM);
1011
    break;
1012 1013 1014
  default:
    break;
  }
1015
  return 0;
1016 1017 1018 1019
}
} // namespace

namespace {
1020 1021 1022 1023
int 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)
1024 1025
{
  int rv;
1026 1027
  auto http2session = static_cast<Http2Session*>(user_data);
  auto sd = static_cast<StreamData*>
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
1028
    (nghttp2_session_get_stream_user_data(session, stream_id));
1029
  if(!sd || !sd->dconn) {
1030
    http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
1031
    return 0;
1032
  }
1033
  auto downstream = sd->dconn->get_downstream();
1034
  if(!downstream || downstream->get_downstream_stream_id() != stream_id) {
1035
    http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
1036
    return 0;
1037
  }
1038

1039
  auto upstream = downstream->get_upstream();
1040 1041
  rv = upstream->on_downstream_body(downstream, data, len);
  if(rv != 0) {
1042
    http2session->submit_rst_stream(stream_id, NGHTTP2_INTERNAL_ERROR);
1043 1044
    downstream->set_response_state(Downstream::MSG_RESET);
  }
1045
  call_downstream_readcb(http2session, downstream);
1046
  return 0;
1047 1048 1049 1050
}
} // namespace

namespace {
1051
int before_frame_send_callback(nghttp2_session *session,
1052
                               const nghttp2_frame *frame,
1053
                               void *user_data)
1054
{
1055
  auto http2session = static_cast<Http2Session*>(user_data);
1056 1057
  if(frame->hd.type == NGHTTP2_HEADERS &&
     frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
1058
    auto sd = static_cast<StreamData*>
1059
      (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1060
    if(!sd || !sd->dconn) {
1061
      http2session->submit_rst_stream(frame->hd.stream_id, NGHTTP2_CANCEL);
1062
      return 0;
1063
    }
1064
    auto downstream = sd->dconn->get_downstream();
1065
    if(downstream) {
1066
      downstream->set_downstream_stream_id(frame->hd.stream_id);
1067
    } else {
1068
      http2session->submit_rst_stream(frame->hd.stream_id, NGHTTP2_CANCEL);
1069 1070
    }
  }
1071
  return 0;
1072 1073 1074
}
} // namespace

1075 1076 1077 1078
namespace {
int on_frame_send_callback(nghttp2_session* session,
                           const nghttp2_frame *frame, void *user_data)
{
1079
  auto http2session = static_cast<Http2Session*>(user_data);
1080 1081
  if(frame->hd.type == NGHTTP2_SETTINGS &&
     (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
1082
    if(http2session->start_settings_timer() != 0) {
1083 1084 1085 1086 1087 1088 1089
      return NGHTTP2_ERR_CALLBACK_FAILURE;
    }
  }
  return 0;
}
} // namespace

1090
namespace {
1091
int on_frame_not_send_callback(nghttp2_session *session,
1092 1093
                               const nghttp2_frame *frame,
                               int lib_error_code, void *user_data)
1094
{
1095
  auto http2session = static_cast<Http2Session*>(user_data);
1096
  SSLOG(WARNING, http2session) << "Failed to send control frame type="
1097
                               << static_cast<uint32_t>(frame->hd.type)
1098 1099
                               << "lib_error_code=" << lib_error_code << ":"
                               << nghttp2_strerror(lib_error_code);
1100 1101
  if(frame->hd.type == NGHTTP2_HEADERS &&
     frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
1102 1103
    // To avoid stream hanging around, flag Downstream::MSG_RESET and
    // terminate the upstream and downstream connections.
1104
    auto sd = static_cast<StreamData*>
1105
      (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id));
1106
    if(!sd) {
1107
      return 0;
1108 1109
    }
    if(sd->dconn) {
1110
      auto downstream = sd->dconn->get_downstream();
1111
      if(!downstream ||
1112
         downstream->get_downstream_stream_id() != frame->hd.stream_id) {
1113
        return 0;
1114 1115
      }
      downstream->set_response_state(Downstream::MSG_RESET);
1116
      call_downstream_readcb(http2session, downstream);
1117
    }
1118
    http2session->remove_stream_data(sd);
1119
  }
1120
  return 0;
1121 1122 1123 1124
}
} // namespace

namespace {
1125 1126 1127 1128
int on_unknown_frame_recv_callback(nghttp2_session *session,
                                   const uint8_t *head, size_t headlen,
                                   const uint8_t *payload, size_t payloadlen,
                                   void *user_data)
1129
{
1130
  auto http2session = static_cast<Http2Session*>(user_data);
1131
  if(LOG_ENABLED(INFO)) {
1132
    SSLOG(INFO, http2session) << "Received unknown control frame";
1133
  }
1134
  return 0;
1135 1136 1137
}
} // namespace

1138
int Http2Session::on_connect()
1139 1140
{
  int rv;
1141
  if(ssl_ctx_) {
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
1142 1143
    const unsigned char *next_proto = nullptr;
    unsigned int next_proto_len;
1144
    SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162
    for(int i = 0; i < 2; ++i) {
      if(next_proto) {
        if(LOG_ENABLED(INFO)) {
          std::string proto(next_proto, next_proto+next_proto_len);
          SSLOG(INFO, this) << "Negotiated next protocol: " << proto;
        }
        if(next_proto_len != NGHTTP2_PROTO_VERSION_ID_LEN ||
           memcmp(NGHTTP2_PROTO_VERSION_ID, next_proto,
                  NGHTTP2_PROTO_VERSION_ID_LEN) != 0) {
          return -1;
        }
        break;
      }
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
      SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len);
#else // OPENSSL_VERSION_NUMBER < 0x10002000L
      break;
#endif // OPENSSL_VERSION_NUMBER < 0x10002000L
1163
    }
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
1164
    if(!next_proto) {
1165 1166
      return -1;
    }
1167
  }
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
1168
  nghttp2_session_callbacks callbacks;
1169 1170
  memset(&callbacks, 0, sizeof(callbacks));
  callbacks.on_stream_close_callback = on_stream_close_callback;
1171
  callbacks.on_frame_recv_callback = on_frame_recv_callback;
1172
  callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
1173
  callbacks.before_frame_send_callback = before_frame_send_callback;
1174
  callbacks.on_frame_send_callback = on_frame_send_callback;
1175 1176
  callbacks.on_frame_not_send_callback = on_frame_not_send_callback;
  callbacks.on_unknown_frame_recv_callback = on_unknown_frame_recv_callback;
1177
  callbacks.on_header_callback = on_header_callback;
1178
  callbacks.on_begin_headers_callback = on_begin_headers_callback;
1179 1180 1181
  if(get_config()->padding) {
    callbacks.select_padding_callback = http::select_padding_callback;
  }
1182

1183 1184 1185 1186 1187 1188 1189 1190
  nghttp2_opt_set opt_set;
  opt_set.no_auto_stream_window_update = 1;
  opt_set.no_auto_connection_window_update = 1;
  uint32_t opt_set_mask =
    NGHTTP2_OPT_NO_AUTO_STREAM_WINDOW_UPDATE |
    NGHTTP2_OPT_NO_AUTO_CONNECTION_WINDOW_UPDATE;
  rv = nghttp2_session_client_new2(&session_, &callbacks, this,
                                   opt_set_mask, &opt_set);
1191 1192 1193 1194
  if(rv != 0) {
    return -1;
  }

1195
  flow_control_ = true;
1196

1197 1198 1199 1200
  nghttp2_settings_entry entry[3];
  entry[0].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
  entry[0].value = 0;
  entry[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1201
  entry[1].value = get_config()->http2_max_concurrent_streams;
1202 1203

  entry[2].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1204
  entry[2].value = (1 << get_config()->http2_downstream_window_bits) - 1;
1205

1206 1207
  rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry,
                               sizeof(entry)/sizeof(nghttp2_settings_entry));
1208 1209 1210
  if(rv != 0) {
    return -1;
  }
1211

1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
  if(get_config()->http2_downstream_connection_window_bits > 16) {
    int32_t delta =
      (1 << get_config()->http2_downstream_connection_window_bits) - 1
      - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE;
    rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta);
    if(rv != 0) {
      return -1;
    }
  }

1222 1223 1224 1225 1226 1227
  rv = bufferevent_write(bev_, NGHTTP2_CLIENT_CONNECTION_HEADER,
                         NGHTTP2_CLIENT_CONNECTION_HEADER_LEN);
  if(rv != 0) {
    SSLOG(FATAL, this) << "bufferevent_write() failed";
    return -1;
  }
1228

1229 1230 1231 1232 1233 1234
  rv = send();
  if(rv != 0) {
    return -1;
  }

  // submit pending request
1235 1236
  for(auto dconn : dconns_) {
    if(dconn->push_request_headers() != 0) {
1237 1238 1239 1240 1241 1242
      return -1;
    }
  }
  return 0;
}

1243
int Http2Session::on_read()
1244
{
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256
  ssize_t rv = 0;
  auto input = bufferevent_get_input(bev_);
  auto inputlen = evbuffer_get_length(input);
  auto mem = evbuffer_pullup(input, -1);

  rv = nghttp2_session_mem_recv(session_, mem, inputlen);
  if(rv < 0) {
    SSLOG(ERROR, this) << "nghttp2_session_recv() returned error: "
                       << nghttp2_strerror(rv);
    return -1;
  }
  evbuffer_drain(input, rv);
1257
  return send();
1258 1259
}

1260
int Http2Session::on_write()
1261 1262 1263 1264
{
  return send();
}

1265
int Http2Session::send()
1266
{
1267
  int rv;
1268
  uint8_t buf[16384];
1269
  auto output = bufferevent_get_output(bev_);
1270
  util::EvbufferBuffer evbbuf(output, buf, sizeof(buf));
1271 1272
  for(;;) {
    // Check buffer length and return WOULDBLOCK if it is large enough.
1273 1274
    if(evbuffer_get_length(output) + evbbuf.get_buflen() >
       Http2Session::OUTBUF_MAX_THRES) {
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288
      return NGHTTP2_ERR_WOULDBLOCK;
    }

    const uint8_t *data;
    auto datalen = nghttp2_session_mem_send(session_, &data);

    if(datalen < 0) {
      SSLOG(ERROR, this) << "nghttp2_session_mem_send() returned error: "
                         << nghttp2_strerror(datalen);
      break;
    }
    if(datalen == 0) {
      break;
    }
1289 1290 1291 1292
    rv = evbbuf.add(data, datalen);
    if(rv != 0) {
      SSLOG(FATAL, this) << "evbuffer_add() failed";
      return -1;
1293
    }
1294 1295
  }

1296 1297
  rv = evbbuf.flush();
  if(rv != 0) {
1298 1299
    SSLOG(FATAL, this) << "evbuffer_add() failed";
    return -1;
1300
  }
1301 1302 1303

  if(nghttp2_session_want_read(session_) == 0 &&
     nghttp2_session_want_write(session_) == 0 &&
Tatsuhiro Tsujikawa's avatar
Tatsuhiro Tsujikawa committed
1304
     evbuffer_get_length(output) == 0) {
1305 1306
    if(LOG_ENABLED(INFO)) {
      SSLOG(INFO, this) << "No more read/write for this session";
1307
    }
1308
    return -1;
1309
  }
1310
  return 0;
1311 1312
}

1313
void Http2Session::clear_notify()
1314
{
1315
  auto input = bufferevent_get_output(rdbev_);
1316 1317 1318
  if(evbuffer_drain(input, evbuffer_get_length(input)) != 0) {
    SSLOG(FATAL, this) << "evbuffer_drain() failed";
  }
1319 1320 1321
  notified_ = false;
}

1322
void Http2Session::notify()
1323 1324
{
  if(!notified_) {
1325 1326 1327
    if(bufferevent_write(wrbev_, "1", 1) != 0) {
      SSLOG(FATAL, this) << "bufferevent_write failed";
    }
1328 1329 1330 1331
    notified_ = true;
  }
}

1332
bufferevent* Http2Session::get_bev() const
1333 1334 1335 1336
{
  return bev_;
}

1337
int Http2Session::get_state() const
1338 1339 1340 1341
{
  return state_;
}

1342
void Http2Session::set_state(int state)
1343 1344 1345 1346
{
  state_ = state;
}

1347
int Http2Session::terminate_session(nghttp2_error_code error_code)
1348 1349
{
  int rv;
1350
  rv = nghttp2_session_terminate_session(session_, error_code);
1351 1352 1353 1354 1355 1356
  if(rv != 0) {
    return -1;
  }
  return 0;
}

1357 1358 1359 1360 1361 1362 1363 1364 1365
size_t Http2Session::get_outbuf_length() const
{
  if(bev_) {
    return evbuffer_get_length(bufferevent_get_output(bev_));
  } else {
    return OUTBUF_MAX_THRES;
  }
}

1366
} // namespace shrpx