blob: 878c040ef721ce536045ab33440fd94be4a461ac [file] [log] [blame]
#include "http2/adapter/nghttp2_adapter.h"
#include "absl/algorithm/container.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "http2/adapter/nghttp2_callbacks.h"
#include "third_party/nghttp2/src/lib/includes/nghttp2/nghttp2.h"
#include "common/platform/api/quiche_logging.h"
#include "common/quiche_endian.h"
namespace http2 {
namespace adapter {
/* static */
std::unique_ptr<NgHttp2Adapter> NgHttp2Adapter::CreateClientAdapter(
Http2VisitorInterface& visitor) {
auto adapter = new NgHttp2Adapter(visitor, Perspective::kClient);
adapter->Initialize();
return absl::WrapUnique(adapter);
}
/* static */
std::unique_ptr<NgHttp2Adapter> NgHttp2Adapter::CreateServerAdapter(
Http2VisitorInterface& visitor) {
auto adapter = new NgHttp2Adapter(visitor, Perspective::kServer);
adapter->Initialize();
return absl::WrapUnique(adapter);
}
ssize_t NgHttp2Adapter::ProcessBytes(absl::string_view bytes) {
const ssize_t processed_bytes = session_->ProcessBytes(bytes);
if (processed_bytes < 0) {
visitor_.OnConnectionError();
}
return processed_bytes;
}
void NgHttp2Adapter::SubmitSettings(absl::Span<const Http2Setting> settings) {
// Submit SETTINGS, converting each Http2Setting to an nghttp2_settings_entry.
std::vector<nghttp2_settings_entry> nghttp2_settings;
absl::c_transform(settings, std::back_inserter(nghttp2_settings),
[](const Http2Setting& setting) {
return nghttp2_settings_entry{setting.id, setting.value};
});
nghttp2_submit_settings(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
nghttp2_settings.data(), nghttp2_settings.size());
}
void NgHttp2Adapter::SubmitPriorityForStream(Http2StreamId stream_id,
Http2StreamId parent_stream_id,
int weight,
bool exclusive) {
nghttp2_priority_spec priority_spec;
nghttp2_priority_spec_init(&priority_spec, parent_stream_id, weight,
static_cast<int>(exclusive));
nghttp2_submit_priority(session_->raw_ptr(), NGHTTP2_FLAG_NONE, stream_id,
&priority_spec);
}
void NgHttp2Adapter::SubmitPing(Http2PingId ping_id) {
uint8_t opaque_data[8] = {};
Http2PingId ping_id_to_serialize = quiche::QuicheEndian::HostToNet64(ping_id);
std::memcpy(opaque_data, &ping_id_to_serialize, sizeof(Http2PingId));
nghttp2_submit_ping(session_->raw_ptr(), NGHTTP2_FLAG_NONE, opaque_data);
}
void NgHttp2Adapter::SubmitGoAway(Http2StreamId last_accepted_stream_id,
Http2ErrorCode error_code,
absl::string_view opaque_data) {
nghttp2_submit_goaway(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
last_accepted_stream_id,
static_cast<uint32_t>(error_code),
ToUint8Ptr(opaque_data.data()), opaque_data.size());
}
void NgHttp2Adapter::SubmitWindowUpdate(Http2StreamId stream_id,
int window_increment) {
nghttp2_submit_window_update(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
stream_id, window_increment);
}
void NgHttp2Adapter::SubmitMetadata(Http2StreamId stream_id,
bool end_metadata) {
QUICHE_LOG(DFATAL) << "Not implemented";
}
std::string NgHttp2Adapter::GetBytesToWrite(absl::optional<size_t> max_bytes) {
ssize_t num_bytes = 0;
std::string result;
do {
const uint8_t* data = nullptr;
num_bytes = nghttp2_session_mem_send(session_->raw_ptr(), &data);
if (num_bytes > 0) {
absl::StrAppend(
&result,
absl::string_view(reinterpret_cast<const char*>(data), num_bytes));
} else if (num_bytes < 0) {
visitor_.OnConnectionError();
}
} while (num_bytes > 0);
return result;
}
int NgHttp2Adapter::GetPeerConnectionWindow() const {
return session_->GetRemoteWindowSize();
}
void NgHttp2Adapter::MarkDataConsumedForStream(Http2StreamId stream_id,
size_t num_bytes) {
int rc = session_->Consume(stream_id, num_bytes);
if (rc != 0) {
QUICHE_LOG(ERROR) << "Error " << rc << " marking " << num_bytes
<< " bytes consumed for stream " << stream_id;
}
}
void NgHttp2Adapter::SubmitRst(Http2StreamId stream_id,
Http2ErrorCode error_code) {
int status =
nghttp2_submit_rst_stream(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
stream_id, static_cast<uint32_t>(error_code));
if (status < 0) {
QUICHE_LOG(WARNING) << "Reset stream failed: " << stream_id
<< " with status code " << status;
}
}
NgHttp2Adapter::NgHttp2Adapter(Http2VisitorInterface& visitor,
Perspective perspective)
: Http2Adapter(visitor), visitor_(visitor), perspective_(perspective) {}
NgHttp2Adapter::~NgHttp2Adapter() {}
void NgHttp2Adapter::Initialize() {
nghttp2_option* options;
nghttp2_option_new(&options);
// Set some common options for compatibility.
nghttp2_option_set_no_closed_streams(options, 1);
nghttp2_option_set_no_auto_window_update(options, 1);
nghttp2_option_set_max_send_header_block_length(options, 0x2000000);
nghttp2_option_set_max_outbound_ack(options, 10000);
session_ =
absl::make_unique<NgHttp2Session>(perspective_, callbacks::Create(),
options, static_cast<void*>(&visitor_));
}
} // namespace adapter
} // namespace http2