| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 53b05e2 commit 098e3d7
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -448,9 +448,13 @@ void Session::Application::SendPendingData() { | |||
| 448 | 448 | ||
| 449 | 449 | // Awesome, let's write our packet! | |
| 450 | 450 | PacketInfo pi; | |
| 451 | - ssize_t nwrite = WriteVStream( | ||
| 452 | - &path, &pi, packet->data(), &ndatalen, packet->length(), | ||
| 453 | - stream_data, ts); | ||
| 451 | + ssize_t nwrite = WriteVStream(&path, | ||
| 452 | + &pi, | ||
| 453 | + packet->data(), | ||
| 454 | + &ndatalen, | ||
| 455 | + packet->length(), | ||
| 456 | + stream_data, | ||
| 457 | + ts); | ||
| 454 | 458 | ||
| 455 | 459 | // When ndatalen is > 0, that's our indication that stream data was accepted | |
| 456 | 460 | // in to the packet. Yay! | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -311,10 +311,18 @@ class Endpoint::UDP::Impl final : public HandleWrap { | |||
| 311 | 311 | SET_SELF_SIZE(Impl) | |
| 312 | 312 | ||
| 313 | 313 | private: | |
| 314 | + // Pre-allocated receive buffer. Reused across all datagrams because | ||
| 315 | + // ngtcp2_conn_read_pkt is synchronous — it copies what it needs and | ||
| 316 | + // does not retain a reference to the buffer after returning. This | ||
| 317 | + // eliminates a malloc(64KB)/free(64KB) cycle per received datagram. | ||
| 318 | + static constexpr size_t kRecvBufferSize = 65536; // UV__UDP_DGRAM_MAXSIZE | ||
| 319 | + char recv_buf_[kRecvBufferSize]; | ||
| 320 | + | ||
| 314 | 321 | static void OnAlloc(uv_handle_t* handle, | |
| 315 | 322 | size_t suggested_size, | |
| 316 | 323 | uv_buf_t* buf) { | |
| 317 | - *buf = From(handle)->env()->allocate_managed_buffer(suggested_size); | ||
| 324 | + auto* impl = From(handle); | ||
| 325 | + *buf = uv_buf_init(impl->recv_buf_, kRecvBufferSize); | ||
| 318 | 326 | } | |
| 319 | 327 | ||
| 320 | 328 | static void OnReceive(uv_udp_t* handle, | |
@@ -326,26 +334,22 @@ class Endpoint::UDP::Impl final : public HandleWrap { | |||
| 326 | 334 | DCHECK_NOT_NULL(impl); | |
| 327 | 335 | DCHECK_NOT_NULL(impl->endpoint_); | |
| 328 | 336 | ||
| 329 | - auto release_buf = [&]() { | ||
| 330 | - if (buf->base != nullptr) impl->env()->release_managed_buffer(*buf); | ||
| 331 | - }; | ||
| 332 | - | ||
| 333 | 337 | // Nothing to do in these cases. Specifically, if the nread | |
| 334 | 338 | // is zero or we have received a partial packet, we are just | |
| 335 | - // going to ignore it. | ||
| 339 | + // going to ignore it. No buffer release needed — recv_buf_ | ||
| 340 | + // is pre-allocated and reused. | ||
| 336 | 341 | if (nread == 0 || flags & UV_UDP_PARTIAL) { | |
| 337 | - release_buf(); | ||
| 338 | 342 | return; | |
| 339 | 343 | } | |
| 340 | 344 | ||
| 341 | 345 | if (nread < 0) { | |
| 342 | - release_buf(); | ||
| 343 | 346 | impl->endpoint_->Destroy(CloseContext::RECEIVE_FAILURE, | |
| 344 | 347 | static_cast<int>(nread)); | |
| 345 | 348 | return; | |
| 346 | 349 | } | |
| 347 | 350 | ||
| 348 | - impl->endpoint_->Receive(uv_buf_init(buf->base, static_cast<size_t>(nread)), | ||
| 351 | + impl->endpoint_->Receive(reinterpret_cast<const uint8_t*>(buf->base), | ||
| 352 | + static_cast<size_t>(nread), | ||
| 349 | 353 | SocketAddress(addr)); | |
| 350 | 354 | } | |
| 351 | 355 | ||
@@ -1264,24 +1268,25 @@ void Endpoint::CloseGracefully() { | |||
| 1264 | 1268 | MaybeDestroy(); | |
| 1265 | 1269 | } | |
| 1266 | 1270 | ||
| 1267 | - void Endpoint::Receive(const uv_buf_t& buf, | ||
| 1271 | + void Endpoint::Receive(const uint8_t* data, | ||
| 1272 | + size_t len, | ||
| 1268 | 1273 | const SocketAddress& remote_address) { | |
| 1269 | 1274 | const auto receive = [&](Session* session, | |
| 1270 | - Store&& store, | ||
| 1275 | + const uint8_t* pkt_data, | ||
| 1276 | + size_t pkt_len, | ||
| 1271 | 1277 | const SocketAddress& local_address, | |
| 1272 | 1278 | const SocketAddress& remote_address, | |
| 1273 | 1279 | const CID& dcid, | |
| 1274 | 1280 | const CID& scid) { | |
| 1275 | 1281 | DCHECK_NOT_NULL(session); | |
| 1276 | 1282 | if (session->is_destroyed()) return; | |
| 1277 | - size_t len = store.length(); | ||
| 1278 | 1283 | // Use ReadPacket (no SendPendingDataScope) so that multiple packets | |
| 1279 | 1284 | // received in the same I/O burst are processed before any responses | |
| 1280 | 1285 | // are generated. The deferred flush via BindingData's uv_check | |
| 1281 | 1286 | // callback calls SendPendingData once per dirty session after all | |
| 1282 | 1287 | // packets in the burst have been read. | |
| 1283 | - if (session->ReadPacket(std::move(store), local_address, remote_address)) { | ||
| 1284 | - STAT_INCREMENT_N(Stats, bytes_received, len); | ||
| 1288 | + if (session->ReadPacket(pkt_data, pkt_len, local_address, remote_address)) { | ||
| 1289 | + STAT_INCREMENT_N(Stats, bytes_received, pkt_len); | ||
| 1285 | 1290 | STAT_INCREMENT(Stats, packets_received); | |
| 1286 | 1291 | } | |
| 1287 | 1292 | // Schedule the session for deferred SendPendingData if it hasn't | |
@@ -1293,7 +1298,9 @@ void Endpoint::Receive(const uv_buf_t& buf, | |||
| 1293 | 1298 | } | |
| 1294 | 1299 | }; | |
| 1295 | 1300 | ||
| 1296 | - const auto accept = [&](const Session::Config& config, Store&& store) { | ||
| 1301 | + const auto accept = [&](const Session::Config& config, | ||
| 1302 | + const uint8_t* pkt_data, | ||
| 1303 | + size_t pkt_len) { | ||
| 1297 | 1304 | // One final check. If the endpoint is closed, closing, or is not listening | |
| 1298 | 1305 | // as a server, then we cannot accept the initial packet. | |
| 1299 | 1306 | if (is_closed() || is_closing() || !is_listening()) return; | |
@@ -1323,7 +1330,8 @@ void Endpoint::Receive(const uv_buf_t& buf, | |||
| 1323 | 1330 | return; | |
| 1324 | 1331 | ||
| 1325 | 1332 | receive(session.get(), | |
| 1326 | - std::move(store), | ||
| 1333 | + pkt_data, | ||
| 1334 | + pkt_len, | ||
| 1327 | 1335 | config.local_address, | |
| 1328 | 1336 | config.remote_address, | |
| 1329 | 1337 | config.dcid, | |
@@ -1333,7 +1341,8 @@ void Endpoint::Receive(const uv_buf_t& buf, | |||
| 1333 | 1341 | const auto acceptInitialPacket = [&](const uint32_t version, | |
| 1334 | 1342 | const CID& dcid, | |
| 1335 | 1343 | const CID& scid, | |
| 1336 | - Store&& store, | ||
| 1344 | + const uint8_t* pkt_data, | ||
| 1345 | + size_t pkt_len, | ||
| 1337 | 1346 | const SocketAddress& local_address, | |
| 1338 | 1347 | const SocketAddress& remote_address) { | |
| 1339 | 1348 | // If we're not listening as a server, do not accept an initial packet. | |
@@ -1343,8 +1352,7 @@ void Endpoint::Receive(const uv_buf_t& buf, | |||
| 1343 | 1352 | ||
| 1344 | 1353 | // This is our first condition check... A minimal check to see if ngtcp2 can | |
| 1345 | 1354 | // even recognize this packet as a quic packet. | |
| 1346 | - ngtcp2_vec vec = store; | ||
| 1347 | - if (ngtcp2_accept(&hd, vec.base, vec.len) != NGTCP2_SUCCESS) { | ||
| 1355 | + if (ngtcp2_accept(&hd, pkt_data, pkt_len) != NGTCP2_SUCCESS) { | ||
| 1348 | 1356 | // Per the ngtcp2 docs, ngtcp2_accept returns 0 if the check was | |
| 1349 | 1357 | // successful, or an error code if it was not. Currently there's only one | |
| 1350 | 1358 | // documented error code (NGTCP2_ERR_INVALID_ARGUMENT) but we'll handle | |
@@ -1582,7 +1590,7 @@ void Endpoint::Receive(const uv_buf_t& buf, | |||
| 1582 | 1590 | } | |
| 1583 | 1591 | } | |
| 1584 | 1592 | ||
| 1585 | - accept(config, std::move(store)); | ||
| 1593 | + accept(config, pkt_data, pkt_len); | ||
| 1586 | 1594 | }; | |
| 1587 | 1595 | ||
| 1588 | 1596 | // When a received packet contains a QUIC short header but cannot be matched | |
@@ -1598,35 +1606,37 @@ void Endpoint::Receive(const uv_buf_t& buf, | |||
| 1598 | 1606 | // possible to avoid a DOS vector. | |
| 1599 | 1607 | const auto maybeStatelessReset = [&](const CID& dcid, | |
| 1600 | 1608 | const CID& scid, | |
| 1601 | - Store& store, | ||
| 1609 | + const uint8_t* pkt_data, | ||
| 1610 | + size_t pkt_len, | ||
| 1602 | 1611 | const SocketAddress& local_address, | |
| 1603 | 1612 | const SocketAddress& remote_address) { | |
| 1604 | 1613 | // Support for stateless resets can be disabled by the application. If that | |
| 1605 | 1614 | // case, or if the packet is too short to contain a reset token, then we | |
| 1606 | 1615 | // skip the remaining checks. | |
| 1607 | 1616 | if (options_.disable_stateless_reset || | |
| 1608 | - store.length() < NGTCP2_STATELESS_RESET_TOKENLEN) { | ||
| 1617 | + pkt_len < NGTCP2_STATELESS_RESET_TOKENLEN) { | ||
| 1609 | 1618 | return false; | |
| 1610 | 1619 | } | |
| 1611 | 1620 | ||
| 1612 | 1621 | // The stateless reset token itself is the *final* | |
| 1613 | 1622 | // NGTCP2_STATELESS_RESET_TOKENLEN bytes in the received packet. If it is a | |
| 1614 | 1623 | // stateless reset then then rest of the bytes in the packet are garbage | |
| 1615 | 1624 | // that we'll ignore. | |
| 1616 | - ngtcp2_vec vec = store; | ||
| 1617 | - vec.base += (vec.len - NGTCP2_STATELESS_RESET_TOKENLEN); | ||
| 1625 | + const uint8_t* token_pos = | ||
| 1626 | + pkt_data + (pkt_len - NGTCP2_STATELESS_RESET_TOKENLEN); | ||
| 1618 | 1627 | ||
| 1619 | 1628 | // If a Session has been associated with the token, then it is a valid | |
| 1620 | 1629 | // stateless reset token. We need to dispatch it to the session to be | |
| 1621 | 1630 | // processed. | |
| 1622 | 1631 | auto* session = session_manager().FindSessionByStatelessResetToken( | |
| 1623 | - StatelessResetToken(vec.base)); | ||
| 1632 | + StatelessResetToken(token_pos)); | ||
| 1624 | 1633 | if (session != nullptr) { | |
| 1625 | 1634 | // If the session happens to have been destroyed already, we'll | |
| 1626 | 1635 | // just ignore the packet. | |
| 1627 | 1636 | if (!session->is_destroyed()) [[likely]] { | |
| 1628 | 1637 | receive(session, | |
| 1629 | - std::move(store), | ||
| 1638 | + pkt_data, | ||
| 1639 | + pkt_len, | ||
| 1630 | 1640 | local_address, | |
| 1631 | 1641 | remote_address, | |
| 1632 | 1642 | dcid, | |
@@ -1654,22 +1664,8 @@ void Endpoint::Receive(const uv_buf_t& buf, | |||
| 1654 | 1664 | // return; | |
| 1655 | 1665 | // } | |
| 1656 | 1666 | ||
| 1657 | - Debug(this, "Received %zu-byte packet from %s", buf.len, remote_address); | ||
| 1658 | - | ||
| 1659 | - // The managed buffer here contains the received packet. We do not yet know | ||
| 1660 | - // at this point if it is a valid QUIC packet. We need to do some basic | ||
| 1661 | - // checks. It is critical at this point that we do as little work as possible | ||
| 1662 | - // to avoid a DOS vector. | ||
| 1663 | - std::shared_ptr<BackingStore> backing = env()->release_managed_buffer(buf); | ||
| 1664 | - if (!backing) [[unlikely]] { | ||
| 1665 | - // At this point something bad happened and we need to treat this as a fatal | ||
| 1666 | - // case. There's likely no way to test this specific condition reliably. | ||
| 1667 | - return Destroy(CloseContext::RECEIVE_FAILURE, UV_ENOMEM); | ||
| 1668 | - } | ||
| 1669 | - | ||
| 1670 | - Store store(std::move(backing), buf.len, 0); | ||
| 1667 | + Debug(this, "Received %zu-byte packet from %s", len, remote_address); | ||
| 1671 | 1668 | ||
| 1672 | - ngtcp2_vec vec = store; | ||
| 1673 | 1669 | ngtcp2_version_cid pversion_cid; | |
| 1674 | 1670 | ||
| 1675 | 1671 | // This is our first check to see if the received data can be processed as a | |
@@ -1678,7 +1674,7 @@ void Endpoint::Receive(const uv_buf_t& buf, | |||
| 1678 | 1674 | // valid QUIC header but there is still no guarantee that the packet can be | |
| 1679 | 1675 | // successfully processed. | |
| 1680 | 1676 | switch (ngtcp2_pkt_decode_version_cid( | |
| 1681 | - &pversion_cid, vec.base, vec.len, NGTCP2_MAX_CIDLEN)) { | ||
| 1677 | + &pversion_cid, data, len, NGTCP2_MAX_CIDLEN)) { | ||
| 1682 | 1678 | case 0: | |
| 1683 | 1679 | break; // Supported version, continue processing. | |
| 1684 | 1680 | case NGTCP2_ERR_VERSION_NEGOTIATION: { | |
@@ -1756,7 +1752,7 @@ void Endpoint::Receive(const uv_buf_t& buf, | |||
| 1756 | 1752 | // necessary here. We want to return immediately without committing any | |
| 1757 | 1753 | // further resources. | |
| 1758 | 1754 | if (pversion_cid.version == 0 && | |
| 1759 | - maybeStatelessReset(dcid, scid, store, addr, remote_address)) { | ||
| 1755 | + maybeStatelessReset(dcid, scid, data, len, addr, remote_address)) { | ||
| 1760 | 1756 | Debug(this, "Packet was a stateless reset"); | |
| 1761 | 1757 | return; // Stateless reset! Don't do any further processing. | |
| 1762 | 1758 | } | |
@@ -1771,17 +1767,13 @@ void Endpoint::Receive(const uv_buf_t& buf, | |||
| 1771 | 1767 | SendStatelessReset( | |
| 1772 | 1768 | PathDescriptor{ | |
| 1773 | 1769 | pversion_cid.version, dcid, scid, addr, remote_address}, | |
| 1774 | - store.length()); | ||
| 1770 | + len); | ||
| 1775 | 1771 | return; | |
| 1776 | 1772 | } | |
| 1777 | 1773 | ||
| 1778 | 1774 | // Process the packet as an initial packet... | |
| 1779 | - return acceptInitialPacket(pversion_cid.version, | ||
| 1780 | - dcid, | ||
| 1781 | - scid, | ||
| 1782 | - std::move(store), | ||
| 1783 | - addr, | ||
| 1784 | - remote_address); | ||
| 1775 | + return acceptInitialPacket( | ||
| 1776 | + pversion_cid.version, dcid, scid, data, len, addr, remote_address); | ||
| 1785 | 1777 | } | |
| 1786 | 1778 | ||
| 1787 | 1779 | if (session->is_destroyed()) [[unlikely]] { | |
@@ -1793,7 +1785,7 @@ void Endpoint::Receive(const uv_buf_t& buf, | |||
| 1793 | 1785 | // If we got here, the dcid matched the scid of a known local session. Yay! | |
| 1794 | 1786 | // The session will take over any further processing of the packet. | |
| 1795 | 1787 | Debug(this, "Dispatching packet to known session"); | |
| 1796 | - receive(session.get(), std::move(store), addr, remote_address, dcid, scid); | ||
| 1788 | + receive(session.get(), data, len, addr, remote_address, dcid, scid); | ||
| 1797 | 1789 | ||
| 1798 | 1790 | // It is important to note that the session may have been destroyed during | |
| 1799 | 1791 | // the call to receive(...). If that's the case, the session object still | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -429,7 +429,7 @@ class Endpoint final : public AsyncWrap, public Packet::Listener { | |||
| 429 | 429 | // Ref() causes a listening Endpoint to keep the event loop active. | |
| 430 | 430 | JS_METHOD(Ref); | |
| 431 | 431 | ||
| 432 | - void Receive(const uv_buf_t& buf, const SocketAddress& from); | ||
| 432 | + void Receive(const uint8_t* data, size_t len, const SocketAddress& from); | ||
| 433 | 433 | ||
| 434 | 434 | AliasedStruct<Stats> stats_; | |
| 435 | 435 | AliasedStruct<State> state_; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2109,7 +2109,8 @@ void Session::SetLastError(QuicError&& error) { | |||
| 2109 | 2109 | impl_->last_error_ = std::move(error); | |
| 2110 | 2110 | } | |
| 2111 | 2111 | ||
| 2112 | - bool Session::Receive(Store&& store, | ||
| 2112 | + bool Session::Receive(const uint8_t* data, | ||
| 2113 | + size_t len, | ||
| 2113 | 2114 | const SocketAddress& local_address, | |
| 2114 | 2115 | const SocketAddress& remote_address, | |
| 2115 | 2116 | const PacketInfo& pkt_info, | |
@@ -2120,24 +2121,23 @@ bool Session::Receive(Store&& store, | |||
| 2120 | 2121 | // The hot receive path uses ReadPacket() directly with deferred | |
| 2121 | 2122 | // flush via BindingData's uv_check callback. | |
| 2122 | 2123 | SendPendingDataScope send_scope(this); | |
| 2123 | - return ReadPacket( | ||
| 2124 | - std::move(store), local_address, remote_address, pkt_info, ts); | ||
| 2124 | + return ReadPacket(data, len, local_address, remote_address, pkt_info, ts); | ||
| 2125 | 2125 | } | |
| 2126 | 2126 | ||
| 2127 | - bool Session::ReadPacket(Store&& store, | ||
| 2127 | + bool Session::ReadPacket(const uint8_t* data, | ||
| 2128 | + size_t len, | ||
| 2128 | 2129 | const SocketAddress& local_address, | |
| 2129 | 2130 | const SocketAddress& remote_address, | |
| 2130 | 2131 | const PacketInfo& pkt_info, | |
| 2131 | 2132 | uint64_t ts) { | |
| 2132 | 2133 | DCHECK(!is_destroyed()); | |
| 2133 | 2134 | impl_->remote_address_ = remote_address; | |
| 2134 | 2135 | ||
| 2135 | - ngtcp2_vec vec = store; | ||
| 2136 | 2136 | Path path(local_address, remote_address); | |
| 2137 | 2137 | ||
| 2138 | 2138 | Debug(this, | |
| 2139 | 2139 | "Session is receiving %zu-byte packet received along path %s", | |
| 2140 | - vec.len, | ||
| 2140 | + len, | ||
| 2141 | 2141 | path); | |
| 2142 | 2142 | ||
| 2143 | 2143 | // It is important to understand that reading the packet will cause | |
@@ -2158,19 +2158,18 @@ bool Session::ReadPacket(Store&& store, | |||
| 2158 | 2158 | // receive path caches a timestamp and passes it to all ReadPacket() | |
| 2159 | 2159 | // calls in the same I/O burst. | |
| 2160 | 2160 | if (ts == 0) ts = uv_hrtime(); | |
| 2161 | - err = ngtcp2_conn_read_pkt( | ||
| 2162 | - *this, &path, pkt_info, vec.base, vec.len, ts); | ||
| 2161 | + err = ngtcp2_conn_read_pkt(*this, &path, pkt_info, data, len, ts); | ||
| 2163 | 2162 | } | |
| 2164 | 2163 | if (is_destroyed()) return false; | |
| 2165 | 2164 | ||
| 2166 | - Debug(this, "Session receiving %zu-byte packet with result %d", vec.len, err); | ||
| 2165 | + Debug(this, "Session receiving %zu-byte packet with result %d", len, err); | ||
| 2167 | 2166 | ||
| 2168 | 2167 | switch (err) { | |
| 2169 | 2168 | case 0: { | |
| 2170 | - Debug(this, "Session successfully received %zu-byte packet", vec.len); | ||
| 2169 | + Debug(this, "Session successfully received %zu-byte packet", len); | ||
| 2171 | 2170 | if (!is_destroyed()) [[likely]] { | |
| 2172 | 2171 | auto& stats_ = impl_->stats_; | |
| 2173 | - STAT_INCREMENT_N(Stats, bytes_received, vec.len); | ||
| 2172 | + STAT_INCREMENT_N(Stats, bytes_received, len); | ||
| 2174 | 2173 | // Process deferred operations that couldn't run inside callback | |
| 2175 | 2174 | // scopes (e.g., HTTP/3 GOAWAY handling that calls into JS). | |
| 2176 | 2175 | application().PostReceive(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -353,7 +353,8 @@ class Session final : public AsyncWrap, private SessionTicket::AppData::Source { | |||
| 353 | 353 | bool early = false; | |
| 354 | 354 | }; | |
| 355 | 355 | ||
| 356 | - bool Receive(Store&& store, | ||
| 356 | + bool Receive(const uint8_t* data, | ||
| 357 | + size_t len, | ||
| 357 | 358 | const SocketAddress& local_address, | |
| 358 | 359 | const SocketAddress& remote_address, | |
| 359 | 360 | const PacketInfo& pkt_info = PacketInfo(), | |
@@ -367,10 +368,14 @@ class Session final : public AsyncWrap, private SessionTicket::AppData::Source { | |||
| 367 | 368 | // Receive() is kept as a convenience wrapper that calls ReadPacket() | |
| 368 | 369 | // then triggers SendPendingData (for paths like Connect that need | |
| 369 | 370 | // immediate response). | |
| 371 | + // The data pointer is used synchronously — ngtcp2_conn_read_pkt does | ||
| 372 | + // not retain a reference after returning, so the caller's buffer can | ||
| 373 | + // be reused immediately. | ||
| 370 | 374 | // When ts is 0 (the default), uv_hrtime() is called internally. | |
| 371 | 375 | // The batched receive path caches a timestamp and passes it to all | |
| 372 | 376 | // ReadPacket() calls in the same I/O burst. | |
| 373 | - bool ReadPacket(Store&& store, | ||
| 377 | + bool ReadPacket(const uint8_t* data, | ||
| 378 | + size_t len, | ||
| 374 | 379 | const SocketAddress& local_address, | |
| 375 | 380 | const SocketAddress& remote_address, | |
| 376 | 381 | const PacketInfo& pkt_info = PacketInfo(), | |
| Back | FazBrowse Home | New Git URL |
0 commit comments