From ee2ee837a46a87217ea45c3eef74d86d3417c0f8 Mon Sep 17 00:00:00 2001 From: Masakazu Kitajo Date: Tue, 29 Jun 2021 16:32:37 +0900 Subject: [PATCH] Squashed commit of the following: commit d232a12ec5ae461235f4a4d6f7c7644d05651aed Merge: 837bd0e41 2edeae477 Author: Masakazu Kitajo Date: Tue Jun 29 15:41:34 2021 +0900 Merge branch 'master' into quic-latest * master: reuse multiple times (#7992) Test bad request behavior (#7884) Fix BoringSSL build (#8001) Update TSHttpTxnAborted API to distinguish client/server aborts (#7901) Enforce case for well known methods (#7886) Add null checks for http_load (#7995) commit 837bd0e413c27b4f2132864c9a0a377a45fabaf5 Author: Masakazu Kitajo Date: Mon Jun 28 15:11:03 2021 +0900 Fix unit tests for QUICStreamState commit c5bb9e0dd41cba2198c5632f85f89f9b343eee34 Merge: 0a63fa977 202b2505c Author: Masakazu Kitajo Date: Mon Jun 28 10:02:54 2021 +0900 Merge branch 'master' into quic-latest * master: Implement TLSBasicSupport for QUICNetVC (#7959) Reload server session inactivity timeout before placing a session into the pool (#7618) Use OpeSSL EVP API if SHA1 API is unavailable (cache_promote) (#7447) Cleanup: Get rid of HTTP2_SESSION_EVENT_RECV (#7879) Timing and permissions update for regex_revalidate test (#7998) limit m_current_range to max value in RangeTransform (#4843) Allow to TLS handshake to error out on TSVConnReenable (#7994) Cleanup: Get rid of HTTP2_SESSION_EVENT_INIT (#7878) Add hook for loading certificate and key data from plugin (#6609) Doc: Now's Minute invocation error (#7990) Fix typo in configure.ac (#7993) commit 0a63fa977f97919143f45508f1f6c5b656324d80 Merge: 312cf393c bd93f2a40 Author: Masakazu Kitajo Date: Fri Jun 25 14:34:55 2021 +0900 Merge branch 'master' into quic-latest * master: Don't rely on SSLNetVC when HttpSM gathers info about SSL (#7961) conf_remap: demote 'Invalid configuration' to warning (#7991) Cleans up the code bit, including milliseconds consistency (#7989) Pass through expect header and handle 100-continue response (#7962) Treat TRACE with body as bad request (#7905) Thread safe Mersenne Twister 64 using c++11 (#7859) ESI plugin documentation updates. (#7970) Add log name configuration and stderr/stdout support. (#7937) Cleanup: Constify MIMEHdr (#7949) Fixed compile error with Linux AIO unit test (#7958) Note YAML parser library bug, and work-around, in documentation. (#7963) Ensure that the content-length value is only digits (#7964) String the url fragment for outgoing requests (#7966) Fix for HTTP/2 frames (#7965) Improve parsing error messages for strategies.yaml. (#7948) fix the scheme of h2 0rtt tests (#7957) Fix double test flakiness due to EOS/TXN_CLOSE race (#7956) Use proxy.config.log.hostname for rotated log filenames (#7943) Fixed memory leak in the QUIC stream manager (#7951) Fixup TS_USE_LINUX_NATIVE_AIO AIO_MODE_NATIVE (#7832) Update GitHub stale action to auto close old PRs (#7952) Revert "Do not invalidate cached resources upon error responses to unsafe methods (#7864)" (#7954) regex_revalidate: add stats for miss/stale counts (#7950) Do not invalidate cached resources upon error responses to unsafe methods (#7864) Add an HTTP/2 304 "Not Modified" AuTest. (#7882) regex_revalidate: optionally retain rule epoch state across restarts (#7939) Fixed memory leak in QUIC ack frame unit test (#7947) cache_promote: Don't promote on uncacheable requests (#7942) Fix dynamic-stack-buffer-overflow of cachekey plugin (#7945) Compilation error fixes for QUIC unit tests (#7944) Adds bytes counting as a trigger to the cache_promote LRU (#7765) Add a JSON schema for strategies.yaml (#7932) Remove second call to TRANSACT_RETURN while handling cache write lock (#7873) Close connection after every bad request for HTTP/1.1 (#7885) Pin Sphinx to 3.x to unblock `make html` (#7940) Add support for Remap rule hit stats (#7936) Remove scrap log object dead code (#7935) Add STL forward iterators to DLL container. (#7934) Add log SQUID code testing to redirect.test.py Au test. (#7870) Fix race condition on server session state (#7921) regex_reval: bug where rule type is always reported as the first (#7928) Remove duplicate entry in overridable txn vars. (#7930) Satisfy ci/jenkins/bin/clang-format.sh (#7929) Add a basic Au test using strategies.yaml, with consistent hashing. (#7911) Add a chunked negative revalidating test. (#7907) Ensure that URL components are valid when alternate eviction is logged (#7924) fix grammar (#7927) AuTest: Enable h2spec generic test cases (#7926) Adjust vc read errors (#7923) Remove bucket search from IntrusiveHashMap::erase (#7848) Ensure TS_VCONN_CLOSE_HOOK hook is called during TS_EVENT_VCONN_CLOSE. (#7913) Update docs languages file to add 9.1.x for en and ja (#7917) * Adds a new peering ring mode to next hop selection strategies. (#7897) Add Au test for strategies.yaml, with consistent hashing, with fallover. (#7914) Make HttpSM server reference a Transaction instead of a Session (#7849) Set accept_options of Http1Transaction in Http1ClientSession::new_connection() (#7894) Reset Http1Transaction before adding vc to keep_alive_queue (#7892) Add dead server policy control and metric. Improve messages. (#7757) Ensure the HTTP protion of the protocol string is upper case (#7904) Fixed spelling mistakes in the docs (#7896) add MISS capability to the regex_revalidate plugin (#7899) docs: fix capitalization of Linux (#7898) Redirect - Make TS to honour the number_of_redirections configuration value (#7867) Clean up producer more regularly (#7386) Fix crash in open_close_h2 (#7586) Cleanup Http2ClientSession SessionHandler (#7876) Enforce HTTP parsing restrictions on HTTP versions supported (#7875) Do not delete the continuation twice (#7862) Cleanup: refer Http2ClientSession::mutex (#7853) Autest - Proxy Verifier Extension, add context template $-base string substitution in the replay file. (#7866) Fixed some spelling mistakes in comments (#7869) Fixed ASAN issues with MMH test (#7868) Cleanup: Move member functions defined inside of class definitions of Http2ConnectionState & Http2ConnectionSettings (#7854) Add URI Signing cdnistd Claim Implementation (#7822) Adds a new --enable-all-asserts configure option (#7858) Unifdef test code for MMH and moved it into its own test file (#7841) Clean up lua plugin doc for overridable configurations (#7844) Save and propagate epoll network error (#7809) Add method to write an IpAddr value to a sockaddr. (#7821) Add proxy.config.http.max_proxy_cycles (#7657) Update NextHop strategies so that unavailable server retry codes (#7837) generator: allow for POST requests (#7635) Fixed double declaration types for log buffer tracking (#7847) Extra braces for clang 5 / ubuntu 16.04 on array initialization (#7842) Conflicts: iocore/net/quic/QUICStreamFactory.cc commit 312cf393c170bbf1ee6c945907868137197afdfa Merge: f90e8dde9 5cdc1459f Author: Masakazu Kitajo Date: Mon May 17 10:07:42 2021 +0900 Merge branch 'master' into quic-latest * master: Get rid of code for OpenSSL that has old QUIC API (#7599) Fixed warning in gcc 11 about array not being initalized (#7840) Don't call next next dup on destroyed mime field mloc. (#7833) build_h3_tools: use OpenSSL_1_1_1k+quic (#7836) Address assert on captive_action (#7807) Fix so EOS are delivered to sessions in the pool (#7828) Fix a format specifier for size_t (#7830) Fix stall on sending response for request with trailer header (#7831) Simplification dir_init_done (#7817) Remove unused member from HttpSM (#7835) AuTest: use exteneded help output to determin curl feature support (#7834) Apply fmt compile time argument checking to log functions (#7829) Adds new X-Cache-Info header to the xdebug plugin (#7784) Cleanup: Remove unused members of Http2Stream (#7813) Cleanup: unused functions of Http2ClientSession (#7812) Cancel cross_thread_event on clear_io_events (#7815) Cleanup: Remove a meaningless Http2Stream::do_io_close() call (#7814) Eliminate next dup call using stale mime field mloc is s3_auth plugin. (#7825) NetEvent cleanup - replace #define with constexpr (#7804) fix origin session related crashes (#7808) Update HTTP version info in HostDB on new outbound connection (#7816) Remove a redundant argument (#7811) SSL Cert lookup using PP dest ip when ProxyProtocol is enabled (#7802) Fix MLoc assert caused by s3auth (#7790) Fix cpu utilization problem in session cache (#7719) Fix to cookie_remap.cc tp avoid Intel compiler warning. (#7792) TSHttpTxnCacheDiskPathGet - tighten up the code a bit. (#7806) Doc: tcpinfo plugin table formatting (#7805) fix DNS spike issue for TCP_RETRY mode (#7307) Adds new TS API TSHttpTxnCacheDiskPathGet (#7783) tests: Fixes spelling (#7789) Traffic Dump: Add an HTTP/3 AuTest (#7758) use sendmsg and recvmsg (#7793) HTTP: clean up the http_hdr_describe format error (#7797) Fixes an issue where next hop unit tests crash when run on macOS. (#7787) Apply log throttling to HTTP/2 session error rate messages (#7772) Cleans up uninitialized warning in LogMessage.cc (#7788) Short circuit remap reload when a valid remap file is not specified (#7782) DNS: Clean up argument passing to DNS queries. (#7778) Remove extra verify-callback (#7540) Augment test cases for tls_verify_override test (#7736) Make when_to_revalidate setting available on HTTPS (#7753) Add traffic_server command line option for debugging in Au test. (#7762) Test: Update tls_partial_blind_tunnel to have a nameserver. (#7773) Test: update tls_forward_nonhttp to have a nameserver. (#7774) Test: add nameserver to log-filter test. (#7776) BWF: Add support for std::error_code. (#7777) Test: add nameserver to log-field test. (#7779) Test: add nameserver to regex_remap test. (#7775) Elevate privileges for traffic_manager during SSL cert reload (#7770) Clean up HTTP version processing (#7766) Remove proxy.config.http.down_server.abort_threshold (#7748) Remove undocumented keepalive_internal_vc setting (#7693) doc: header_rewrite random function not inclusive (#7760) Experimental Cache fill plugin (#7470) Remove references to removed options (#7756) Propagate TLS errors (#7714) AuTest extension: check for unrecognized configurations (#7752) Fixes errors in the strategies.yaml documentation. (#7745) Updates to Nexthop strategies to limit the number of simultaneous (#7744) Fixes Issue #7739 - Next hop strategy with bad 'to' URL causes TS crash. (#7749) header_rewrite: Various fixes for MaxMind support (#7746) Remove unused variable is_revalidation_necessary (#7747) Fix simple remapping in regex_remap plugin. (#7718) Adding DNS TTL AuTests. (#7742) Add a chunked disabled test. (#7743) Fix monitor threads in lib records to exit on system shutdown. (#7731) Add overload for memcpy to take a destination buffer and source string_view / TextView (#7732) Test: Add nameserver to TLS tunnel forward test. (#7733) AIO_NOT_IN_PROGRESS should not be 0 (#7734) if transaction status non-success, bypass intercept plugin (#7724) ink_utf8_to_latin1 is not defined, removing declaration (#7737) Fix build on FreeBSD 13 (#7730) Update VSCode CPP Standard (#7723) Updating to use Proxy Verifier 2.2.0 (#7729) header_rewrite: Allow for relative path to geo database files (#7727) Override proxy.config.ssl.client.sni_policy from sni.yaml (#7703) compress.test.py: Reference config file from Test.RunDirectory (#7725) Ran clang-tidy over the code (#7708) Deny unknown transfer encoding values (#7694) Fix doc for http2.no_activity_timeout_in (#7721) Add DynamicStats (#7704) header_rewrite: allow for use of maxminddb as source of geo truth (#7695) Include in parentselectdefs.h in install target (#7713) uri_signing: fix warning which affects ubuntu:20.04 builds (#7717) Increase the maximum slice block size from 32MB to 128MB (#7709) commit f90e8dde99564ff4270f2ae63e8592b0948b6130 Author: Masakazu Kitajo Date: Tue Jan 12 12:21:51 2021 +0900 Add QUICStreamStateListener commit f66646cb1907f7079eaf41aa81da9705934eff18 Merge: be9837c03 9f9594fd3 Author: Masakazu Kitajo Date: Sat Apr 17 13:57:50 2021 +0900 Merge branch 'master' into quic-latest * master: Fix ALPN support on QUIC connections (#7593) fix mem leak in session cache (#7707) Parent Select Plugin (#7467) Add new TS API function TSUrlRawPortGet. (#7568) Add NixOS support (#7697) Remove support for --enable-remote-cov-commit (#7700) Remove configure-time loopback interface detection (#7702) Add sqpv log field for server protocol (#7680) Call do_io_close instead of HTTP2_SESSION_EVENT_FINI handler (#7594) Fix a bug in tspush that pushes corrupted content to cache (#7696) Automatically marks PRs and issues stale (#7675) New rate_limit plugin for simple resource limitations (#7623) Remove undefined method HttpSM::perform_nca_cache_action (#7692) Remove undefined method HttpSM::setup_client_header_nca (#7691) Scalar; Move "tag" struct to be inside the "ts" namespace to avoid collisions. (#7690) Rollback LAZY_BUF_ALLOC remove in HttpTunnel (#7583) Add class to normalize handling of pending action (#7667) Make HTTP/2 Curl AuTest gold files case insensitive (#7683) Add STL compliant field iteration to MIMEHdr. - rebase. (#7476) Fix use of -mcx16 flag - only use if it compiles cleanly. (#7684) Refine connection failure logging and messages and eliminate suprious connection errors (#7580) Add close header normalize openclose test (#7679) Fix has_consumer_besides_client to deal with no clients (#7685) create a new cache status RWW_HIT (#7670) Updating to AuTest 1.10.0 (#7682) sslheaders AuTest: Skip if plugin does not exist (#7678) Add AuTest for Background Fill (#7613) Do NOT kill tunnel if it has any consumer besides HT_HTTP_CLIENT (#7641) AuTest: address various permissions issues (#7668) Adding TCP Info header support to header rewrite (#7516) Refine Inline.cc carveout for arm64 darwin builds (#7662) Comment why log eviction isn't implemented via a log field. (#7648) Fixing Throttler.h for older clang and gcc compilers (#7651) Update -with-profile and add some profiling documentation (#7601) Use correct default value for verify.server.policy (#7636) Update server_response_body_bytes when background fill worked (#7621) Remove erroneous manager.log mesg with remap include file reload (#7646) Change ROUNDUP from function-like macro to function template. (#7614) Document http.default_buffer_water_mark (#7612) Add proxy.config.cache.log.alternate.eviction (#7629) Fix HttpSessionManager::acquireSession from previous rebase error (#7631) Fix tls_client_versions and tls_hooks18 tests (#7645) Updating documentation for negative_revalidating_lifetime (#7633) Remove reference to client.verify.server from tests and other bits (#7639) Add pooled_server_connections metric (#7627) Expose URL element methods through HTTPHdr (#7628) Add default implementation for allow_half_open (#7630) Add thread yeield to avoid busy waiting in LogObject::_checkout_write(). (#7576) Add proxy.process.http.background_fill_total_count (#7625) statichit: misc. fixes (#7634) Remove unused variables (#7626) Adding negative revalidating AuTests. (#7620) Add failed state to hostdb to better track failing origins (#7291) Use standard isdigit library function (#7619) Typo in output when forcing kqueue for configure (#7617) Implement log throttling (#7279) Increase Proxy Verifier caching delay. (#7616) Set pcre_malloc/free function pointers in core main() only. (#7608) commit be9837c03219f2cb9efbd4981d13dbc78294ce51 Merge: 99ff68fa3 d4fc16f64 Author: Masakazu Kitajo Date: Wed Mar 17 09:38:59 2021 +0900 Merge branch 'master' into quic-latest * master: Fix the connection limit crash while using parents (#7604) Remove inline for detail::cache::CacheData::idAddr (#7592) Remove UnixNetVConnection::startEvent - not actually called. (#7596) Use return values to fix ubuntu release build error (#7591) Fix double destuct on Http2Stream termination (#7600) Add pointer/reference upcast function that is checked in debug builds. (#7582) Call constructors and destructors for H1/2 Session/Transaction via ClassAllocator (#7584) Add gold test for remap config .include directive. (#7589) Change the default value for verify.server.policy (#7587) Build the test library for tls_engine consistently (#7588) Generalize ALPN logic (#7555) Fix the final consumer write size from unchunked to chunked tunnel (#7577) Reactivate accept_no_activity_timeout (#7408) Tidy up session/transaction destruction process (#7571) Remove ProxyTransaction::set_proxy_ssn (#7567) Introduce TLSBasicSupport interface (#7556) Cleanup: Rename IOBufferReader of Http2ClientSession (#7569) Add a check for compress response, if from server and 304, then check cache for headers instead of the 304 response (#7564) Updates the STATUS file with all recent releases (#7566) Make Allocator.h less silly (no creepy "proto" object). (#6241) Cleanup: Remove unused member of Http2ClientSession (#7570) enable origin server session cache by default (#7537) Add tscontdestroy when transaction is closed and pacing rate is reset (#7572) Remove reference to CoreUtils (#7557) Remove unused enums from YamlSNIConfig struct. (#7565) Removes deprecated sni.yaml option: disable_h2 (#7547) This PR updates parent selection to limit the number of simultaneous (#7485) Fix KA header not checking strategy (#7483) Get rid of kruft LogObject copy constructor. (#7553) For TSHttpHdrEffectiveUrlBufGet(), include scheme for request to server URL. (#7545) Adding lower_ support to stats and bonding_slave data points for port status (#7560) Change cookie_remap plugin to allow use of pre-remap URL (and components). (#7519) check verify policy and properties (#7559) Fix parent.config to 504 not 502 on timeout (#7558) use SSL_CTX address as part of the lookup key (#7552) Add ALPN support on TLS Partial Blind Tunnel (#7511) Add server_name option to proxy.config.ssl.client.sni_policy (#7533) Fix a crash on origin session reuse (#7543) Removes the test plugins from the .spec file / RPM (#7551) Convert the inactive_client_timeout test to use Proxy Verifier (#7535) Fix ja3_fingerprint configure syntax (#7550) Fix asserts in multiplexer plugin. (#7532) parse expiration time and reload config at time out (#7281) Fix origin_session_reuse test (#7542) Fix tls_session_reuse test (#7541) Split SSL_CTX initialization logic into small functions (#7434) Remove dependency for SSL stuff from P_Net.h (#7531) Unify all the connect timeouts into one (#7335) Fix lua_states_stats Au test. (#7232) origin session reuse (#7479) Updating to use Proxy Verifier 2.1.0 (#7534) update the session reuse tests (#7529) commit 99ff68fa395a6e3f8ab2e27e98049ceb00a0a7c8 Author: Masakazu Kitajo Date: Wed Feb 17 11:14:40 2021 +0900 Fix link error commit c4ad0c071d53fb888d8b2ffac34b848524f4fe68 Merge: c40d95a91 cd33010ff Author: Masakazu Kitajo Date: Wed Feb 17 09:56:25 2021 +0900 Merge branch 'master' into quic-latest * master: Select lua context per thread (#7465) Fix out of bounds access error in jtest (#7526) Disable compiling Inline.cc on macOS (#7389) Makes sure the types are correct, avoiding compiler warnings (#7523) Move has_request_body to ProxyTransaction (#7499) Make the H3 build script work properly on Debian platforms (#7522) slice/handleFirstServerHeader: return sooner on requested range errors (#7486) Add new log field for negotiated ALPN Protocol ID with the client (#7491) Add Outbound PROXY Protocol (v1/v2) Support (#7446) Updates the Dockerfile for debian (#7518) Disable client inactivity timeout while server is processing POST request (#7309) Upgrade Catch.hpp to v2.13.4 (#7464) Move reopen_moved_log_files to log flushing thread (#7450) replace psutil.pid() with psutil.process_iter() for safer execution (#7515) Fix spacing in clang-analyzer.sh script (#7480) Fix out of bounds access error in ats_base64_decode (#7490) Updated to build lastest versions of Fedora and CentOS docker images (#7505) Fix QUIC unit tests build issue on GNU ld (#7496) Fix QUIC unit test failures (#7497) Fixed build issues with Fedora 34 (#7506) Fixing DNS local_ipv* config option (#7507) traffic_dump: AuTests to use Proxy Verifier. (#7502) Disable ja3 plugin when building with boringssl (#7500) Avoid -Warray-bounds on PROXY Protocol Builder (#7488) AuTest: Upgrade to Proxy Verifier 2.0.2 (#7493) fix certs (#7494) Add zlib1g-dev to Debian dependencies in README (#7495) Unit Test - Increase openssl's key size. Place test certs into a common test folder. (#7451) Add basic type aliases for std::chrono types to ink_time.h for future use. (#7482) traffic_ctl - Fix lookup key for run-root option (#7484) update thread config tests (#7370) Perf: Replace casecmp with memcmp in HPACK static table lookup (#6521) Add PROXY Protocol Builder (#7445) Adjust so transfer-encoding header can be treated hop-by-hop (#7473) Convert auxkey form 2 uint32_t to 1 uint64_t. (#7350) Remove the queuing option from proxy.config.http.per_server.connection (#7302) Remove unused function ink_microseconds. (#7481) use std::unordered_map to store sessions (#7405) drop use of BIO_f_base64 and EVP_PKEY_new_mac_key (#7106) Do not write to the cache if the plugin decides not to write to the cache (#7461) API to retrieve NoStore set by plugins (#7439) Update AuTest version update directions for pipenv (#7469) Add command line utility to help convert remap plugin usage to ATS9. (#7426) Cleanup: Get rid of MIMEFieldWrapper from HPACK encoding (#6520) Proxy Verifier: Making use of delay directives for caching tests. (#7468) Cleanup: Add SNIRoutingType (#7453) Updating to Proxy Verifier v2.0.0 (#7454) Adjust to actually try a server address more than once (#7288) Change atoi to atol, causing obvious issues on what needs to be int64's (#7466) Cleans up duplicated TSOutboundConnectionMatchType definition (#7090) Fixing compress expectation for new microserver (#7463) Update to the new MicroServer 1.0.6 release (#7460) CacheRead: clear dir entry if doc is found to be truncated (#7064) Do not provide a stale negative cache (#7422) Generalize SNI support (#6870) Add synchronization between UDPNetProcessor::UDPBind in main Thread and initialize_thread_for_udp_net in ET_UDP Thread (#7407) Fix heap use after free in DNSProcessor::getby() (#3871) Fix comment in include/tscore/Filenames.h. (#7457) Fix Makefile target for creating changelogs (#7455) Change squid log code for self looping (#7443) Enhancements for compress plugin (#7416) Add incoming PROXY Protocol v2 support (#7340) Cleanup: Remove unused members of NextHopProperty (#7436) Small fix to regex_remap PR # 7347. (#7437) PoolableSession (#6828) option to disable compression for range request's response (#7287) Make TSUrlSchemeGet() return scheme implied by URL type when there is no explicit scheme. (#7262) commit c40d95a912166224e517b07d6dc3ffd273907fc9 Merge: 573035c60 ecd70df36 Author: Masakazu Kitajo Date: Wed Jan 20 09:39:34 2021 +0900 Merge branch 'master' into quic-latest * master: Fix a link error on traffi_quic command (#7433) Fix stall on outbound TLS handshake (#7432) Fix the Proxy Verifier AuTest extension to handle cert paths correctly (#7415) Update documentation for TSSslSessionInsert (#7420) Improve zlib detection logic (#7430) Fix parent connect fail segfault (#7429) commit 573035c606fa088349420de35f0cdabe38649f5e Merge: 5704095ba 95b8d575a Author: Masakazu Kitajo Date: Fri Jan 15 23:24:29 2021 +0900 Merge branch 'master' into quic-latest * master: Doc: Fix typo in negative_revalidating_lifetime (#7427) Change comment handling for long lines in url_sig plugin (#7421) Add unit tests for PROXY Protocol v1 parser (#7332) LGTM: Remove superfluous const qualifier in return type (#7412) Fix issue with unavailable server retry codes (#7410) Remove the warning statement (#7414) default to throttling and subsequently simplify the transfer code (#7257) Improvement to lua plugin (#7413) Make places to bind/unbind SSL object with/from NetVC (#7399) traffic_ctl - plugin msg now require only the tag as mandatory field data field is now optional. (#7364) API - Add new api function TSHttpTxnServerSsnTransactionCount() to retrieve the number of transactions between TS proxy and the origin server from a single session. (#7387) Fix clang compiler complaint about an unused parameter in SNIAction. (#7409) Add compression support to stats_over_http (#7393) Doc: Fix INPUT tag of Doxyfile (#7404) Remove unneeded variables in UnixNetVConnection (#7403) Correctly pass back errno to HttpSM (#7402) Reverting to old negative_caching conditional behavior (#7401) Remove unused MAYBE_ABORT state (#7400) traffic_manager should not retry on disk failure (#7397) Eliminate dangling pointer into stack space. (#7392) This PR aims to address some of the lock contention found and (#7377) Remove a special treatment for SSLNetVC in migrateToCurrentThread() (#7384) Replace ::exit() with _exit() to avoid secondary cleanup cores (#7395) [Doc] Fix build warnings (#7391) Clear call_sm on tunnel reset (#7352) Unused code: HostDBContinuation::removeEvent (#7383) Traffic Dump: Fix stream-id printing after first transaction. (#7311) Add comments to ink_queue.h. (#7376) Cleanup incoming PROXY Protocol v1 (#7331) In CI, only run autopep8 on branches that enforce autopep8 (#7270) Fix FreeBSD 12 link issue in test_libhttp2. (#7367) Adjust flags to ensure tunnel producer is cleaned up (#7336) Cleanup: Remove SSL Wire Trace releated code in UnixNetVConnection (#7368) Use EVP MAC API if available (#7363) Use EVP API instead of MD5_Init/Update/Final (secure_link plugin) (#7355) Use ERR_get_error_all if available (#7354) Use OpeSSL EVP API instead of SHA256_Init/Update/Final (#7342) Cleanup: Get rid of NetVConnection::outstanding() (#7366) Cleanup: Remove unused functions (#7365) Add a post case to the conn_timeout test (#7334) Fix sni ip_allow and host_sni_policy (#7349) AuTest for Split DNS (#7325) Make reloading client certificate configuration more reliable (#7313) Add negative caching tests and fixes. (#7361) ESI: Ensure gzip header is always initialized (#7360) Allow for regex_remap of pristine URL. (#7347) Set thread mutex to the DNSHandler mutex of SplitDNS (#7321) Fix lookup split dns rule with fast path (#7320) Add note to background fetch about include/exclude (#7343) AuTest for incoming PROXY Protocol v1 (#7326) Fix vc close migration race condition (#7337) TLS Session Reuse: Downgrade add_session messages to debug (#7345) TLS Session Reuse: Downgrade noisy log to debug (#7344) Remove the last remnants of the enable_url_expandomatic (#7276) Remove unnecessary cast from ReverseProxy. (#7329) Updates the Dockerfile with more packages (#7323) fixup in HttpSM to only set [TS_MILESTONE_SERVER_CLOSE if TS_MILESTONE_SERVER_CONNECT has been set (#7259) Add option for hybrid global and thread session pools (#6978) Get appropriate locks on SSN_START hook delays (#7295) s3_auth: demote noisy errors around configuration that doesn't affect plugin usability (#7306) Follow the comments in I_Thread.h, add an independent ink_thread_key for EThread. (#6288) Reduce the number of write operation on H2 (#7282) commit 5704095ba63316e672d9fae67d2757fff084e03c Merge: 882a79d87 0c88b24a0 Author: Masakazu Kitajo Date: Wed Oct 28 21:06:11 2020 +0900 Merge branch 'master' into quic-latest * master: Adds a shell script to help build the H3 toolchains (#7299) Remove unfinished h2c support (#7286) Allow disabling SO_MARK and IP_TOS usage (#7292) Enable all h2spec test (#7289) Fix bad HTTP/2 post client causing stuck HttpSM (#7237) Sticky server does not work with H2 client (#7261) 7096: Synchronize Server Session Management and Network I/O (#7278) HostDB: remove unused field in HostDBApplicationInfo, and update remaining types in http_data to fix broken padding. (#7264) Add support for a new (TSMgmtDataTypeGet) mgmt API function to retrieve the record data type (#7221) Fix example in default sni.yaml configuration. (#7277) Fix proxy.process.http.current_client_transactions (#7258) Add AuTest for HTTP/2 Graceful Shutdown (#7271) Fix truncated reponse on HTTP/2 graceful shutdown (#7267) url_sig add 'ignore_expiry = true' option for log replay testing (#7231) Respecting default rolling_enabled in plugins. (#7275) gracefully handle TSReleaseAsserts in statichit and generator plugins (#7269) Removes commented out code from esi plugin (#7273) Allow initial // in request targets. (#7266) Document external log rotation support via SIGUSR2 (#7265) Let Dedicated EThreads use `EThread::schedule` (#7228) HostDB: Fix cache data version checking to use full version, not major version. (#7263) Bugfix: set a default inactivity timeout only if a read or write I/O operation was set (#7226) Treat objects with negative max-age CC directives as stale. (#7260) Remove some usless defines, which just obsfucates code (#7252) Remove useless if for port set assertion. (#7250) Fix test_error_page_selection memory leaks and logic errors (#7248) [multiplexer] option to skip post/put requests (#7233) Incorporates the latest CI build changes (#7251) Add support for server protocol stack API (#7239) Fix for plugins ASAN suppression file (#7249) RolledLogDeleter: do not sort on each candidate consideration. (#7243) Make double Au test more reliable. (#7216) Ensure that ca override does not get lost (#7219) Stop crash on disk failure (#7218) Do not cache Transfer-Encoding header (#7234) clean up body factory tests (#7236) Revert "Create an explicit runroot.yaml for AuTests (#7177)" (#7235) New option to dead server to not retry during dead period (#7142) Increment ssl_error_syscall only if not EOF (#7225) Fix renamed setting in default config (#7224) Log config reload: use new config for initialization (#7215) Introduce proxy-verifier to AuTests (#7211) Follow redirection responses when refreshing stale cache objects. (#7213) Create an explicit runroot.yaml for AuTests (#7177) Support external log rotation tools via SIGUSR2 (#6806) Add support for TS API for Note, Status, Warning, Alert (#7208) If the weight is 0, the SRV record should be selected from the highest priority group (#7206) Cleanup: remove unnecessary memset() within dns_process() (#7209) Docs cleanup (#7210) Strip whitespaces after field-name and before the colon in headers from the origin (#7202) Adds new plugin: statichit (#7173) Add duplicate header field processing when creating outgoing response (#7207) commit 882a79d87126a27482b2d1dc5a172ef042acad6b Merge: 2a9887f4c bb5c39086 Author: Masakazu Kitajo Date: Fri Sep 18 10:01:14 2020 +0900 Merge branch 'master' into quic-latest * master: Rename ambiguous log variable (#7199) KWF useless member function HttpSM::kill_this_async_hook(). (#7198) Fix the active_timeout test to work without quic enabled (#7197) Remove obsolete cdn_ HttpTransact vars (#7182) Remove unused HttpUpdate mechanism (#7194) Updates the list of supported / linked Docs versions (#7152) Make custom xdebug HTTP header name available to other plugins. (#7193) Update sni outbound policy to allow directly setting the outbound SNI. (#7188) commit 2a9887f4c4c5a9259cdd64bf24c76b1618d78d29 Author: Masakazu Kitajo Date: Wed Sep 16 17:54:01 2020 +0900 Avoid unnecessary QUIC CID randomization commit 42e8898aafbdb8f17fefb1da99d7ae7cdc019a19 Author: Masakazu Kitajo Date: Tue Sep 15 12:41:28 2020 +0900 Simplify interface between H3 and QUIC, and remove memcopy between them commit 112fc71a324397a590c1cad6a4b2cfed27a551c2 Merge: ac31adaa8 b09096481 Author: Masakazu Kitajo Date: Tue Sep 15 09:21:25 2020 +0900 Merge branch 'master' into quic-latest * master: Add an autest testcase for HTTP3 (#7063) Fix TSHttpTxnServerPacket* API's to correctly update existing server connections (#7175) Do not lose original inactivity timeout on disable (#7134) Emits log when OCSP fails to connect to server (#7183) autopep8: avoid running on non-tracked files. (#7186) TextView: Add additional constructor tests. (#7189) Remove duplicate code (#7180) TextView: add constructor size values to enable strlen even for null pointers. (#7185) Add virtual destructor to QUICRTTProvider. (#7184) AuTest: Reuse venv if it exists already (#7178) TS_API for Note,Status,Warning,Alert,Fatal (#7181) Traffic Dump: Record HTTP/2 priority. (#7149) leaks in logs (#7172) Additions to enable loading qat_engine (#7150) Removes references to non-existent function handle_conditional_headers (#7162) Fix #7164 Chaning Warning to Debug and creating a stat for inserting duplicates to pending dns (#7166) Fix #7167, make autopep8 failure (#7168) MicroDNS Extension: handle different 'default' types (#7159) Traffic Dump documentation for post_process.py (#7161) Fix memory leaks in multiplexer plugin (#7160) rc: fixes systemd unit file stopping (#7157) Fix lua plugin mem leak problem (#7158) Don't make an error on duplicated RETIRE_CONNECTION frames (#7131) URL::parse fixes for empty paths (#7119) Replace ACTION_RESULT_NONE with nullptr (#7135) Add metric tracking async job pauses (#7153) PluginFactory - Remove unused code that was left from last PluginFactory change(TSPluginDSOReloadEnable) (#7155) Fix stale pointer due to SSL config reload (#7148) slice: check if vio is still valid before calling TSVIODone* on shutdown (#7147) Deprecate cqhv field (#7143) Don't return QUIC frame if the size exceeds maximum frame size (#7121) Check VIO availability before acquiring a lock for it (#7145) Fix #7116, skip the insertion of the same continuation to pending dns (#7117) Allow override of CA certs for cert from client based on SNI server name sent by client. (#7130) Fix typo in cache docs (#7144) remove useless shortopt (#7138) Protect TSActionCancel from null INKContInternal actions (#7128) Check VIO availability before checking whether the VIO has data (#7120) Accept NAT rebinding on a QUIC connection (#7123) Fixes garbled logs when using % log tag (#7140) Removes duplicated listing of files in same Makefile target (#7137) Updated gdb mutex script to get process file for Fedora 32 (#7133) SSLConfig mem leak fix (#7125) Replaces "smart" quotes with ASCII equivalents (#7126) Comment out a wrong assertion in QUIC Loss Detection logic (#7129) Add member initialization to the Errata class. (#7132) Cancel active/inactive timeout on closing Http2Stream (#7111) Add modsecurity lua script to example (#7105) Expose remap config file callback (#7073) Make tls_hooks tests more likely to pass (#7122) commit ac31adaa82f9271f902de2f45c071e328f620271 Merge: 4d579f49a e904dbcef Author: Masakazu Kitajo Date: Mon Aug 17 09:14:14 2020 +0900 Merge branch 'master' into quic-latest * master: Backing out my update of our jenkin's autest file. (#7118) Don't send image/webp responses from cache to broswers that don't support it (#7104) Updating our autest suite to require Python3.6 (#7113) Squashed commit of the following: (#7110) Supporting out of source builds for AuTests. (#7109) Fixes uninitialized variables found by Xcode (#7100) Add cross references between server session sharing match and upstream connection tracking match. (#7038) --- iocore/net/quic/Makefile.am | 3 + iocore/net/quic/Mock.h | 90 ++- iocore/net/quic/QUICApplication.cc | 248 +------- iocore/net/quic/QUICApplication.h | 52 +- iocore/net/quic/QUICBidirectionalStream.cc | 381 ++++--------- iocore/net/quic/QUICBidirectionalStream.h | 23 +- iocore/net/quic/QUICPacket.h | 2 +- iocore/net/quic/QUICPacketFactory.cc | 4 +- iocore/net/quic/QUICStream.cc | 158 +----- iocore/net/quic/QUICStream.h | 49 +- iocore/net/quic/QUICStreamAdapter.cc | 32 ++ iocore/net/quic/QUICStreamAdapter.h | 65 +++ iocore/net/quic/QUICStreamFactory.cc | 6 +- iocore/net/quic/QUICStreamFactory.h | 6 +- iocore/net/quic/QUICStreamManager.cc | 89 +-- iocore/net/quic/QUICStreamManager.h | 11 +- iocore/net/quic/QUICStreamState.cc | 96 ++-- iocore/net/quic/QUICStreamState.h | 43 +- iocore/net/quic/QUICStreamVCAdapter.cc | 320 +++++++++++ iocore/net/quic/QUICStreamVCAdapter.h | 105 ++++ .../net/quic/QUICTransferProgressProvider.cc | 83 +++ .../net/quic/QUICTransferProgressProvider.h | 44 +- iocore/net/quic/QUICUnidirectionalStream.cc | 533 ++++-------------- iocore/net/quic/QUICUnidirectionalStream.h | 20 +- iocore/net/quic/test/test_QUICStream.cc | 189 ++++--- iocore/net/quic/test/test_QUICStreamState.cc | 134 ++--- proxy/http3/Http09App.cc | 53 +- proxy/http3/Http09App.h | 4 + proxy/http3/Http3App.cc | 157 ++++-- proxy/http3/Http3App.h | 28 +- proxy/http3/Http3DataFramer.cc | 8 +- proxy/http3/Http3DataFramer.h | 2 +- proxy/http3/Http3Frame.cc | 127 +++-- proxy/http3/Http3Frame.h | 12 +- proxy/http3/Http3FrameCollector.cc | 22 +- proxy/http3/Http3FrameCollector.h | 4 +- proxy/http3/Http3FrameDispatcher.cc | 15 +- proxy/http3/Http3FrameDispatcher.h | 4 +- proxy/http3/Http3FrameGenerator.h | 4 +- proxy/http3/Http3HeaderFramer.cc | 9 +- proxy/http3/Http3HeaderFramer.h | 2 +- proxy/http3/Http3HeaderVIOAdaptor.cc | 122 +++- proxy/http3/Http3HeaderVIOAdaptor.h | 23 +- proxy/http3/Http3Transaction.cc | 178 ++---- proxy/http3/Http3Transaction.h | 19 +- proxy/http3/QPACK.cc | 159 +++--- proxy/http3/QPACK.h | 33 +- proxy/http3/test/main.cc | 8 + proxy/http3/test/test_Http3Frame.cc | 24 +- proxy/http3/test/test_QPACK.cc | 22 +- src/traffic_quic/quic_client.cc | 67 ++- src/traffic_quic/quic_client.h | 11 + src/traffic_quic/traffic_quic.cc | 5 + 53 files changed, 2003 insertions(+), 1905 deletions(-) create mode 100644 iocore/net/quic/QUICStreamAdapter.cc create mode 100644 iocore/net/quic/QUICStreamAdapter.h create mode 100644 iocore/net/quic/QUICStreamVCAdapter.cc create mode 100644 iocore/net/quic/QUICStreamVCAdapter.h create mode 100644 iocore/net/quic/QUICTransferProgressProvider.cc diff --git a/iocore/net/quic/Makefile.am b/iocore/net/quic/Makefile.am index 8a60a23b465..ca72382de7a 100644 --- a/iocore/net/quic/Makefile.am +++ b/iocore/net/quic/Makefile.am @@ -62,6 +62,8 @@ libquic_a_SOURCES = \ QUICNewRenoCongestionController.cc \ QUICFlowController.cc \ QUICStreamState.cc \ + QUICStreamAdapter.cc \ + QUICStreamVCAdapter.cc \ QUICStream.cc \ QUICHandshake.cc \ QUICPacketHeaderProtector.cc \ @@ -92,6 +94,7 @@ libquic_a_SOURCES = \ QUICFrameGenerator.cc \ QUICFrameRetransmitter.cc \ QUICAddrVerifyState.cc \ + QUICTransferProgressProvider.cc \ QUICBidirectionalStream.cc \ QUICCryptoStream.cc \ QUICUnidirectionalStream.cc \ diff --git a/iocore/net/quic/Mock.h b/iocore/net/quic/Mock.h index eda260c04e4..3185315dc6c 100644 --- a/iocore/net/quic/Mock.h +++ b/iocore/net/quic/Mock.h @@ -36,6 +36,7 @@ #include "QUICPinger.h" #include "QUICPadder.h" #include "QUICHandshakeProtocol.h" +#include "QUICStreamAdapter.h" class MockQUICContext; @@ -717,6 +718,76 @@ class MockQUICLossDetector : public QUICLossDetector MockQUICCongestionController _cc; }; +class MockQUICStreamAdapter : public QUICStreamAdapter +{ +public: + MockQUICStreamAdapter(QUICStream &stream) : QUICStreamAdapter(stream) {} + + void + write_to_stream(const uint8_t *buf, size_t len) + { + this->_total_sending_data_len += len; + this->_sending_data_len += len; + } + + int64_t + write(QUICOffset offset, const uint8_t *data, uint64_t data_length, bool fin) override + { + this->_total_receiving_data_len += data_length; + this->_receiving_data_len += data_length; + return data_length; + } + bool + is_eos() override + { + return false; + } + uint64_t + unread_len() override + { + return this->_sending_data_len; + } + uint64_t + read_len() override + { + return 0; + } + uint64_t + total_len() override + { + return this->_total_sending_data_len; + } + void + encourge_read() override + { + } + void + encourge_write() override + { + } + void + notify_eos() override + { + } + +protected: + Ptr + _read(size_t len) override + { + this->_sending_data_len -= len; + Ptr block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(len, BUFFER_SIZE_INDEX_32K)); + block->fill(len); + return block; + } + +private: + size_t _sending_data_len = 0; + size_t _total_sending_data_len = 0; + size_t _receiving_data_len = 0; + size_t _total_receiving_data_len = 0; +}; + class MockQUICApplication : public QUICApplication { public: @@ -726,20 +797,29 @@ class MockQUICApplication : public QUICApplication main_event_handler(int event, Event *data) { if (event == 12345) { - QUICStreamIO *stream_io = static_cast(data->cookie); - stream_io->write_reenable(); } return EVENT_CONT; } + void + on_new_stream(QUICStream &stream) override + { + auto ite = this->_streams.emplace(stream.id(), stream); + QUICStreamAdapter &adapter = ite.first->second; + stream.set_io_adapter(&adapter); + } + void send(const uint8_t *data, size_t size, QUICStreamId stream_id) { - QUICStreamIO *stream_io = this->_find_stream_io(stream_id); - stream_io->write(data, size); + auto ite = this->_streams.find(stream_id); + auto &adapter = ite->second; + adapter.write_to_stream(data, size); - eventProcessor.schedule_imm(this, ET_CALL, 12345, stream_io); + // eventProcessor.schedule_imm(this, ET_CALL, 12345, adapter); } + + std::unordered_map _streams; }; class MockQUICPacketR : public QUICPacketR diff --git a/iocore/net/quic/QUICApplication.cc b/iocore/net/quic/QUICApplication.cc index a5bfd26541f..d6dc495e2fb 100644 --- a/iocore/net/quic/QUICApplication.cc +++ b/iocore/net/quic/QUICApplication.cc @@ -24,172 +24,6 @@ #include "QUICApplication.h" #include "QUICStream.h" -static constexpr char tag_stream_io[] = "quic_stream_io"; -static constexpr char tag_app[] = "quic_app"; - -#define QUICStreamIODebug(fmt, ...) \ - Debug(tag_stream_io, "[%s] [%" PRIu64 "] " fmt, this->_stream_vc->connection_info()->cids().data(), this->_stream_vc->id(), \ - ##__VA_ARGS__) - -// -// QUICStreamIO -// -QUICStreamIO::QUICStreamIO(QUICApplication *app, QUICStreamVConnection *stream_vc) : _stream_vc(stream_vc) -{ - this->_read_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_8K); - this->_write_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_8K); - - this->_read_buffer_reader = this->_read_buffer->alloc_reader(); - this->_write_buffer_reader = this->_write_buffer->alloc_reader(); - - switch (stream_vc->direction()) { - case QUICStreamDirection::BIDIRECTIONAL: - this->_read_vio = stream_vc->do_io_read(app, INT64_MAX, this->_read_buffer); - this->_write_vio = stream_vc->do_io_write(app, INT64_MAX, this->_write_buffer_reader); - break; - case QUICStreamDirection::SEND: - this->_write_vio = stream_vc->do_io_write(app, INT64_MAX, this->_write_buffer_reader); - break; - case QUICStreamDirection::RECEIVE: - this->_read_vio = stream_vc->do_io_read(app, INT64_MAX, this->_read_buffer); - break; - default: - ink_assert(false); - break; - } -} - -QUICStreamIO::~QUICStreamIO() -{ - // All readers will be deallocated - free_MIOBuffer(this->_read_buffer); - free_MIOBuffer(this->_write_buffer); -}; - -uint32_t -QUICStreamIO::stream_id() const -{ - return this->_stream_vc->id(); -} - -bool -QUICStreamIO::is_bidirectional() const -{ - return this->_stream_vc->is_bidirectional(); -} - -int64_t -QUICStreamIO::read(uint8_t *buf, int64_t len) -{ - if (is_debug_tag_set(tag_stream_io)) { - if (this->_read_vio->nbytes == INT64_MAX) { - QUICStreamIODebug("nbytes=- ndone=%" PRId64 " read_avail=%" PRId64 " read_len=%" PRId64, this->_read_vio->ndone, - this->_read_buffer_reader->read_avail(), len); - } else { - QUICStreamIODebug("nbytes=%" PRId64 " ndone=%" PRId64 " read_avail=%" PRId64 " read_len=%" PRId64, this->_read_vio->nbytes, - this->_read_vio->ndone, this->_read_buffer_reader->read_avail(), len); - } - } - - int64_t nread = this->_read_buffer_reader->read(buf, len); - if (nread > 0) { - this->_read_vio->ndone += nread; - } - - this->_stream_vc->on_read(); - - return nread; -} - -int64_t -QUICStreamIO::peek(uint8_t *buf, int64_t len) -{ - return this->_read_buffer_reader->memcpy(buf, len) - reinterpret_cast(buf); -} - -void -QUICStreamIO::consume(int64_t len) -{ - this->_read_buffer_reader->consume(len); - this->_stream_vc->on_read(); -} - -bool -QUICStreamIO::is_read_done() const -{ - return this->_read_vio->ntodo() == 0; -} - -int64_t -QUICStreamIO::write(const uint8_t *buf, int64_t len) -{ - SCOPED_MUTEX_LOCK(lock, this->_write_vio->mutex, this_ethread()); - - int64_t nwritten = this->_write_buffer->write(buf, len); - if (nwritten > 0) { - this->_nwritten += nwritten; - } - - return len; -} - -int64_t -QUICStreamIO::write(IOBufferReader *r, int64_t len) -{ - SCOPED_MUTEX_LOCK(lock, this->_write_vio->mutex, this_ethread()); - - int64_t bytes_avail = this->_write_buffer->write_avail(); - - if (bytes_avail > 0) { - if (is_debug_tag_set(tag_stream_io)) { - if (this->_write_vio->nbytes == INT64_MAX) { - QUICStreamIODebug("nbytes=- ndone=%" PRId64 " write_avail=%" PRId64 " write_len=%" PRId64, this->_write_vio->ndone, - bytes_avail, len); - } else { - QUICStreamIODebug("nbytes=%" PRId64 " ndone=%" PRId64 " write_avail=%" PRId64 " write_len=%" PRId64, - this->_write_vio->nbytes, this->_write_vio->ndone, bytes_avail, len); - } - } - - int64_t bytes_len = std::min(bytes_avail, len); - int64_t nwritten = this->_write_buffer->write(r, bytes_len); - - if (nwritten > 0) { - this->_nwritten += nwritten; - } - - return nwritten; - } else { - return 0; - } -} - -// TODO: Similar to other "write" apis, but do not copy. -int64_t -QUICStreamIO::write(IOBufferBlock *b) -{ - ink_assert(!"not implemented yet"); - return 0; -} - -void -QUICStreamIO::write_done() -{ - this->_write_vio->nbytes = this->_nwritten; -} - -void -QUICStreamIO::read_reenable() -{ - return this->_read_vio->reenable(); -} - -void -QUICStreamIO::write_reenable() -{ - return this->_write_vio->reenable(); -} - // // QUICApplication // @@ -198,84 +32,4 @@ QUICApplication::QUICApplication(QUICConnection *qc) : Continuation(new_ProxyMut this->_qc = qc; } -QUICApplication::~QUICApplication() -{ - for (auto const &kv : this->_stream_map) { - delete kv.second; - } -} - -// @brief Bind stream and application -void -QUICApplication::set_stream(QUICStreamVConnection *stream_vc, QUICStreamIO *stream_io) -{ - if (stream_io == nullptr) { - stream_io = new QUICStreamIO(this, stream_vc); - } - this->_stream_map.insert(std::make_pair(stream_vc->id(), stream_io)); -} - -// @brief Bind stream and application -void -QUICApplication::set_stream(QUICStreamIO *stream_io) -{ - this->_stream_map.insert(std::make_pair(stream_io->stream_id(), stream_io)); -} - -bool -QUICApplication::is_stream_set(QUICStreamVConnection *stream) -{ - auto result = this->_stream_map.find(stream->id()); - - return result != this->_stream_map.end(); -} - -void -QUICApplication::reenable(QUICStreamVConnection *stream) -{ - QUICStreamIO *stream_io = this->_find_stream_io(stream->id()); - if (stream_io) { - stream_io->read_reenable(); - stream_io->write_reenable(); - } else { - Debug(tag_app, "[%s] Unknown Stream id=%" PRIx64, this->_qc->cids().data(), stream->id()); - } - - return; -} - -void -QUICApplication::unset_stream(QUICStreamVConnection *stream) -{ - QUICStreamIO *stream_io = this->_find_stream_io(stream->id()); - if (stream_io) { - this->_stream_map.erase(stream->id()); - } -} - -QUICStreamIO * -QUICApplication::_find_stream_io(QUICStreamId id) -{ - auto result = this->_stream_map.find(id); - - if (result == this->_stream_map.end()) { - return nullptr; - } else { - return result->second; - } -} - -QUICStreamIO * -QUICApplication::_find_stream_io(VIO *vio) -{ - if (vio == nullptr) { - return nullptr; - } - - QUICStream *stream = dynamic_cast(vio->vc_server); - if (stream == nullptr) { - return nullptr; - } - - return this->_find_stream_io(stream->id()); -} +QUICApplication::~QUICApplication() {} diff --git a/iocore/net/quic/QUICApplication.h b/iocore/net/quic/QUICApplication.h index c4fb16fd50d..a697ea2994d 100644 --- a/iocore/net/quic/QUICApplication.h +++ b/iocore/net/quic/QUICApplication.h @@ -31,48 +31,6 @@ class QUICApplication; -/** - @brief QUICStream I/O Interface for QUICApplication - */ -class QUICStreamIO -{ -public: - QUICStreamIO(QUICApplication *app, QUICStreamVConnection *stream); - virtual ~QUICStreamIO(); - - uint32_t stream_id() const; - bool is_bidirectional() const; - - int64_t read(uint8_t *buf, int64_t len); - int64_t peek(uint8_t *buf, int64_t len); - void consume(int64_t len); - bool is_read_done() const; - virtual void read_reenable(); - - int64_t write(const uint8_t *buf, int64_t len); - int64_t write(IOBufferReader *r, int64_t len); - int64_t write(IOBufferBlock *b); - void write_done(); - virtual void write_reenable(); - -protected: - MIOBuffer *_read_buffer = nullptr; - MIOBuffer *_write_buffer = nullptr; - - IOBufferReader *_read_buffer_reader = nullptr; - IOBufferReader *_write_buffer_reader = nullptr; - -private: - QUICStreamVConnection *_stream_vc = nullptr; - - VIO *_read_vio = nullptr; - VIO *_write_vio = nullptr; - - // Track how much data is written to _write_vio. When total size of data become clear, - // set it to _write_vio.nbytes. - uint64_t _nwritten = 0; -}; - /** * @brief Abstract QUIC Application Class * @detail Every quic application must inherits this class @@ -83,18 +41,10 @@ class QUICApplication : public Continuation QUICApplication(QUICConnection *qc); virtual ~QUICApplication(); - void set_stream(QUICStreamVConnection *stream_vc, QUICStreamIO *stream_io = nullptr); - void set_stream(QUICStreamIO *stream_io); - bool is_stream_set(QUICStreamVConnection *stream_vc); - void reenable(QUICStreamVConnection *stream_vc); - void unset_stream(QUICStreamVConnection *stream_vc); + virtual void on_new_stream(QUICStream &stream) = 0; protected: - QUICStreamIO *_find_stream_io(QUICStreamId id); - QUICStreamIO *_find_stream_io(VIO *vio); - QUICConnection *_qc = nullptr; private: - std::map _stream_map; }; diff --git a/iocore/net/quic/QUICBidirectionalStream.cc b/iocore/net/quic/QUICBidirectionalStream.cc index 4203ec0eeb6..e5653e7d349 100644 --- a/iocore/net/quic/QUICBidirectionalStream.cc +++ b/iocore/net/quic/QUICBidirectionalStream.cc @@ -22,118 +22,25 @@ */ #include "QUICBidirectionalStream.h" +#include "QUICStreamAdapter.h" // // QUICBidirectionalStream // QUICBidirectionalStream::QUICBidirectionalStream(QUICRTTProvider *rtt_provider, QUICConnectionInfoProvider *cinfo, QUICStreamId sid, uint64_t recv_max_stream_data, uint64_t send_max_stream_data) - : QUICStreamVConnection(cinfo, sid), + : QUICStream(cinfo, sid), _remote_flow_controller(send_max_stream_data, _id), _local_flow_controller(rtt_provider, recv_max_stream_data, _id), _flow_control_buffer_size(recv_max_stream_data), - _state(nullptr, &this->_progress_vio, this, nullptr) + _state(nullptr, &this->_progress_sa, this, nullptr) { - SET_HANDLER(&QUICBidirectionalStream::state_stream_open); - QUICStreamFCDebug("[LOCAL] %" PRIu64 "/%" PRIu64, this->_local_flow_controller.current_offset(), this->_local_flow_controller.current_limit()); QUICStreamFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller.current_offset(), this->_remote_flow_controller.current_limit()); } -int -QUICBidirectionalStream::state_stream_open(int event, void *data) -{ - QUICVStreamDebug("%s (%d)", get_vc_event_name(event), event); - QUICErrorUPtr error = nullptr; - - switch (event) { - case VC_EVENT_READ_READY: - case VC_EVENT_READ_COMPLETE: { - int64_t len = this->_process_read_vio(); - if (len > 0) { - this->_signal_read_event(); - } - - break; - } - case VC_EVENT_WRITE_READY: - case VC_EVENT_WRITE_COMPLETE: { - int64_t len = this->_process_write_vio(); - if (len > 0) { - this->_signal_write_event(); - } - - break; - } - case VC_EVENT_EOS: - case VC_EVENT_ERROR: - case VC_EVENT_INACTIVITY_TIMEOUT: - case VC_EVENT_ACTIVE_TIMEOUT: { - // TODO - ink_assert(false); - break; - } - default: - QUICStreamDebug("unknown event"); - ink_assert(false); - } - - // FIXME error is always nullptr - if (error != nullptr) { - if (error->cls == QUICErrorClass::TRANSPORT) { - QUICStreamDebug("QUICError: %s (%u), %s (0x%x)", QUICDebugNames::error_class(error->cls), - static_cast(error->cls), QUICDebugNames::error_code(error->code), - static_cast(error->code)); - } else { - QUICStreamDebug("QUICError: %s (%u), APPLICATION ERROR (0x%x)", QUICDebugNames::error_class(error->cls), - static_cast(error->cls), static_cast(error->code)); - } - if (dynamic_cast(error.get()) != nullptr) { - // Stream Error - QUICStreamErrorUPtr serror = QUICStreamErrorUPtr(static_cast(error.get())); - this->reset(std::move(serror)); - } else { - // Connection Error - // TODO Close connection (Does this really happen?) - } - } - - return EVENT_DONE; -} - -int -QUICBidirectionalStream::state_stream_closed(int event, void *data) -{ - QUICVStreamDebug("%s (%d)", get_vc_event_name(event), event); - - switch (event) { - case VC_EVENT_READ_READY: - case VC_EVENT_READ_COMPLETE: { - // ignore - break; - } - case VC_EVENT_WRITE_READY: - case VC_EVENT_WRITE_COMPLETE: { - // ignore - break; - } - case VC_EVENT_EOS: - case VC_EVENT_ERROR: - case VC_EVENT_INACTIVITY_TIMEOUT: - case VC_EVENT_ACTIVE_TIMEOUT: { - // TODO - ink_assert(false); - break; - } - default: - ink_assert(false); - } - - return EVENT_DONE; -} - bool QUICBidirectionalStream::is_transfer_goal_set() const { @@ -168,7 +75,6 @@ QUICConnectionErrorUPtr QUICBidirectionalStream::recv(const QUICStreamFrame &frame) { ink_assert(_id == frame.stream_id()); - ink_assert(this->_read_vio.op == VIO::READ); // Check stream state - Do this first before accept the frame if (!this->_state.is_allowed_to_receive(frame)) { @@ -202,9 +108,11 @@ QUICBidirectionalStream::recv(const QUICStreamFrame &frame) last_offset = stream_frame->offset(); last_length = stream_frame->data_length(); - this->_write_to_read_vio(stream_frame->offset(), reinterpret_cast(stream_frame->data()->start()), - stream_frame->data_length(), stream_frame->has_fin_flag()); - this->_state.update_with_receiving_frame(*new_frame); + this->_adapter->write(stream_frame->offset(), reinterpret_cast(stream_frame->data()->start()), + stream_frame->data_length(), stream_frame->has_fin_flag()); + if (this->_state.update_with_receiving_frame(*new_frame)) { + this->_notify_state_change(); + } delete new_frame; new_frame = this->_received_stream_frame_buffer.pop(); @@ -218,7 +126,7 @@ QUICBidirectionalStream::recv(const QUICStreamFrame &frame) this->_local_flow_controller.current_limit()); } - this->_signal_read_event(); + this->_adapter->encourge_read(); return nullptr; } @@ -230,10 +138,7 @@ QUICBidirectionalStream::recv(const QUICMaxStreamDataFrame &frame) QUICStreamFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller.current_offset(), this->_remote_flow_controller.current_limit()); - int64_t len = this->_process_write_vio(); - if (len > 0) { - this->_signal_write_event(); - } + this->_adapter->encourge_write(); return nullptr; } @@ -249,7 +154,9 @@ QUICBidirectionalStream::recv(const QUICStreamDataBlockedFrame &frame) QUICConnectionErrorUPtr QUICBidirectionalStream::recv(const QUICStopSendingFrame &frame) { - this->_state.update_with_receiving_frame(frame); + if (this->_state.update_with_receiving_frame(frame)) { + this->_notify_state_change(); + } this->_reset_reason = QUICStreamErrorUPtr(new QUICStreamError(this, QUIC_APP_ERROR_CODE_STOPPING)); // We received and processed STOP_SENDING frame, so return NO_ERROR here return nullptr; @@ -258,97 +165,11 @@ QUICBidirectionalStream::recv(const QUICStopSendingFrame &frame) QUICConnectionErrorUPtr QUICBidirectionalStream::recv(const QUICRstStreamFrame &frame) { - this->_state.update_with_receiving_frame(frame); - this->_signal_read_eos_event(); - return nullptr; -} - -// this->_read_vio.nbytes should be INT64_MAX until receive FIN flag -VIO * -QUICBidirectionalStream::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf) -{ - if (buf) { - this->_read_vio.buffer.writer_for(buf); - } else { - this->_read_vio.buffer.clear(); - } - - this->_read_vio.mutex = c ? c->mutex : this->mutex; - this->_read_vio.cont = c; - this->_read_vio.nbytes = nbytes; - this->_read_vio.ndone = 0; - this->_read_vio.vc_server = this; - this->_read_vio.op = VIO::READ; - - this->_process_read_vio(); - this->_send_tracked_event(this->_read_event, VC_EVENT_READ_READY, &this->_read_vio); - - return &this->_read_vio; -} - -VIO * -QUICBidirectionalStream::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, bool owner) -{ - if (buf) { - this->_write_vio.buffer.reader_for(buf); - } else { - this->_write_vio.buffer.clear(); - } - - this->_write_vio.mutex = c ? c->mutex : this->mutex; - this->_write_vio.cont = c; - this->_write_vio.nbytes = nbytes; - this->_write_vio.ndone = 0; - this->_write_vio.vc_server = this; - this->_write_vio.op = VIO::WRITE; - - this->_process_write_vio(); - this->_send_tracked_event(this->_write_event, VC_EVENT_WRITE_READY, &this->_write_vio); - - return &this->_write_vio; -} - -void -QUICBidirectionalStream::do_io_close(int lerrno) -{ - SET_HANDLER(&QUICBidirectionalStream::state_stream_closed); - - this->_read_vio.buffer.clear(); - this->_read_vio.nbytes = 0; - this->_read_vio.op = VIO::NONE; - this->_read_vio.cont = nullptr; - - this->_write_vio.buffer.clear(); - this->_write_vio.nbytes = 0; - this->_write_vio.op = VIO::NONE; - this->_write_vio.cont = nullptr; -} - -void -QUICBidirectionalStream::do_io_shutdown(ShutdownHowTo_t howto) -{ - ink_assert(false); // unimplemented yet - return; -} - -void -QUICBidirectionalStream::reenable(VIO *vio) -{ - if (vio->op == VIO::READ) { - QUICVStreamDebug("read_vio reenabled"); - - int64_t len = this->_process_read_vio(); - if (len > 0) { - this->_signal_read_event(); - } - } else if (vio->op == VIO::WRITE) { - QUICVStreamDebug("write_vio reenabled"); - - int64_t len = this->_process_write_vio(); - if (len > 0) { - this->_signal_write_event(); - } + if (this->_state.update_with_receiving_frame(frame)) { + this->_notify_state_change(); } + this->_adapter->notify_eos(); + return nullptr; } bool @@ -361,9 +182,9 @@ QUICBidirectionalStream::will_generate_frame(QUICEncryptionLevel level, size_t c if (!this->is_retransmited_frame_queue_empty()) { return true; } - if (this->_write_vio.op != VIO::NONE && this->_write_vio.get_reader()->is_read_avail_more_than(0)) { + if (this->_adapter && this->_adapter->unread_len() > 0) { return true; - }; + } return false; } @@ -386,7 +207,9 @@ QUICBidirectionalStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, return nullptr; } this->_records_rst_stream_frame(level, *static_cast(frame)); - this->_state.update_with_sending_frame(*frame); + if (this->_state.update_with_sending_frame(*frame)) { + this->_notify_state_change(); + } this->_is_reset_sent = true; return frame; } @@ -400,7 +223,9 @@ QUICBidirectionalStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, return nullptr; } this->_records_stop_sending_frame(level, *static_cast(frame)); - this->_state.update_with_sending_frame(*frame); + if (this->_state.update_with_sending_frame(*frame)) { + this->_notify_state_change(); + } this->_is_stop_sending_sent = true; return frame; } @@ -412,91 +237,87 @@ QUICBidirectionalStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, return frame; } - if (this->_write_vio.op != VIO::NONE && this->_state.is_allowed_to_send(QUICFrameType::STREAM)) { - SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread()); + if (!this->_adapter || !this->_state.is_allowed_to_send(QUICFrameType::STREAM)) { + return frame; + } + + uint64_t maximum_data_size = 0; + if (maximum_frame_size <= MAX_STREAM_FRAME_OVERHEAD) { + return frame; + } + maximum_data_size = maximum_frame_size - MAX_STREAM_FRAME_OVERHEAD; + + bool pure_fin = false; + bool fin = false; + if (this->_adapter->is_eos()) { + // Pure FIN stream should be sent regardless status of remote flow controller, because the length is zero. + pure_fin = true; + fin = true; + } - uint64_t maximum_data_size = 0; - if (maximum_frame_size <= MAX_STREAM_FRAME_OVERHEAD) { + uint64_t len = 0; + if (!pure_fin) { + uint64_t data_len = this->_adapter->unread_len(); + if (data_len == 0) { return frame; } - maximum_data_size = maximum_frame_size - MAX_STREAM_FRAME_OVERHEAD; - - bool pure_fin = false; - bool fin = false; - if ((this->_write_vio.nbytes != 0 || this->_write_vio.nbytes != INT64_MAX) && - this->_write_vio.nbytes == static_cast(this->_send_offset)) { - // Pure FIN stream should be sent regardless status of remote flow controller, because the length is zero. - pure_fin = true; - fin = true; - } - uint64_t len = 0; - IOBufferReader *reader = this->_write_vio.get_reader(); - if (!pure_fin) { - uint64_t data_len = reader->block_read_avail(); - if (data_len == 0) { - return frame; - } - - // Check Connection/Stream level credit only if the generating STREAM frame is not pure fin - uint64_t stream_credit = this->_remote_flow_controller.credit(); - if (stream_credit == 0) { - // STREAM_DATA_BLOCKED - frame = - this->_remote_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num); - return frame; - } - - if (connection_credit == 0) { - // BLOCKED - BLOCKED frame will be sent by connection level remote flow controller - return frame; - } - - len = std::min(data_len, std::min(maximum_data_size, std::min(stream_credit, connection_credit))); - - // data_len, maximum_data_size, stream_credit and connection_credit are already checked they're larger than 0 - ink_assert(len != 0); - - if (this->_write_vio.nbytes == static_cast(this->_send_offset + len)) { - fin = true; - } + // Check Connection/Stream level credit only if the generating STREAM frame is not pure fin + uint64_t stream_credit = this->_remote_flow_controller.credit(); + if (stream_credit == 0) { + // STREAM_DATA_BLOCKED + frame = + this->_remote_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num); + return frame; } - Ptr block = make_ptr(reader->get_current_block()->clone()); - block->consume(reader->start_offset); - block->_end = std::min(block->start() + len, block->_buf_end); - ink_assert(static_cast(block->read_avail()) == len); - - // STREAM - Pure FIN or data length is lager than 0 - // FIXME has_length_flag and has_offset_flag should be configurable - frame = QUICFrameFactory::create_stream_frame(buf, block, this->_id, this->_send_offset, fin, true, true, - this->_issue_frame_id(), this); - if (!this->_state.is_allowed_to_send(*frame)) { - QUICStreamDebug("Canceled sending %s frame due to the stream state", QUICDebugNames::frame_type(frame->type())); + if (connection_credit == 0) { + // BLOCKED - BLOCKED frame will be sent by connection level remote flow controller return frame; } - if (!pure_fin) { - int ret = this->_remote_flow_controller.update(this->_send_offset + len); - // We cannot cancel sending the frame after updating the flow controller + len = std::min(data_len, std::min(maximum_data_size, std::min(stream_credit, connection_credit))); - // Calling update always success, because len is always less than stream_credit - ink_assert(ret == 0); + // data_len, maximum_data_size, stream_credit and connection_credit are already checked they're larger than 0 + ink_assert(len != 0); - QUICVStreamFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller.current_offset(), - this->_remote_flow_controller.current_limit()); - if (this->_remote_flow_controller.current_offset() == this->_remote_flow_controller.current_limit()) { - QUICStreamDebug("Flow Controller will block sending a STREAM frame"); - } + if (this->_adapter->total_len() == this->_send_offset + len) { + fin = true; + } + } + + Ptr block = this->_adapter->read(len); + ink_assert(static_cast(block->read_avail()) == len); + + // STREAM - Pure FIN or data length is lager than 0 + // FIXME has_length_flag and has_offset_flag should be configurable + frame = QUICFrameFactory::create_stream_frame(buf, block, this->_id, this->_send_offset, fin, true, true, this->_issue_frame_id(), + this); + if (!this->_state.is_allowed_to_send(*frame)) { + QUICStreamDebug("Canceled sending %s frame due to the stream state", QUICDebugNames::frame_type(frame->type())); + return frame; + } - reader->consume(len); - this->_send_offset += len; - this->_write_vio.ndone += len; + if (!pure_fin) { + int ret = this->_remote_flow_controller.update(this->_send_offset + len); + // We cannot cancel sending the frame after updating the flow controller + + // Calling update always success, because len is always less than stream_credit + ink_assert(ret == 0); + + QUICVStreamFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller.current_offset(), + this->_remote_flow_controller.current_limit()); + if (this->_remote_flow_controller.current_offset() == this->_remote_flow_controller.current_limit()) { + QUICStreamDebug("Flow Controller will block sending a STREAM frame"); } - this->_records_stream_frame(level, *static_cast(frame)); - this->_signal_write_event(); - this->_state.update_with_sending_frame(*frame); + this->_send_offset += len; + } + this->_records_stream_frame(level, *static_cast(frame)); + + this->_adapter->encourge_write(); + if (this->_state.update_with_sending_frame(*frame)) { + this->_notify_state_change(); } return frame; @@ -522,7 +343,9 @@ QUICBidirectionalStream::_on_frame_acked(QUICFrameInformationUPtr &info) break; } - this->_state.update_on_ack(); + if (this->_state.update_on_ack()) { + this->_notify_state_change(); + } } void @@ -564,13 +387,17 @@ QUICBidirectionalStream::reset(QUICStreamErrorUPtr error) void QUICBidirectionalStream::on_read() { - this->_state.update_on_read(); + if (this->_state.update_on_read()) { + this->_notify_state_change(); + } } void QUICBidirectionalStream::on_eos() { - this->_state.update_on_eos(); + if (this->_state.update_on_eos()) { + this->_notify_state_change(); + } } QUICOffset @@ -584,3 +411,9 @@ QUICBidirectionalStream::largest_offset_sent() const { return this->_remote_flow_controller.current_offset(); } + +void +QUICBidirectionalStream::_on_adapter_updated() +{ + this->_progress_sa.set_stream_adapter(this->_adapter); +} diff --git a/iocore/net/quic/QUICBidirectionalStream.h b/iocore/net/quic/QUICBidirectionalStream.h index d64f899bd6c..6f0118bcd72 100644 --- a/iocore/net/quic/QUICBidirectionalStream.h +++ b/iocore/net/quic/QUICBidirectionalStream.h @@ -25,24 +25,18 @@ #include "QUICStream.h" -class QUICBidirectionalStream : public QUICStreamVConnection, public QUICTransferProgressProvider +class QUICBidirectionalStream : public QUICStream, public QUICTransferProgressProvider { public: QUICBidirectionalStream(QUICRTTProvider *rtt_provider, QUICConnectionInfoProvider *cinfo, QUICStreamId sid, uint64_t recv_max_stream_data, uint64_t send_max_stream_data); QUICBidirectionalStream() - : QUICStreamVConnection(), - _remote_flow_controller(0, 0), - _local_flow_controller(nullptr, 0, 0), - _state(nullptr, nullptr, nullptr, nullptr) + : QUICStream(), _remote_flow_controller(0, 0), _local_flow_controller(nullptr, 0, 0), _state(nullptr, nullptr, nullptr, nullptr) { } ~QUICBidirectionalStream() {} - int state_stream_open(int event, void *data); - int state_stream_closed(int event, void *data); - // QUICFrameGenerator bool will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) override; QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size, @@ -54,13 +48,6 @@ class QUICBidirectionalStream : public QUICStreamVConnection, public QUICTransfe virtual QUICConnectionErrorUPtr recv(const QUICStopSendingFrame &frame) override; virtual QUICConnectionErrorUPtr recv(const QUICRstStreamFrame &frame) override; - // Implement VConnection Interface. - VIO *do_io_read(Continuation *c, int64_t nbytes = INT64_MAX, MIOBuffer *buf = 0) override; - VIO *do_io_write(Continuation *c = nullptr, int64_t nbytes = INT64_MAX, IOBufferReader *buf = 0, bool owner = false) override; - void do_io_close(int lerrno = -1) override; - void do_io_shutdown(ShutdownHowTo_t howto) override; - void reenable(VIO *vio) override; - void stop_sending(QUICStreamErrorUPtr error) override; void reset(QUICStreamErrorUPtr error) override; @@ -79,6 +66,9 @@ class QUICBidirectionalStream : public QUICStreamVConnection, public QUICTransfe QUICOffset largest_offset_received() const override; QUICOffset largest_offset_sent() const override; +protected: + virtual void _on_adapter_updated() override; + private: QUICStreamErrorUPtr _reset_reason = nullptr; bool _is_reset_sent = false; @@ -88,7 +78,7 @@ class QUICBidirectionalStream : public QUICStreamVConnection, public QUICTransfe bool _is_transfer_complete = false; bool _is_reset_complete = false; - QUICTransferProgressProviderVIO _progress_vio = {this->_write_vio}; + QUICTransferProgressProviderSA _progress_sa; QUICRemoteStreamFlowController _remote_flow_controller; QUICLocalStreamFlowController _local_flow_controller; @@ -98,7 +88,6 @@ class QUICBidirectionalStream : public QUICStreamVConnection, public QUICTransfe // TODO: Consider to replace with ts/RbTree.h or other data structure QUICIncomingStreamFrameBuffer _received_stream_frame_buffer; - // FIXME Unidirectional streams should use either ReceiveStreamState or SendStreamState QUICBidirectionalStreamStateMachine _state; // QUICFrameGenerator diff --git a/iocore/net/quic/QUICPacket.h b/iocore/net/quic/QUICPacket.h index e9d02127328..49d0b145f89 100644 --- a/iocore/net/quic/QUICPacket.h +++ b/iocore/net/quic/QUICPacket.h @@ -275,7 +275,7 @@ class QUICShortHeaderPacketR : public QUICPacketR QUICKeyPhase _key_phase; QUICPacketNumber _packet_number; int _packet_number_len; - QUICConnectionId _dcid; + QUICConnectionId _dcid = QUICConnectionId::ZERO(); }; class QUICStatelessResetPacket : public QUICPacket diff --git a/iocore/net/quic/QUICPacketFactory.cc b/iocore/net/quic/QUICPacketFactory.cc index 2fcb1a29549..279cf7d21e3 100644 --- a/iocore/net/quic/QUICPacketFactory.cc +++ b/iocore/net/quic/QUICPacketFactory.cc @@ -75,8 +75,8 @@ QUICPacketFactory::create(uint8_t *packet_buf, UDPConnection *udp_con, IpEndpoin QUICPacketType type; QUICVersion version; - QUICConnectionId dcid; - QUICConnectionId scid; + QUICConnectionId dcid = QUICConnectionId::ZERO(); + QUICConnectionId scid = QUICConnectionId::ZERO(); QUICPacketNumber packet_number; QUICKeyPhase key_phase; diff --git a/iocore/net/quic/QUICStream.cc b/iocore/net/quic/QUICStream.cc index 93ac8f1f733..932d1c14015 100644 --- a/iocore/net/quic/QUICStream.cc +++ b/iocore/net/quic/QUICStream.cc @@ -62,6 +62,13 @@ QUICStream::final_offset() const return 0; } +void +QUICStream::set_io_adapter(QUICStreamAdapter *adapter) +{ + this->_adapter = adapter; + this->_on_adapter_updated(); +} + QUICOffset QUICStream::reordered_bytes() const { @@ -153,6 +160,20 @@ QUICStream::_records_crypto_frame(QUICEncryptionLevel level, const QUICCryptoFra this->_records_frame(frame.id(), std::move(info)); } +void +QUICStream::set_state_listener(QUICStreamStateListener *listener) +{ + this->_state_listener = listener; +} + +void +QUICStream::_notify_state_change() +{ + if (this->_state_listener) { + // TODO Check own state and call an appropriate callback function + } +} + void QUICStream::reset(QUICStreamErrorUPtr error) { @@ -184,140 +205,3 @@ void QUICStream::on_read() { } - -// -// QUICStreamVConnection -// -QUICStreamVConnection::~QUICStreamVConnection() -{ - if (this->_read_event) { - this->_read_event->cancel(); - this->_read_event = nullptr; - } - - if (this->_write_event) { - this->_write_event->cancel(); - this->_write_event = nullptr; - } -} - -void -QUICStreamVConnection::_write_to_read_vio(QUICOffset offset, const uint8_t *data, uint64_t data_length, bool fin) -{ - SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread()); - - uint64_t bytes_added = this->_read_vio.buffer.writer()->write(data, data_length); - - // Until receive FIN flag, keep nbytes INT64_MAX - if (fin && bytes_added == data_length) { - this->_read_vio.nbytes = offset + data_length; - } -} - -/** - * Replace existing event only if the new event is different than the inprogress event - */ -Event * -QUICStreamVConnection::_send_tracked_event(Event *event, int send_event, VIO *vio) -{ - if (event != nullptr) { - if (event->callback_event != send_event) { - event->cancel(); - event = nullptr; - } - } - - if (event == nullptr) { - event = this_ethread()->schedule_imm(this, send_event, vio); - } - - return event; -} - -/** - * @brief Signal event to this->_read_vio.cont - */ -void -QUICStreamVConnection::_signal_read_event() -{ - if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) { - return; - } - MUTEX_TRY_LOCK(lock, this->_read_vio.mutex, this_ethread()); - - int event = this->_read_vio.nbytes == INT64_MAX ? VC_EVENT_READ_READY : VC_EVENT_READ_COMPLETE; - - if (lock.is_locked()) { - this->_read_vio.cont->handleEvent(event, &this->_read_vio); - } else { - this_ethread()->schedule_imm(this->_read_vio.cont, event, &this->_read_vio); - } -} - -/** - * @brief Signal event to this->_write_vio.cont - */ -void -QUICStreamVConnection::_signal_write_event() -{ - if (this->_write_vio.cont == nullptr || this->_write_vio.op == VIO::NONE) { - return; - } - MUTEX_TRY_LOCK(lock, this->_write_vio.mutex, this_ethread()); - - int event = this->_write_vio.ntodo() ? VC_EVENT_WRITE_READY : VC_EVENT_WRITE_COMPLETE; - - if (lock.is_locked()) { - this->_write_vio.cont->handleEvent(event, &this->_write_vio); - } else { - this_ethread()->schedule_imm(this->_write_vio.cont, event, &this->_write_vio); - } -} - -/** - * @brief Signal event to this->_write_vio.cont - */ -void -QUICStreamVConnection::_signal_read_eos_event() -{ - if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) { - return; - } - MUTEX_TRY_LOCK(lock, this->_read_vio.mutex, this_ethread()); - - int event = VC_EVENT_EOS; - - if (lock.is_locked()) { - this->_write_vio.cont->handleEvent(event, &this->_write_vio); - } else { - this_ethread()->schedule_imm(this->_read_vio.cont, event, &this->_read_vio); - } -} - -int64_t -QUICStreamVConnection::_process_read_vio() -{ - if (this->_read_vio.cont == nullptr || this->_read_vio.op == VIO::NONE) { - return 0; - } - - // Pass through. Read operation is done by QUICStream::recv(const std::shared_ptr frame) - // TODO: 1. pop frame from _received_stream_frame_buffer - // 2. write data to _read_vio - - return 0; -} - -/** - * @brief Send STREAM DATA from _response_buffer - * @detail Call _signal_write_event() to indicate event upper layer - */ -int64_t -QUICStreamVConnection::_process_write_vio() -{ - if (this->_write_vio.cont == nullptr || this->_write_vio.op == VIO::NONE) { - return 0; - } - - return 0; -} diff --git a/iocore/net/quic/QUICStream.h b/iocore/net/quic/QUICStream.h index afeb03c4a23..d6cdf06ab07 100644 --- a/iocore/net/quic/QUICStream.h +++ b/iocore/net/quic/QUICStream.h @@ -37,6 +37,9 @@ #include "QUICFrameRetransmitter.h" #include "QUICDebugNames.h" +class QUICStreamAdapter; +class QUICStreamStateListener; + /** * @brief QUIC Stream * TODO: This is similar to Http2Stream. Need to think some integration. @@ -54,6 +57,14 @@ class QUICStream : public QUICFrameGenerator, public QUICFrameRetransmitter bool is_bidirectional() const; QUICOffset final_offset() const; + /** + * Set an adapter to read/write data from/to this stream + * + * This is an interface for QUICApplication. An application can set an adapter + * to access data in the way the applications wants. + */ + void set_io_adapter(QUICStreamAdapter *adapter); + /* * QUICApplication need to call one of these functions when it process VC_EVENT_* */ @@ -74,6 +85,8 @@ class QUICStream : public QUICFrameGenerator, public QUICFrameRetransmitter virtual void stop_sending(QUICStreamErrorUPtr error); virtual void reset(QUICStreamErrorUPtr error); + void set_state_listener(QUICStreamStateListener *listener); + LINK(QUICStream, link); protected: @@ -82,41 +95,23 @@ class QUICStream : public QUICFrameGenerator, public QUICFrameRetransmitter QUICOffset _send_offset = 0; QUICOffset _reordered_bytes = 0; + QUICStreamAdapter *_adapter = nullptr; + QUICStreamStateListener *_state_listener = nullptr; + + virtual void _on_adapter_updated(){}; + + void _notify_state_change(); + void _records_rst_stream_frame(QUICEncryptionLevel level, const QUICRstStreamFrame &frame); void _records_stream_frame(QUICEncryptionLevel level, const QUICStreamFrame &frame); void _records_stop_sending_frame(QUICEncryptionLevel level, const QUICStopSendingFrame &frame); void _records_crypto_frame(QUICEncryptionLevel level, const QUICCryptoFrame &frame); }; -// This is VConnection class for VIO operation. -class QUICStreamVConnection : public VConnection, public QUICStream +class QUICStreamStateListener { public: - QUICStreamVConnection(QUICConnectionInfoProvider *cinfo, QUICStreamId sid) : VConnection(nullptr), QUICStream(cinfo, sid) - { - mutex = new_ProxyMutex(); - } - - QUICStreamVConnection() : VConnection(nullptr) {} - virtual ~QUICStreamVConnection(); - - LINK(QUICStreamVConnection, link); - -protected: - virtual int64_t _process_read_vio(); - virtual int64_t _process_write_vio(); - void _signal_read_event(); - void _signal_write_event(); - void _signal_read_eos_event(); - Event *_send_tracked_event(Event *, int, VIO *); - - void _write_to_read_vio(QUICOffset offset, const uint8_t *data, uint64_t data_length, bool fin); - - VIO _read_vio; - VIO _write_vio; - - Event *_read_event = nullptr; - Event *_write_event = nullptr; + virtual void on_stream_state_close(const QUICStream *stream) = 0; }; #define QUICStreamDebug(fmt, ...) \ diff --git a/iocore/net/quic/QUICStreamAdapter.cc b/iocore/net/quic/QUICStreamAdapter.cc new file mode 100644 index 00000000000..8534781f3c5 --- /dev/null +++ b/iocore/net/quic/QUICStreamAdapter.cc @@ -0,0 +1,32 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "QUICStreamAdapter.h" + +Ptr +QUICStreamAdapter::read(size_t len) +{ + auto ret = this->_read(len); + this->_stream.on_read(); + return ret; +} diff --git a/iocore/net/quic/QUICStreamAdapter.h b/iocore/net/quic/QUICStreamAdapter.h new file mode 100644 index 00000000000..3c3a0e331eb --- /dev/null +++ b/iocore/net/quic/QUICStreamAdapter.h @@ -0,0 +1,65 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "QUICStream.h" + +class QUICStreamAdapter +{ +public: + QUICStreamAdapter(QUICStream &stream) : _stream(stream) {} + virtual ~QUICStreamAdapter() = default; + + QUICStream & + stream() + { + return _stream; + } + + virtual int64_t write(QUICOffset offset, const uint8_t *data, uint64_t data_length, bool fin) = 0; + Ptr read(size_t len); + virtual bool is_eos() = 0; + virtual uint64_t unread_len() = 0; + virtual uint64_t read_len() = 0; + virtual uint64_t total_len() = 0; + + /** + * Tell the application that there is data to read + */ + virtual void encourge_read() = 0; + + /** + * Tell the application that there is some space to write data + */ + virtual void encourge_write() = 0; + + /** + * Tell the application that there is no more data to read + */ + virtual void notify_eos() = 0; + +protected: + virtual Ptr _read(size_t len) = 0; + QUICStream &_stream; +}; diff --git a/iocore/net/quic/QUICStreamFactory.cc b/iocore/net/quic/QUICStreamFactory.cc index 548be1050fa..1f09c752141 100644 --- a/iocore/net/quic/QUICStreamFactory.cc +++ b/iocore/net/quic/QUICStreamFactory.cc @@ -26,10 +26,10 @@ #include "QUICUnidirectionalStream.h" #include "QUICStreamFactory.h" -QUICStreamVConnection * +QUICStream * QUICStreamFactory::create(QUICStreamId sid, uint64_t local_max_stream_data, uint64_t remote_max_stream_data) { - QUICStreamVConnection *stream = nullptr; + QUICStream *stream = nullptr; switch (QUICTypeUtil::detect_stream_direction(sid, this->_info->direction())) { case QUICStreamDirection::BIDIRECTIONAL: stream = new QUICBidirectionalStream(this->_rtt_provider, this->_info, sid, local_max_stream_data, remote_max_stream_data); @@ -50,7 +50,7 @@ QUICStreamFactory::create(QUICStreamId sid, uint64_t local_max_stream_data, uint } void -QUICStreamFactory::delete_stream(QUICStreamVConnection *stream) +QUICStreamFactory::delete_stream(QUICStream *stream) { delete stream; } diff --git a/iocore/net/quic/QUICStreamFactory.h b/iocore/net/quic/QUICStreamFactory.h index fd497bf2b1a..43690edbd3c 100644 --- a/iocore/net/quic/QUICStreamFactory.h +++ b/iocore/net/quic/QUICStreamFactory.h @@ -25,7 +25,7 @@ #include "QUICTypes.h" -class QUICStreamVConnection; +class QUICStream; // PS: this class function should not static because of THREAD_ALLOC and THREAD_FREE class QUICStreamFactory @@ -35,10 +35,10 @@ class QUICStreamFactory ~QUICStreamFactory() {} // create a bidistream, send only stream or receive only stream - QUICStreamVConnection *create(QUICStreamId sid, uint64_t recv_max_stream_data, uint64_t send_max_stream_data); + QUICStream *create(QUICStreamId sid, uint64_t recv_max_stream_data, uint64_t send_max_stream_data); // delete stream by stream type - void delete_stream(QUICStreamVConnection *stream); + void delete_stream(QUICStream *stream); private: QUICRTTProvider *_rtt_provider = nullptr; diff --git a/iocore/net/quic/QUICStreamManager.cc b/iocore/net/quic/QUICStreamManager.cc index 54a6d20958b..2338321623f 100644 --- a/iocore/net/quic/QUICStreamManager.cc +++ b/iocore/net/quic/QUICStreamManager.cc @@ -94,17 +94,14 @@ QUICConnectionErrorUPtr QUICStreamManager::create_stream(QUICStreamId stream_id) { // TODO: check stream_id - QUICConnectionErrorUPtr error = nullptr; - QUICStreamVConnection *stream_vc = this->_find_or_create_stream_vc(stream_id); - if (!stream_vc) { + QUICConnectionErrorUPtr error = nullptr; + QUICStream *stream = this->_find_or_create_stream(stream_id); + if (!stream) { return std::make_unique(QUICTransErrorCode::STREAM_LIMIT_ERROR); } QUICApplication *application = this->_app_map->get(stream_id); - - if (!application->is_stream_set(stream_vc)) { - application->set_stream(stream_vc); - } + application->on_new_stream(*stream); return error; } @@ -136,7 +133,7 @@ QUICStreamManager::create_bidi_stream(QUICStreamId &new_stream_id) void QUICStreamManager::reset_stream(QUICStreamId stream_id, QUICStreamErrorUPtr error) { - auto stream = this->_find_stream_vc(stream_id); + auto stream = this->_find_stream(stream_id); stream->reset(std::move(error)); } @@ -177,7 +174,7 @@ QUICStreamManager::handle_frame(QUICEncryptionLevel level, const QUICFrame &fram QUICConnectionErrorUPtr QUICStreamManager::_handle_frame(const QUICMaxStreamDataFrame &frame) { - QUICStreamVConnection *stream = this->_find_or_create_stream_vc(frame.stream_id()); + QUICStream *stream = this->_find_or_create_stream(frame.stream_id()); if (stream) { return stream->recv(frame); } else { @@ -188,7 +185,7 @@ QUICStreamManager::_handle_frame(const QUICMaxStreamDataFrame &frame) QUICConnectionErrorUPtr QUICStreamManager::_handle_frame(const QUICStreamDataBlockedFrame &frame) { - QUICStreamVConnection *stream = this->_find_or_create_stream_vc(frame.stream_id()); + QUICStream *stream = this->_find_or_create_stream(frame.stream_id()); if (stream) { return stream->recv(frame); } else { @@ -199,24 +196,18 @@ QUICStreamManager::_handle_frame(const QUICStreamDataBlockedFrame &frame) QUICConnectionErrorUPtr QUICStreamManager::_handle_frame(const QUICStreamFrame &frame) { - QUICStreamVConnection *stream = this->_find_or_create_stream_vc(frame.stream_id()); - if (!stream) { + QUICStream *stream = this->_find_or_create_stream(frame.stream_id()); + if (stream) { + return stream->recv(frame); + } else { return std::make_unique(QUICTransErrorCode::STREAM_LIMIT_ERROR); } - - QUICApplication *application = this->_app_map->get(frame.stream_id()); - - if (application && !application->is_stream_set(stream)) { - application->set_stream(stream); - } - - return stream->recv(frame); } QUICConnectionErrorUPtr QUICStreamManager::_handle_frame(const QUICRstStreamFrame &frame) { - QUICStream *stream = this->_find_or_create_stream_vc(frame.stream_id()); + QUICStream *stream = this->_find_or_create_stream(frame.stream_id()); if (stream) { return stream->recv(frame); } else { @@ -227,7 +218,7 @@ QUICStreamManager::_handle_frame(const QUICRstStreamFrame &frame) QUICConnectionErrorUPtr QUICStreamManager::_handle_frame(const QUICStopSendingFrame &frame) { - QUICStream *stream = this->_find_or_create_stream_vc(frame.stream_id()); + QUICStream *stream = this->_find_or_create_stream(frame.stream_id()); if (stream) { return stream->recv(frame); } else { @@ -247,10 +238,10 @@ QUICStreamManager::_handle_frame(const QUICMaxStreamsFrame &frame) return nullptr; } -QUICStreamVConnection * -QUICStreamManager::_find_stream_vc(QUICStreamId id) +QUICStream * +QUICStreamManager::_find_stream(QUICStreamId id) { - for (QUICStreamVConnection *s = this->stream_list.head; s; s = s->link.next) { + for (QUICStream *s = this->stream_list.head; s; s = s->link.next) { if (s->id() == id) { return s; } @@ -258,10 +249,10 @@ QUICStreamManager::_find_stream_vc(QUICStreamId id) return nullptr; } -QUICStreamVConnection * -QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) +QUICStream * +QUICStreamManager::_find_or_create_stream(QUICStreamId stream_id) { - QUICStreamVConnection *stream = this->_find_stream_vc(stream_id); + QUICStream *stream = this->_find_stream(stream_id); if (!stream) { if (!this->_local_tp) { return nullptr; @@ -353,7 +344,11 @@ QUICStreamManager::_find_or_create_stream_vc(QUICStreamId stream_id) stream = this->_stream_factory.create(stream_id, local_max_stream_data, remote_max_stream_data); ink_assert(stream != nullptr); + stream->set_state_listener(this); this->stream_list.push(stream); + + QUICApplication *application = this->_app_map->get(stream_id); + application->on_new_stream(*stream); } return stream; @@ -365,7 +360,7 @@ QUICStreamManager::total_reordered_bytes() const uint64_t total_bytes = 0; // FIXME Iterating all (open + closed) streams is expensive - for (QUICStreamVConnection *s = this->stream_list.head; s; s = s->link.next) { + for (QUICStream *s = this->stream_list.head; s; s = s->link.next) { total_bytes += s->reordered_bytes(); } return total_bytes; @@ -377,7 +372,7 @@ QUICStreamManager::total_offset_received() const uint64_t total_offset_received = 0; // FIXME Iterating all (open + closed) streams is expensive - for (QUICStreamVConnection *s = this->stream_list.head; s; s = s->link.next) { + for (QUICStream *s = this->stream_list.head; s; s = s->link.next) { total_offset_received += s->largest_offset_received(); } return total_offset_received; @@ -400,7 +395,7 @@ uint32_t QUICStreamManager::stream_count() const { uint32_t count = 0; - for (QUICStreamVConnection *s = this->stream_list.head; s; s = s->link.next) { + for (QUICStream *s = this->stream_list.head; s; s = s->link.next) { ++count; } return count; @@ -429,7 +424,7 @@ QUICStreamManager::will_generate_frame(QUICEncryptionLevel level, size_t current return false; } - for (QUICStreamVConnection *s = this->stream_list.head; s; s = s->link.next) { + for (QUICStream *s = this->stream_list.head; s; s = s->link.next) { if (s->will_generate_frame(level, current_packet_size, ack_eliciting, seq_num)) { return true; } @@ -454,7 +449,7 @@ QUICStreamManager::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint6 } // FIXME We should pick a stream based on priority - for (QUICStreamVConnection *s = this->stream_list.head; s; s = s->link.next) { + for (QUICStream *s = this->stream_list.head; s; s = s->link.next) { frame = s->generate_frame(buf, level, connection_credit, maximum_frame_size, current_packet_size, seq_num); if (frame) { break; @@ -468,6 +463,34 @@ QUICStreamManager::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint6 return frame; } +void +QUICStreamManager::on_stream_state_close(const QUICStream *stream) +{ + auto direction = this->_context->connection_info()->direction(); + switch (QUICTypeUtil::detect_stream_type(stream->id())) { + case QUICStreamType::SERVER_BIDI: + if (direction == NET_VCONNECTION_OUT) { + this->_local_max_streams_bidi += 1; + } + break; + case QUICStreamType::SERVER_UNI: + if (direction == NET_VCONNECTION_OUT) { + this->_local_max_streams_uni += 1; + } + break; + case QUICStreamType::CLIENT_BIDI: + if (direction == NET_VCONNECTION_IN) { + this->_local_max_streams_bidi += 1; + } + break; + case QUICStreamType::CLIENT_UNI: + if (direction == NET_VCONNECTION_IN) { + this->_local_max_streams_uni += 1; + } + break; + } +} + bool QUICStreamManager::_is_level_matched(QUICEncryptionLevel level) { diff --git a/iocore/net/quic/QUICStreamManager.h b/iocore/net/quic/QUICStreamManager.h index f158035a8aa..a52a375d6b0 100644 --- a/iocore/net/quic/QUICStreamManager.h +++ b/iocore/net/quic/QUICStreamManager.h @@ -36,7 +36,7 @@ class QUICTransportParameters; -class QUICStreamManager : public QUICFrameHandler, public QUICFrameGenerator +class QUICStreamManager : public QUICFrameHandler, public QUICFrameGenerator, public QUICStreamStateListener { public: QUICStreamManager(QUICContext *context, QUICApplicationMap *app_map); @@ -58,7 +58,7 @@ class QUICStreamManager : public QUICFrameHandler, public QUICFrameGenerator void set_default_application(QUICApplication *app); - DLL stream_list; + DLL stream_list; // QUICFrameHandler virtual std::vector interests() override; @@ -69,12 +69,15 @@ class QUICStreamManager : public QUICFrameHandler, public QUICFrameGenerator QUICFrame *generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t connection_credit, uint16_t maximum_frame_size, size_t current_packet_size, uint32_t timestamp) override; + // QUICStreamStateListener + void on_stream_state_close(const QUICStream *stream) override; + protected: virtual bool _is_level_matched(QUICEncryptionLevel level) override; private: - QUICStreamVConnection *_find_stream_vc(QUICStreamId id); - QUICStreamVConnection *_find_or_create_stream_vc(QUICStreamId stream_id); + QUICStream *_find_stream(QUICStreamId id); + QUICStream *_find_or_create_stream(QUICStreamId stream_id); void _add_total_offset_sent(uint32_t sent_byte); QUICConnectionErrorUPtr _handle_frame(const QUICStreamFrame &frame); QUICConnectionErrorUPtr _handle_frame(const QUICRstStreamFrame &frame); diff --git a/iocore/net/quic/QUICStreamState.cc b/iocore/net/quic/QUICStreamState.cc index 7c5b8d1050a..e5ddb6c5fae 100644 --- a/iocore/net/quic/QUICStreamState.cc +++ b/iocore/net/quic/QUICStreamState.cc @@ -72,14 +72,16 @@ QUICReceiveStreamStateMachine::is_allowed_to_receive(QUICFrameType type) const return false; } -void +bool QUICReceiveStreamStateMachine::update_with_sending_frame(const QUICFrame &frame) { + return false; } -void +bool QUICReceiveStreamStateMachine::update_with_receiving_frame(const QUICFrame &frame) { + bool state_changed = false; // The receiving part of a stream initiated by a peer (types 1 and 3 for a client, or 0 and 2 for a server) is created when the // first STREAM, STREAM_DATA_BLOCKED, or RESET_STREAM is received for that stream. QUICReceiveStreamState state = this->get(); @@ -87,32 +89,32 @@ QUICReceiveStreamStateMachine::update_with_receiving_frame(const QUICFrame &fram if (state == QUICReceiveStreamState::Init && (type == QUICFrameType::STREAM || type == QUICFrameType::STREAM_DATA_BLOCKED || type == QUICFrameType::RESET_STREAM)) { - this->_set_state(QUICReceiveStreamState::Recv); + state_changed |= this->_set_state(QUICReceiveStreamState::Recv); } switch (this->get()) { case QUICReceiveStreamState::Recv: if (type == QUICFrameType::STREAM) { if (static_cast(frame).has_fin_flag()) { - this->_set_state(QUICReceiveStreamState::SizeKnown); + state_changed |= this->_set_state(QUICReceiveStreamState::SizeKnown); if (this->_in_progress->is_transfer_complete()) { - this->_set_state(QUICReceiveStreamState::DataRecvd); + state_changed |= this->_set_state(QUICReceiveStreamState::DataRecvd); } } } else if (type == QUICFrameType::RESET_STREAM) { - this->_set_state(QUICReceiveStreamState::ResetRecvd); + state_changed |= this->_set_state(QUICReceiveStreamState::ResetRecvd); } break; case QUICReceiveStreamState::SizeKnown: if (type == QUICFrameType::STREAM && this->_in_progress->is_transfer_complete()) { - this->_set_state(QUICReceiveStreamState::DataRecvd); + state_changed |= this->_set_state(QUICReceiveStreamState::DataRecvd); } else if (type == QUICFrameType::RESET_STREAM) { - this->_set_state(QUICReceiveStreamState::ResetRecvd); + state_changed |= this->_set_state(QUICReceiveStreamState::ResetRecvd); } break; case QUICReceiveStreamState::DataRecvd: if (type == QUICFrameType::STREAM && this->_in_progress->is_transfer_complete()) { - this->_set_state(QUICReceiveStreamState::ResetRecvd); + state_changed |= this->_set_state(QUICReceiveStreamState::ResetRecvd); } break; case QUICReceiveStreamState::Init: @@ -124,23 +126,25 @@ QUICReceiveStreamStateMachine::update_with_receiving_frame(const QUICFrame &fram ink_assert(!"Unknown state"); break; } + return state_changed; } -void +bool QUICReceiveStreamStateMachine::update_on_read() { if (this->_in_progress->is_transfer_complete()) { - this->_set_state(QUICReceiveStreamState::DataRead); + return this->_set_state(QUICReceiveStreamState::DataRead); } + return false; } -void +bool QUICReceiveStreamStateMachine::update_on_eos() { - this->_set_state(QUICReceiveStreamState::ResetRead); + return this->_set_state(QUICReceiveStreamState::ResetRead); } -void +bool QUICReceiveStreamStateMachine::update(const QUICSendStreamState state) { // The receiving part of a stream enters the "Recv" state when the sending part of a bidirectional stream initiated by the @@ -148,12 +152,14 @@ QUICReceiveStreamStateMachine::update(const QUICSendStreamState state) switch (this->get()) { case QUICReceiveStreamState::Init: if (state == QUICSendStreamState::Ready) { - this->_set_state(QUICReceiveStreamState::Recv); + return this->_set_state(QUICReceiveStreamState::Recv); } break; default: break; } + + return false; } // ---------- QUICSendStreamState ------------- @@ -251,29 +257,30 @@ QUICSendStreamStateMachine::is_allowed_to_receive(QUICFrameType type) const return false; } -void +bool QUICSendStreamStateMachine::update_with_sending_frame(const QUICFrame &frame) { + bool state_changed = false; QUICSendStreamState state = this->get(); QUICFrameType type = frame.type(); if (state == QUICSendStreamState::Ready && (type == QUICFrameType::STREAM || type == QUICFrameType::STREAM_DATA_BLOCKED || type == QUICFrameType::RESET_STREAM)) { - this->_set_state(QUICSendStreamState::Send); + state_changed |= this->_set_state(QUICSendStreamState::Send); } switch (this->get()) { case QUICSendStreamState::Send: if (type == QUICFrameType::STREAM) { if (static_cast(frame).has_fin_flag()) { - this->_set_state(QUICSendStreamState::DataSent); + state_changed |= this->_set_state(QUICSendStreamState::DataSent); } } else if (type == QUICFrameType::RESET_STREAM) { - this->_set_state(QUICSendStreamState::ResetSent); + state_changed |= this->_set_state(QUICSendStreamState::ResetSent); } break; case QUICSendStreamState::DataSent: if (type == QUICFrameType::RESET_STREAM) { - this->_set_state(QUICSendStreamState::ResetSent); + state_changed |= this->_set_state(QUICSendStreamState::ResetSent); } break; case QUICSendStreamState::Init: @@ -286,37 +293,42 @@ QUICSendStreamStateMachine::update_with_sending_frame(const QUICFrame &frame) ink_assert(!"Unknown state"); break; } + return state_changed; } -void +bool QUICSendStreamStateMachine::update_with_receiving_frame(const QUICFrame &frame) { + return false; } -void +bool QUICSendStreamStateMachine::update_on_ack() { if (this->_out_progress->is_transfer_complete()) { - this->_set_state(QUICSendStreamState::DataRecvd); + return this->_set_state(QUICSendStreamState::DataRecvd); } else if (this->_out_progress->is_cancelled()) { - this->_set_state(QUICSendStreamState::ResetRecvd); + return this->_set_state(QUICSendStreamState::ResetRecvd); } + return false; } -void +bool QUICSendStreamStateMachine::update(const QUICReceiveStreamState state) { + bool state_changed = false; // The sending part of a bidirectional stream initiated by a peer (type 0 for a server, type 1 for a client) enters the "Ready" // state then immediately transitions to the "Send" state if the receiving part enters the "Recv" state (Section 3.2). switch (this->get()) { case QUICSendStreamState::Ready: if (state == QUICReceiveStreamState::Recv) { - this->_set_state(QUICSendStreamState::Send); + state_changed |= this->_set_state(QUICSendStreamState::Send); } break; default: break; } + return state_changed; } // ---------QUICBidirectionalStreamState ----------- @@ -369,48 +381,56 @@ QUICBidirectionalStreamStateMachine::get() const } } -void +bool QUICBidirectionalStreamStateMachine::update_with_sending_frame(const QUICFrame &frame) { + bool state_changed = false; + // The receiving part of a stream enters the "Recv" state when the sending part of a bidirectional stream initiated by the // endpoint (type 0 for a client, type 1 for a server) enters the "Ready" state. - this->_send_stream_state.update_with_sending_frame(frame); + state_changed |= this->_send_stream_state.update_with_sending_frame(frame); // PS: It should not happen because we initialize the send side and read side together. And the SendState has the default state // "Ready". But to obey the specs, we do this as follow. if (this->_send_stream_state.get() == QUICSendStreamState::Ready && this->_recv_stream_state.get() == QUICReceiveStreamState::Init) { - this->_recv_stream_state.update(this->_send_stream_state.get()); + state_changed |= this->_recv_stream_state.update(this->_send_stream_state.get()); } + + return state_changed; } -void +bool QUICBidirectionalStreamStateMachine::update_with_receiving_frame(const QUICFrame &frame) { + bool state_changed = false; + // The sending part of a bidirectional stream initiated by a peer (type 0 for a server, type 1 for a client) enters the "Ready" // state then immediately transitions to the "Send" state if the receiving part enters the "Recv" state (Section 3.2). - this->_recv_stream_state.update_with_receiving_frame(frame); + state_changed |= this->_recv_stream_state.update_with_receiving_frame(frame); if (this->_send_stream_state.get() == QUICSendStreamState::Ready && this->_recv_stream_state.get() == QUICReceiveStreamState::Recv) { - this->_send_stream_state.update(this->_recv_stream_state.get()); + state_changed |= this->_send_stream_state.update(this->_recv_stream_state.get()); } + + return state_changed; } -void +bool QUICBidirectionalStreamStateMachine::update_on_ack() { - this->_send_stream_state.update_on_ack(); + return this->_send_stream_state.update_on_ack(); } -void +bool QUICBidirectionalStreamStateMachine::update_on_read() { - this->_recv_stream_state.update_on_read(); + return this->_recv_stream_state.update_on_read(); } -void +bool QUICBidirectionalStreamStateMachine::update_on_eos() { - this->_recv_stream_state.update_on_eos(); + return this->_recv_stream_state.update_on_eos(); } bool diff --git a/iocore/net/quic/QUICStreamState.h b/iocore/net/quic/QUICStreamState.h index f95bfc04712..235bfa384ee 100644 --- a/iocore/net/quic/QUICStreamState.h +++ b/iocore/net/quic/QUICStreamState.h @@ -67,8 +67,8 @@ template class QUICStreamStateMachine return this->_state; } - virtual void update_with_sending_frame(const QUICFrame &frame) = 0; - virtual void update_with_receiving_frame(const QUICFrame &frame) = 0; + [[nodiscard]] virtual bool update_with_sending_frame(const QUICFrame &frame) = 0; + [[nodiscard]] virtual bool update_with_receiving_frame(const QUICFrame &frame) = 0; virtual bool is_allowed_to_send(QUICFrameType type) const = 0; virtual bool is_allowed_to_send(const QUICFrame &frame) const = 0; @@ -76,11 +76,16 @@ template class QUICStreamStateMachine virtual bool is_allowed_to_receive(const QUICFrame &frame) const = 0; protected: - void + bool _set_state(T s) { ink_assert(s != T::Init); - this->_state = s; + if (this->_state != s) { + this->_state = s; + return true; + } else { + return false; + } } private: @@ -109,16 +114,16 @@ class QUICSendStreamStateMachine : public QUICUnidirectionalStreamStateMachine, this->_set_state(QUICSendStreamState::Ready); } - void update_with_sending_frame(const QUICFrame &frame) override; - void update_with_receiving_frame(const QUICFrame &frame) override; - void update_on_ack(); + [[nodiscard]] bool update_with_sending_frame(const QUICFrame &frame) override; + [[nodiscard]] bool update_with_receiving_frame(const QUICFrame &frame) override; + [[nodiscard]] bool update_on_ack(); bool is_allowed_to_send(QUICFrameType type) const override; bool is_allowed_to_send(const QUICFrame &frame) const override; bool is_allowed_to_receive(QUICFrameType type) const override; bool is_allowed_to_receive(const QUICFrame &frame) const override; - void update(const QUICReceiveStreamState opposite_side); + [[nodiscard]] bool update(const QUICReceiveStreamState opposite_side); }; class QUICReceiveStreamStateMachine : public QUICUnidirectionalStreamStateMachine, @@ -130,17 +135,17 @@ class QUICReceiveStreamStateMachine : public QUICUnidirectionalStreamStateMachin { } - void update_with_sending_frame(const QUICFrame &frame) override; - void update_with_receiving_frame(const QUICFrame &frame) override; - void update_on_read(); - void update_on_eos(); + [[nodiscard]] bool update_with_sending_frame(const QUICFrame &frame) override; + [[nodiscard]] bool update_with_receiving_frame(const QUICFrame &frame) override; + [[nodiscard]] bool update_on_read(); + [[nodiscard]] bool update_on_eos(); bool is_allowed_to_send(QUICFrameType type) const override; bool is_allowed_to_send(const QUICFrame &frame) const override; bool is_allowed_to_receive(QUICFrameType type) const override; bool is_allowed_to_receive(const QUICFrame &frame) const override; - void update(const QUICSendStreamState opposite_side); + [[nodiscard]] bool update(const QUICSendStreamState opposite_side); }; class QUICBidirectionalStreamStateMachine : public QUICStreamStateMachine @@ -150,16 +155,16 @@ class QUICBidirectionalStreamStateMachine : public QUICStreamStateMachine_recv_stream_state.update(this->_send_stream_state.get()); + ink_assert(this->_recv_stream_state.update(this->_send_stream_state.get())); }; QUICBidirectionalStreamState get() const override; - void update_with_sending_frame(const QUICFrame &frame) override; - void update_with_receiving_frame(const QUICFrame &frame) override; - void update_on_ack(); - void update_on_read(); - void update_on_eos(); + [[nodiscard]] bool update_with_sending_frame(const QUICFrame &frame) override; + [[nodiscard]] bool update_with_receiving_frame(const QUICFrame &frame) override; + [[nodiscard]] bool update_on_ack(); + [[nodiscard]] bool update_on_read(); + [[nodiscard]] bool update_on_eos(); bool is_allowed_to_send(QUICFrameType type) const override; bool is_allowed_to_send(const QUICFrame &frame) const override; diff --git a/iocore/net/quic/QUICStreamVCAdapter.cc b/iocore/net/quic/QUICStreamVCAdapter.cc new file mode 100644 index 00000000000..b8c4afc89fd --- /dev/null +++ b/iocore/net/quic/QUICStreamVCAdapter.cc @@ -0,0 +1,320 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "I_VConnection.h" +#include "QUICStreamVCAdapter.h" + +QUICStreamVCAdapter::QUICStreamVCAdapter(QUICStream &stream) : QUICStreamAdapter(stream), VConnection(new_ProxyMutex()) +{ + SET_HANDLER(&QUICStreamVCAdapter::state_stream_open); +} + +QUICStreamVCAdapter::~QUICStreamVCAdapter() +{ + if (this->_read_event) { + this->_read_event->cancel(); + this->_read_event = nullptr; + } + + if (this->_write_event) { + this->_write_event->cancel(); + this->_write_event = nullptr; + } +} + +int64_t +QUICStreamVCAdapter::write(QUICOffset offset, const uint8_t *data, uint64_t data_length, bool fin) +{ + uint64_t bytes_added = -1; + if (this->_read_vio.op == VIO::READ) { + SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread()); + + bytes_added = this->_read_vio.get_writer()->write(data, data_length); + + // Until receive FIN flag, keep nbytes INT64_MAX + if (fin && bytes_added == data_length) { + this->_read_vio.nbytes = offset + data_length; + } + } + + return bytes_added; +} + +Ptr +QUICStreamVCAdapter::_read(size_t len) +{ + Ptr block; + + if (this->_write_vio.op == VIO::WRITE) { + SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread()); + + IOBufferReader *reader = this->_write_vio.get_reader(); + block = make_ptr(reader->get_current_block()->clone()); + if (block->size()) { + block->consume(reader->start_offset); + block->_end = std::min(block->start() + len, block->_buf_end); + this->_write_vio.ndone += len; + } + reader->consume(block->size()); + } + + return block; +} + +bool +QUICStreamVCAdapter::is_eos() +{ + if (this->_write_vio.op == VIO::WRITE) { + SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread()); + + if (this->_write_vio.nbytes == INT64_MAX) { + return false; + } + if (this->_write_vio.ntodo() != 0) { + return false; + } + return true; + } else { + return false; + } +} + +uint64_t +QUICStreamVCAdapter::unread_len() +{ + if (this->_write_vio.op == VIO::WRITE) { + SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread()); + return this->_write_vio.get_reader()->block_read_avail(); + } else { + return 0; + } +} + +uint64_t +QUICStreamVCAdapter::read_len() +{ + if (this->_write_vio.op == VIO::WRITE) { + SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread()); + return this->_write_vio.ndone; + } else { + return 0; + } +} + +uint64_t +QUICStreamVCAdapter::total_len() +{ + if (this->_write_vio.op == VIO::WRITE) { + SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread()); + return this->_write_vio.nbytes; + } else { + return 0; + } +} + +/** + * @brief Signal event to this->_read_vio.cont + */ +void +QUICStreamVCAdapter::encourge_read() +{ + if (this->_read_vio.op == VIO::READ) { + SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread()); + + if (this->_read_vio.cont == nullptr) { + return; + } + + int event = this->_read_vio.nbytes == INT64_MAX ? VC_EVENT_READ_READY : VC_EVENT_READ_COMPLETE; + this_ethread()->schedule_imm(this->_read_vio.cont, event, &this->_read_vio); + } +} + +/** + * @brief Signal event to this->_write_vio.cont + */ +void +QUICStreamVCAdapter::encourge_write() +{ + if (this->_write_vio.op == VIO::WRITE) { + SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread()); + + if (this->_write_vio.cont == nullptr) { + return; + } + + int event = this->_write_vio.ntodo() ? VC_EVENT_WRITE_READY : VC_EVENT_WRITE_COMPLETE; + this_ethread()->schedule_imm(this->_write_vio.cont, event, &this->_write_vio); + } +} + +/** + * @brief Signal event to this->_read_vio.cont + */ +void +QUICStreamVCAdapter::notify_eos() +{ + if (this->_read_vio.op == VIO::READ) { + if (this->_read_vio.cont == nullptr) { + return; + } + int event = VC_EVENT_EOS; + + MUTEX_TRY_LOCK(lock, this->_read_vio.mutex, this_ethread()); + if (lock.is_locked()) { + this->_read_vio.cont->handleEvent(event, &this->_read_vio); + } else { + this_ethread()->schedule_imm(this->_read_vio.cont, event, &this->_read_vio); + } + } +} + +// this->_read_vio.nbytes should be INT64_MAX until receive FIN flag +VIO * +QUICStreamVCAdapter::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf) +{ + if (buf) { + this->_read_vio.buffer.writer_for(buf); + } else { + this->_read_vio.buffer.clear(); + } + + this->_read_vio.mutex = c ? c->mutex : this->mutex; + this->_read_vio.cont = c; + this->_read_vio.nbytes = nbytes; + this->_read_vio.ndone = 0; + this->_read_vio.vc_server = this; + this->_read_vio.op = VIO::READ; + + return &this->_read_vio; +} + +VIO * +QUICStreamVCAdapter::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, bool owner) +{ + if (buf) { + this->_write_vio.buffer.reader_for(buf); + } else { + this->_write_vio.buffer.clear(); + } + + this->_write_vio.mutex = c ? c->mutex : this->mutex; + this->_write_vio.cont = c; + this->_write_vio.nbytes = nbytes; + this->_write_vio.ndone = 0; + this->_write_vio.vc_server = this; + this->_write_vio.op = VIO::WRITE; + + return &this->_write_vio; +} + +void +QUICStreamVCAdapter::do_io_close(int lerrno) +{ + SET_HANDLER(&QUICStreamVCAdapter::state_stream_closed); + + this->_read_vio.buffer.clear(); + this->_read_vio.nbytes = 0; + this->_read_vio.op = VIO::NONE; + this->_read_vio.cont = nullptr; + + this->_write_vio.buffer.clear(); + this->_write_vio.nbytes = 0; + this->_write_vio.op = VIO::NONE; + this->_write_vio.cont = nullptr; +} + +void +QUICStreamVCAdapter::do_io_shutdown(ShutdownHowTo_t howto) +{ + ink_assert(false); // unimplemented yet + return; +} + +void +QUICStreamVCAdapter::reenable(VIO *vio) +{ + // TODO We probably need to tell QUICStream that the application consumed received data + // to update receive window here. In other words, we should not update receive window + // until the application consume data. +} + +int +QUICStreamVCAdapter::state_stream_open(int event, void *data) +{ + QUICErrorUPtr error = nullptr; + + switch (event) { + case VC_EVENT_READ_READY: + case VC_EVENT_READ_COMPLETE: { + this->encourge_read(); + break; + } + case VC_EVENT_WRITE_READY: + case VC_EVENT_WRITE_COMPLETE: { + this->encourge_write(); + break; + } + case VC_EVENT_EOS: + case VC_EVENT_ERROR: + case VC_EVENT_INACTIVITY_TIMEOUT: + case VC_EVENT_ACTIVE_TIMEOUT: { + // TODO + ink_assert(false); + break; + } + default: + ink_assert(false); + } + + return EVENT_DONE; +} + +int +QUICStreamVCAdapter::state_stream_closed(int event, void *data) +{ + switch (event) { + case VC_EVENT_READ_READY: + case VC_EVENT_READ_COMPLETE: { + // ignore + break; + } + case VC_EVENT_WRITE_READY: + case VC_EVENT_WRITE_COMPLETE: { + // ignore + break; + } + case VC_EVENT_EOS: + case VC_EVENT_ERROR: + case VC_EVENT_INACTIVITY_TIMEOUT: + case VC_EVENT_ACTIVE_TIMEOUT: { + // TODO + ink_assert(false); + break; + } + default: + ink_assert(false); + } + + return EVENT_DONE; +} diff --git a/iocore/net/quic/QUICStreamVCAdapter.h b/iocore/net/quic/QUICStreamVCAdapter.h new file mode 100644 index 00000000000..36131599544 --- /dev/null +++ b/iocore/net/quic/QUICStreamVCAdapter.h @@ -0,0 +1,105 @@ +/** @file + * + * A brief file description + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include "QUICStreamAdapter.h" +#include "I_IOBuffer.h" + +class QUICStreamVCAdapter : public QUICStreamAdapter, public VConnection +{ +public: + class IOInfo; + + QUICStreamVCAdapter(QUICStream &stream); + virtual ~QUICStreamVCAdapter(); + + // Implement QUICStreamAdapter Interface + int64_t write(QUICOffset offset, const uint8_t *data, uint64_t data_length, bool fin) override; + void encourge_read() override; + bool is_eos() override; + uint64_t unread_len() override; + uint64_t read_len() override; + uint64_t total_len() override; + void encourge_write() override; + void notify_eos() override; + + // Implement VConnection Interface. + VIO *do_io_read(Continuation *c, int64_t nbytes = INT64_MAX, MIOBuffer *buf = 0) override; + VIO *do_io_write(Continuation *c = nullptr, int64_t nbytes = INT64_MAX, IOBufferReader *buf = 0, bool owner = false) override; + void do_io_close(int lerrno = -1) override; + void do_io_shutdown(ShutdownHowTo_t howto) override; + void reenable(VIO *vio) override; + + int state_stream_open(int event, void *data); + int state_stream_closed(int event, void *data); + +protected: + Ptr _read(size_t len) override; + + VIO _read_vio; + VIO _write_vio; + + Event *_read_event = nullptr; + Event *_write_event = nullptr; +}; + +class QUICStreamVCAdapter::IOInfo +{ +public: + IOInfo(QUICStream &stream) + : adapter(stream), read_buffer(new_MIOBuffer(BUFFER_SIZE_INDEX_8K)), write_buffer(new_MIOBuffer(BUFFER_SIZE_INDEX_8K)) + { + } + ~IOInfo() + { + free_MIOBuffer(this->read_buffer); + free_MIOBuffer(this->write_buffer); + } + + void + setup_read_vio(Continuation *c) + { + read_vio = adapter.do_io_read(c, INT64_MAX, read_buffer); + + // This is uncommon but it has basically the same effect as + // read_buffer->alloc_reader, and it allows VIO user to obtain the + // reader by calling read_vio.get_reader() + // It limits a number of readers to one, but it wouldn't be a real + // limitation for this particular usecase in QUICStreamVCAdapter. + read_vio->set_reader(read_buffer->alloc_reader()); + adapter.encourge_read(); + } + + void + setup_write_vio(Continuation *c) + { + write_vio = adapter.do_io_write(c, INT64_MAX, write_buffer->alloc_reader()); + adapter.encourge_write(); + } + + QUICStreamVCAdapter adapter; + MIOBuffer *read_buffer; + MIOBuffer *write_buffer; + VIO *read_vio = nullptr; + VIO *write_vio = nullptr; +}; diff --git a/iocore/net/quic/QUICTransferProgressProvider.cc b/iocore/net/quic/QUICTransferProgressProvider.cc new file mode 100644 index 00000000000..a01198bcbdf --- /dev/null +++ b/iocore/net/quic/QUICTransferProgressProvider.cc @@ -0,0 +1,83 @@ +/** @file + * + * Interface for providing transfer progress + * + * @section license License + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "I_IOBuffer.h" +#include "QUICStreamAdapter.h" + +void +QUICTransferProgressProviderSA::set_stream_adapter(QUICStreamAdapter *adapter) +{ + this->_adapter = adapter; +} + +bool +QUICTransferProgressProviderSA::is_transfer_goal_set() const +{ + return this->transfer_goal() != INT64_MAX; +} + +uint64_t +QUICTransferProgressProviderSA::transfer_progress() const +{ + return this->_adapter->read_len(); +} + +uint64_t +QUICTransferProgressProviderSA::transfer_goal() const +{ + return this->_adapter->total_len(); +} + +bool +QUICTransferProgressProviderSA::is_cancelled() const +{ + return false; +} + +// +// QUICTransferProgressProviderVIO:: +// + +bool +QUICTransferProgressProviderVIO::is_transfer_goal_set() const +{ + return this->_vio.nbytes != INT64_MAX; +} + +uint64_t +QUICTransferProgressProviderVIO::transfer_progress() const +{ + return this->_vio.ndone; +} + +uint64_t +QUICTransferProgressProviderVIO::transfer_goal() const +{ + return this->_vio.nbytes; +} + +bool +QUICTransferProgressProviderVIO::is_cancelled() const +{ + return false; +} diff --git a/iocore/net/quic/QUICTransferProgressProvider.h b/iocore/net/quic/QUICTransferProgressProvider.h index ce798fec954..f0bc747fe18 100644 --- a/iocore/net/quic/QUICTransferProgressProvider.h +++ b/iocore/net/quic/QUICTransferProgressProvider.h @@ -21,10 +21,11 @@ * limitations under the License. */ -#include "I_VIO.h" - #pragma once +class VIO; +class QUICStreamAdapter; + class QUICTransferProgressProvider { public: @@ -40,34 +41,29 @@ class QUICTransferProgressProvider } }; -class QUICTransferProgressProviderVIO : public QUICTransferProgressProvider +class QUICTransferProgressProviderSA : public QUICTransferProgressProvider { public: - QUICTransferProgressProviderVIO(VIO &vio) : _vio(vio) {} + void set_stream_adapter(QUICStreamAdapter *adapter); - bool - is_transfer_goal_set() const - { - return this->_vio.nbytes != INT64_MAX; - } + bool is_transfer_goal_set() const override; + uint64_t transfer_progress() const override; + uint64_t transfer_goal() const override; + bool is_cancelled() const override; - uint64_t - transfer_progress() const - { - return this->_vio.ndone; - } +private: + QUICStreamAdapter *_adapter; +}; - uint64_t - transfer_goal() const - { - return this->_vio.nbytes; - } +class QUICTransferProgressProviderVIO : public QUICTransferProgressProvider +{ +public: + QUICTransferProgressProviderVIO(VIO &vio) : _vio(vio) {} - bool - is_cancelled() const - { - return false; - } + bool is_transfer_goal_set() const override; + uint64_t transfer_progress() const override; + uint64_t transfer_goal() const override; + bool is_cancelled() const override; private: VIO &_vio; diff --git a/iocore/net/quic/QUICUnidirectionalStream.cc b/iocore/net/quic/QUICUnidirectionalStream.cc index c6a3d7a44de..599af3a3580 100644 --- a/iocore/net/quic/QUICUnidirectionalStream.cc +++ b/iocore/net/quic/QUICUnidirectionalStream.cc @@ -22,115 +22,25 @@ */ #include "QUICUnidirectionalStream.h" +#include "QUICStreamAdapter.h" // // QUICSendStream // QUICSendStream::QUICSendStream(QUICConnectionInfoProvider *cinfo, QUICStreamId sid, uint64_t send_max_stream_data) - : QUICStreamVConnection(cinfo, sid), _remote_flow_controller(send_max_stream_data, _id), _state(nullptr, &this->_progress_vio) + : QUICStream(cinfo, sid), _remote_flow_controller(send_max_stream_data, _id), _state(nullptr, &this->_progress_sa) { - SET_HANDLER(&QUICSendStream::state_stream_open); - QUICStreamFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller.current_offset(), this->_remote_flow_controller.current_limit()); } -int -QUICSendStream::state_stream_open(int event, void *data) -{ - QUICVStreamDebug("%s (%d)", get_vc_event_name(event), event); - QUICErrorUPtr error = nullptr; - - switch (event) { - case VC_EVENT_READ_READY: - case VC_EVENT_READ_COMPLETE: { - // should not schedule read event. - ink_assert(0); - break; - } - case VC_EVENT_WRITE_READY: - case VC_EVENT_WRITE_COMPLETE: { - int64_t len = this->_process_write_vio(); - if (len > 0) { - this->_signal_write_event(); - } - - break; - } - case VC_EVENT_EOS: - case VC_EVENT_ERROR: - case VC_EVENT_INACTIVITY_TIMEOUT: - case VC_EVENT_ACTIVE_TIMEOUT: { - // TODO - ink_assert(false); - break; - } - default: - QUICStreamDebug("unknown event"); - ink_assert(false); - } - - // FIXME error is always nullptr - if (error != nullptr) { - if (error->cls == QUICErrorClass::TRANSPORT) { - QUICStreamDebug("QUICError: %s (%u), %s (0x%x)", QUICDebugNames::error_class(error->cls), - static_cast(error->cls), QUICDebugNames::error_code(error->code), - static_cast(error->code)); - } else { - QUICStreamDebug("QUICError: %s (%u), APPLICATION ERROR (0x%x)", QUICDebugNames::error_class(error->cls), - static_cast(error->cls), static_cast(error->code)); - } - if (dynamic_cast(error.get()) != nullptr) { - // Stream Error - QUICStreamErrorUPtr serror = QUICStreamErrorUPtr(static_cast(error.get())); - this->reset(std::move(serror)); - } else { - // Connection Error - // TODO Close connection (Does this really happen?) - } - } - - return EVENT_DONE; -} - -int -QUICSendStream::state_stream_closed(int event, void *data) -{ - QUICVStreamDebug("%s (%d)", get_vc_event_name(event), event); - - switch (event) { - case VC_EVENT_READ_READY: - case VC_EVENT_READ_COMPLETE: { - // ignore - break; - } - case VC_EVENT_WRITE_READY: - case VC_EVENT_WRITE_COMPLETE: { - // ignore - break; - } - case VC_EVENT_EOS: - case VC_EVENT_ERROR: - case VC_EVENT_INACTIVITY_TIMEOUT: - case VC_EVENT_ACTIVE_TIMEOUT: { - // TODO - ink_assert(false); - break; - } - default: - ink_assert(false); - } - - return EVENT_DONE; -} - bool QUICSendStream::will_generate_frame(QUICEncryptionLevel level, size_t current_packet_size, bool ack_eliciting, uint32_t seq_num) { if (!this->is_retransmited_frame_queue_empty()) { return true; } - if (this->_write_vio.op != VIO::NONE && this->_write_vio.get_reader()->is_read_avail_more_than(0)) { + if (this->_adapter && this->_adapter->unread_len() > 0) { return true; } return false; @@ -155,96 +65,94 @@ QUICSendStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t return nullptr; } this->_records_rst_stream_frame(level, *static_cast(frame)); - this->_state.update_with_sending_frame(*frame); + if (this->_state.update_with_sending_frame(*frame)) { + this->_notify_state_change(); + } this->_is_reset_sent = true; return frame; } - if (this->_write_vio.op != VIO::NONE && this->_state.is_allowed_to_send(QUICFrameType::STREAM)) { - SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread()); + if (!this->_adapter || !this->_state.is_allowed_to_send(QUICFrameType::STREAM)) { + return frame; + } + + uint64_t maximum_data_size = 0; + if (maximum_frame_size <= MAX_STREAM_FRAME_OVERHEAD) { + return frame; + } + maximum_data_size = maximum_frame_size - MAX_STREAM_FRAME_OVERHEAD; + + bool pure_fin = false; + bool fin = false; + if (this->_adapter->is_eos()) { + // Pure FIN stream should be sent regardless status of remote flow controller, because the length is zero. + pure_fin = true; + fin = true; + } - uint64_t maximum_data_size = 0; - if (maximum_frame_size <= MAX_STREAM_FRAME_OVERHEAD) { + uint64_t len = 0; + if (!pure_fin) { + uint64_t data_len = this->_adapter->unread_len(); + if (data_len == 0) { return frame; } - maximum_data_size = maximum_frame_size - MAX_STREAM_FRAME_OVERHEAD; - - bool pure_fin = false; - bool fin = false; - if ((this->_write_vio.nbytes != 0 || this->_write_vio.nbytes != INT64_MAX) && - this->_write_vio.nbytes == static_cast(this->_send_offset)) { - // Pure FIN stream should be sent regardless status of remote flow controller, because the length is zero. - pure_fin = true; - fin = true; - } - uint64_t len = 0; - IOBufferReader *reader = this->_write_vio.get_reader(); - if (!pure_fin) { - uint64_t data_len = reader->block_read_avail(); - if (data_len == 0) { - return frame; - } - - // Check Connection/Stream level credit only if the generating STREAM frame is not pure fin - uint64_t stream_credit = this->_remote_flow_controller.credit(); - if (stream_credit == 0) { - // STREAM_DATA_BLOCKED - frame = - this->_remote_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num); - return frame; - } - - if (connection_credit == 0) { - // BLOCKED - BLOCKED frame will be sent by connection level remote flow controller - return frame; - } - - len = std::min(data_len, std::min(maximum_data_size, std::min(stream_credit, connection_credit))); - - // data_len, maximum_data_size, stream_credit and connection_credit are already checked they're larger than 0 - ink_assert(len != 0); - - if (this->_write_vio.nbytes == static_cast(this->_send_offset + len)) { - fin = true; - } + // Check Connection/Stream level credit only if the generating STREAM frame is not pure fin + uint64_t stream_credit = this->_remote_flow_controller.credit(); + if (stream_credit == 0) { + // STREAM_DATA_BLOCKED + frame = + this->_remote_flow_controller.generate_frame(buf, level, UINT16_MAX, maximum_frame_size, current_packet_size, seq_num); + return frame; } - Ptr block = make_ptr(reader->get_current_block()->clone()); - block->consume(reader->start_offset); - block->_end = std::min(block->start() + len, block->_buf_end); - ink_assert(static_cast(block->read_avail()) == len); - - // STREAM - Pure FIN or data length is lager than 0 - // FIXME has_length_flag and has_offset_flag should be configurable - frame = QUICFrameFactory::create_stream_frame(buf, block, this->_id, this->_send_offset, fin, true, true, - this->_issue_frame_id(), this); - if (!this->_state.is_allowed_to_send(*frame)) { - QUICStreamDebug("Canceled sending %s frame due to the stream state", QUICDebugNames::frame_type(frame->type())); + if (connection_credit == 0) { + // BLOCKED - BLOCKED frame will be sent by connection level remote flow controller return frame; } - if (!pure_fin) { - int ret = this->_remote_flow_controller.update(this->_send_offset + len); - // We cannot cancel sending the frame after updating the flow controller + len = std::min(data_len, std::min(maximum_data_size, std::min(stream_credit, connection_credit))); - // Calling update always success, because len is always less than stream_credit - ink_assert(ret == 0); + // data_len, maximum_data_size, stream_credit and connection_credit are already checked they're larger than 0 + ink_assert(len != 0); - QUICStreamFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller.current_offset(), - this->_remote_flow_controller.current_limit()); - if (this->_remote_flow_controller.current_offset() == this->_remote_flow_controller.current_limit()) { - QUICStreamDebug("Flow Controller will block sending a STREAM frame"); - } + if (this->_adapter->total_len() == this->_send_offset + len) { + fin = true; + } + } + + Ptr block = this->_adapter->read(len); + ink_assert(static_cast(block->read_avail()) == len); + + // STREAM - Pure FIN or data length is lager than 0 + // FIXME has_length_flag and has_offset_flag should be configurable + frame = QUICFrameFactory::create_stream_frame(buf, block, this->_id, this->_send_offset, fin, true, true, this->_issue_frame_id(), + this); + if (!this->_state.is_allowed_to_send(*frame)) { + QUICStreamDebug("Canceled sending %s frame due to the stream state", QUICDebugNames::frame_type(frame->type())); + return frame; + } - reader->consume(len); - this->_send_offset += len; - this->_write_vio.ndone += len; + if (!pure_fin) { + int ret = this->_remote_flow_controller.update(this->_send_offset + len); + // We cannot cancel sending the frame after updating the flow controller + + // Calling update always success, because len is always less than stream_credit + ink_assert(ret == 0); + + QUICStreamFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller.current_offset(), + this->_remote_flow_controller.current_limit()); + if (this->_remote_flow_controller.current_offset() == this->_remote_flow_controller.current_limit()) { + QUICStreamDebug("Flow Controller will block sending a STREAM frame"); } - this->_records_stream_frame(level, *static_cast(frame)); - this->_signal_write_event(); - this->_state.update_with_sending_frame(*frame); + this->_send_offset += len; + } + this->_records_stream_frame(level, *static_cast(frame)); + + this->_adapter->encourge_write(); + if (this->_state.update_with_sending_frame(*frame)) { + this->_notify_state_change(); } return frame; @@ -253,7 +161,9 @@ QUICSendStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint64_t QUICConnectionErrorUPtr QUICSendStream::recv(const QUICStopSendingFrame &frame) { - this->_state.update_with_receiving_frame(frame); + if (this->_state.update_with_receiving_frame(frame)) { + this->_notify_state_change(); + } this->reset(QUICStreamErrorUPtr(new QUICStreamError(this, QUIC_APP_ERROR_CODE_STOPPING))); // We received and processed STOP_SENDING frame, so return NO_ERROR here return nullptr; @@ -266,96 +176,11 @@ QUICSendStream::recv(const QUICMaxStreamDataFrame &frame) QUICStreamFCDebug("[REMOTE] %" PRIu64 "/%" PRIu64, this->_remote_flow_controller.current_offset(), this->_remote_flow_controller.current_limit()); - int64_t len = this->_process_write_vio(); - if (len > 0) { - this->_signal_write_event(); - } - - return nullptr; -} + this->_adapter->encourge_write(); -VIO * -QUICSendStream::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf) -{ - QUICStreamDebug("Warning wants to read from send only stream ignore"); - // FIXME: should not assert here - ink_assert(!"read from send only stream"); return nullptr; } -VIO * -QUICSendStream::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, bool owner) -{ - if (buf) { - this->_write_vio.buffer.reader_for(buf); - } else { - this->_write_vio.buffer.clear(); - } - - this->_write_vio.mutex = c ? c->mutex : this->mutex; - this->_write_vio.cont = c; - this->_write_vio.nbytes = nbytes; - this->_write_vio.ndone = 0; - this->_write_vio.vc_server = this; - this->_write_vio.op = VIO::WRITE; - - this->_process_write_vio(); - this->_send_tracked_event(this->_write_event, VC_EVENT_WRITE_READY, &this->_write_vio); - - return &this->_write_vio; -} - -void -QUICSendStream::do_io_close(int lerrno) -{ - SET_HANDLER(&QUICSendStream::state_stream_closed); - - ink_assert(this->_read_vio.nbytes == 0); - ink_assert(this->_read_vio.op == VIO::NONE); - ink_assert(this->_read_vio.cont == nullptr); - this->_read_vio.buffer.clear(); - - this->_write_vio.buffer.clear(); - this->_write_vio.nbytes = 0; - this->_write_vio.op = VIO::NONE; - this->_write_vio.cont = nullptr; -} - -void -QUICSendStream::do_io_shutdown(ShutdownHowTo_t howto) -{ - switch (howto) { - case IO_SHUTDOWN_READ: - // ignore - break; - case IO_SHUTDOWN_WRITE: - case IO_SHUTDOWN_READWRITE: - this->do_io_close(); - break; - default: - ink_assert(0); - break; - } -} - -void -QUICSendStream::reenable(VIO *vio) -{ - ink_assert(vio == &this->_write_vio); - ink_assert(vio->op == VIO::WRITE); - - int64_t len = this->_process_write_vio(); - if (len > 0) { - this->_signal_write_event(); - } -} - -void -QUICSendStream::reset(QUICStreamErrorUPtr error) -{ - this->_reset_reason = std::move(error); -} - void QUICSendStream::_on_frame_acked(QUICFrameInformationUPtr &info) { @@ -405,111 +230,26 @@ QUICSendStream::largest_offset_sent() const return this->_remote_flow_controller.current_offset(); } +void +QUICSendStream::reset(QUICStreamErrorUPtr error) +{ + this->_reset_reason = std::move(error); +} + // // QUICReceiveStream // QUICReceiveStream::QUICReceiveStream(QUICRTTProvider *rtt_provider, QUICConnectionInfoProvider *cinfo, QUICStreamId sid, uint64_t recv_max_stream_data) - : QUICStreamVConnection(cinfo, sid), + : QUICStream(cinfo, sid), _local_flow_controller(rtt_provider, recv_max_stream_data, _id), _flow_control_buffer_size(recv_max_stream_data), _state(this, nullptr) { - SET_HANDLER(&QUICReceiveStream::state_stream_open); - QUICStreamFCDebug("[LOCAL] %" PRIu64 "/%" PRIu64, this->_local_flow_controller.current_offset(), this->_local_flow_controller.current_limit()); } -int -QUICReceiveStream::state_stream_open(int event, void *data) -{ - QUICVStreamDebug("%s (%d)", get_vc_event_name(event), event); - QUICErrorUPtr error = nullptr; - - switch (event) { - case VC_EVENT_READ_READY: - case VC_EVENT_READ_COMPLETE: { - int64_t len = this->_process_read_vio(); - if (len > 0) { - this->_signal_read_event(); - } - - break; - } - case VC_EVENT_WRITE_READY: - case VC_EVENT_WRITE_COMPLETE: { - // should not schedule write event - ink_assert(!"should not schedule write even"); - break; - } - case VC_EVENT_EOS: - case VC_EVENT_ERROR: - case VC_EVENT_INACTIVITY_TIMEOUT: - case VC_EVENT_ACTIVE_TIMEOUT: { - // TODO - ink_assert(false); - break; - } - default: - QUICStreamDebug("unknown event"); - ink_assert(false); - } - - // FIXME error is always nullptr - if (error != nullptr) { - if (error->cls == QUICErrorClass::TRANSPORT) { - QUICStreamDebug("QUICError: %s (%u), %s (0x%x)", QUICDebugNames::error_class(error->cls), - static_cast(error->cls), QUICDebugNames::error_code(error->code), - static_cast(error->code)); - } else { - QUICStreamDebug("QUICError: %s (%u), APPLICATION ERROR (0x%x)", QUICDebugNames::error_class(error->cls), - static_cast(error->cls), static_cast(error->code)); - } - if (dynamic_cast(error.get()) != nullptr) { - // Stream Error - QUICStreamErrorUPtr serror = QUICStreamErrorUPtr(static_cast(error.get())); - this->reset(std::move(serror)); - } else { - // Connection Error - // TODO Close connection (Does this really happen?) - } - } - - return EVENT_DONE; -} - -int -QUICReceiveStream::state_stream_closed(int event, void *data) -{ - QUICVStreamDebug("%s (%d)", get_vc_event_name(event), event); - - switch (event) { - case VC_EVENT_READ_READY: - case VC_EVENT_READ_COMPLETE: { - // ignore - break; - } - case VC_EVENT_WRITE_READY: - case VC_EVENT_WRITE_COMPLETE: { - // ignore - break; - } - case VC_EVENT_EOS: - case VC_EVENT_ERROR: - case VC_EVENT_INACTIVITY_TIMEOUT: - case VC_EVENT_ACTIVE_TIMEOUT: { - // TODO - ink_assert(false); - break; - } - default: - ink_assert(false); - } - - return EVENT_DONE; -} - bool QUICReceiveStream::is_transfer_goal_set() const { @@ -560,7 +300,9 @@ QUICReceiveStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint6 return nullptr; } this->_records_stop_sending_frame(level, *static_cast(frame)); - this->_state.update_with_sending_frame(*frame); + if (this->_state.update_with_sending_frame(*frame)) { + this->_notify_state_change(); + } this->_is_stop_sending_sent = true; return frame; } @@ -574,8 +316,10 @@ QUICReceiveStream::generate_frame(uint8_t *buf, QUICEncryptionLevel level, uint6 QUICConnectionErrorUPtr QUICReceiveStream::recv(const QUICRstStreamFrame &frame) { - this->_state.update_with_receiving_frame(frame); - this->_signal_read_eos_event(); + if (this->_state.update_with_receiving_frame(frame)) { + this->_notify_state_change(); + } + this->_adapter->notify_eos(); return nullptr; } @@ -597,7 +341,6 @@ QUICConnectionErrorUPtr QUICReceiveStream::recv(const QUICStreamFrame &frame) { ink_assert(_id == frame.stream_id()); - ink_assert(this->_read_vio.op == VIO::READ); // Check stream state - Do this first before accept the frame if (!this->_state.is_allowed_to_receive(frame)) { @@ -631,9 +374,11 @@ QUICReceiveStream::recv(const QUICStreamFrame &frame) last_offset = stream_frame->offset(); last_length = stream_frame->data_length(); - this->_write_to_read_vio(stream_frame->offset(), reinterpret_cast(stream_frame->data()->start()), - stream_frame->data_length(), stream_frame->has_fin_flag()); - this->_state.update_with_receiving_frame(*new_frame); + this->_adapter->write(stream_frame->offset(), reinterpret_cast(stream_frame->data()->start()), + stream_frame->data_length(), stream_frame->has_fin_flag()); + if (this->_state.update_with_receiving_frame(*new_frame)) { + this->_notify_state_change(); + } delete new_frame; new_frame = this->_received_stream_frame_buffer.pop(); @@ -647,97 +392,25 @@ QUICReceiveStream::recv(const QUICStreamFrame &frame) this->_local_flow_controller.current_limit()); } - this->_signal_read_event(); + this->_adapter->encourge_read(); return nullptr; } -VIO * -QUICReceiveStream::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf) -{ - if (buf) { - this->_read_vio.buffer.writer_for(buf); - } else { - this->_read_vio.buffer.clear(); - } - - this->_read_vio.mutex = c ? c->mutex : this->mutex; - this->_read_vio.cont = c; - this->_read_vio.nbytes = nbytes; - this->_read_vio.ndone = 0; - this->_read_vio.vc_server = this; - this->_read_vio.op = VIO::READ; - - this->_process_read_vio(); - this->_send_tracked_event(this->_read_event, VC_EVENT_READ_READY, &this->_read_vio); - - return &this->_read_vio; -} - -VIO * -QUICReceiveStream::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, bool owner) -{ - QUICStreamDebug("Warning wants to write to send only stream ignore"); - // FIXME: should not assert here - ink_assert(!"write to send only stream"); - return nullptr; -} - -void -QUICReceiveStream::do_io_close(int lerrno) -{ - SET_HANDLER(&QUICReceiveStream::state_stream_closed); - - ink_assert(this->_write_vio.nbytes == 0); - ink_assert(this->_write_vio.op == VIO::NONE); - ink_assert(this->_write_vio.cont == nullptr); - this->_write_vio.buffer.clear(); - - this->_read_vio.buffer.clear(); - this->_read_vio.nbytes = 0; - this->_read_vio.op = VIO::NONE; - this->_read_vio.cont = nullptr; -} - -void -QUICReceiveStream::do_io_shutdown(ShutdownHowTo_t howto) -{ - switch (howto) { - case IO_SHUTDOWN_WRITE: - // ignore - break; - case IO_SHUTDOWN_READ: - case IO_SHUTDOWN_READWRITE: - this->do_io_close(); - break; - default: - ink_assert(0); - break; - } -} - -void -QUICReceiveStream::reenable(VIO *vio) -{ - ink_assert(vio == &this->_read_vio); - ink_assert(vio->op == VIO::READ); - - int64_t len = this->_process_read_vio(); - if (len > 0) { - this->_signal_read_event(); - } -} - void QUICReceiveStream::on_read() { - this->_state.update_on_read(); + if (this->_state.update_on_read()) { + this->_notify_state_change(); + } } void QUICReceiveStream::on_eos() { - this->_state.update_on_eos(); + if (this->_state.update_on_eos()) { + this->_notify_state_change(); + } } QUICOffset diff --git a/iocore/net/quic/QUICUnidirectionalStream.h b/iocore/net/quic/QUICUnidirectionalStream.h index 5ebe5527c0b..315c24f0b77 100644 --- a/iocore/net/quic/QUICUnidirectionalStream.h +++ b/iocore/net/quic/QUICUnidirectionalStream.h @@ -25,7 +25,7 @@ #include "QUICStream.h" -class QUICSendStream : public QUICStreamVConnection +class QUICSendStream : public QUICStream { public: QUICSendStream(QUICConnectionInfoProvider *cinfo, QUICStreamId sid, uint64_t send_max_stream_data); @@ -44,13 +44,6 @@ class QUICSendStream : public QUICStreamVConnection virtual QUICConnectionErrorUPtr recv(const QUICMaxStreamDataFrame &frame) override; virtual QUICConnectionErrorUPtr recv(const QUICStopSendingFrame &frame) override; - // Implement VConnection Interface. - VIO *do_io_read(Continuation *c, int64_t nbytes = INT64_MAX, MIOBuffer *buf = 0) override; - VIO *do_io_write(Continuation *c = nullptr, int64_t nbytes = INT64_MAX, IOBufferReader *buf = 0, bool owner = false) override; - void do_io_close(int lerrno = -1) override; - void do_io_shutdown(ShutdownHowTo_t howto) override; - void reenable(VIO *vio) override; - void reset(QUICStreamErrorUPtr error) override; QUICOffset largest_offset_sent() const override; @@ -62,7 +55,7 @@ class QUICSendStream : public QUICStreamVConnection bool _is_transfer_complete = false; bool _is_reset_complete = false; - QUICTransferProgressProviderVIO _progress_vio = {this->_read_vio}; + QUICTransferProgressProviderSA _progress_sa; QUICRemoteStreamFlowController _remote_flow_controller; @@ -73,7 +66,7 @@ class QUICSendStream : public QUICStreamVConnection void _on_frame_lost(QUICFrameInformationUPtr &info) override; }; -class QUICReceiveStream : public QUICStreamVConnection, public QUICTransferProgressProvider +class QUICReceiveStream : public QUICStream, public QUICTransferProgressProvider { public: QUICReceiveStream(QUICRTTProvider *rtt_provider, QUICConnectionInfoProvider *cinfo, QUICStreamId sid, @@ -94,13 +87,6 @@ class QUICReceiveStream : public QUICStreamVConnection, public QUICTransferProgr virtual QUICConnectionErrorUPtr recv(const QUICStreamDataBlockedFrame &frame) override; virtual QUICConnectionErrorUPtr recv(const QUICRstStreamFrame &frame) override; - // Implement VConnection Interface. - VIO *do_io_read(Continuation *c, int64_t nbytes = INT64_MAX, MIOBuffer *buf = 0) override; - VIO *do_io_write(Continuation *c = nullptr, int64_t nbytes = INT64_MAX, IOBufferReader *buf = 0, bool owner = false) override; - void do_io_close(int lerrno = -1) override; - void do_io_shutdown(ShutdownHowTo_t howto) override; - void reenable(VIO *vio) override; - // QUICTransferProgressProvider bool is_transfer_goal_set() const override; uint64_t transfer_progress() const override; diff --git a/iocore/net/quic/test/test_QUICStream.cc b/iocore/net/quic/test/test_QUICStream.cc index 828049f43b2..92617812a16 100644 --- a/iocore/net/quic/test/test_QUICStream.cc +++ b/iocore/net/quic/test/test_QUICStream.cc @@ -25,6 +25,7 @@ #include "quic/QUICBidirectionalStream.h" #include "quic/QUICUnidirectionalStream.h" +#include "quic/QUICStreamVCAdapter.h" #include "quic/Mock.h" TEST_CASE("QUICBidiStream", "[quic]") @@ -88,7 +89,9 @@ TEST_CASE("QUICBidiStream", "[quic]") MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream( new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, stream_id, 1024, 1024)); - stream->do_io_read(nullptr, INT64_MAX, read_buffer); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + adapter.do_io_read(nullptr, INT64_MAX, read_buffer); stream->recv(frame_1); stream->recv(frame_2); @@ -116,7 +119,9 @@ TEST_CASE("QUICBidiStream", "[quic]") MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream( new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX, UINT64_MAX)); - stream->do_io_read(nullptr, INT64_MAX, read_buffer); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + adapter.do_io_read(nullptr, INT64_MAX, read_buffer); stream->recv(frame_8); stream->recv(frame_7); @@ -144,7 +149,9 @@ TEST_CASE("QUICBidiStream", "[quic]") MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream( new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX, UINT64_MAX)); - stream->do_io_read(nullptr, INT64_MAX, read_buffer); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + adapter.do_io_read(nullptr, INT64_MAX, read_buffer); stream->recv(frame_8); stream->recv(frame_7); @@ -175,7 +182,9 @@ TEST_CASE("QUICBidiStream", "[quic]") MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream( new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, stream_id, 4096, 4096)); - stream->do_io_read(nullptr, INT64_MAX, read_buffer); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + adapter.do_io_read(nullptr, INT64_MAX, read_buffer); Ptr block = make_ptr(new_IOBufferBlock()); block->alloc(BUFFER_SIZE_INDEX_32K); @@ -216,11 +225,13 @@ TEST_CASE("QUICBidiStream", "[quic]") MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream( new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, stream_id, 4096, 4096)); - SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + SCOPED_MUTEX_LOCK(lock, adapter.mutex, this_ethread()); - MockContinuation mock_cont(stream->mutex); - stream->do_io_read(nullptr, INT64_MAX, read_buffer); - stream->do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); + MockContinuation mock_cont(adapter.mutex); + adapter.do_io_read(nullptr, INT64_MAX, read_buffer); + adapter.do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; @@ -228,28 +239,28 @@ TEST_CASE("QUICBidiStream", "[quic]") QUICFrame *frame = nullptr; write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); CHECK(stream->will_generate_frame(level, 0, false, 0) == false); write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); CHECK(stream->will_generate_frame(level, 0, false, 0) == false); write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); CHECK(stream->will_generate_frame(level, 0, false, 0) == false); write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); @@ -257,7 +268,7 @@ TEST_CASE("QUICBidiStream", "[quic]") // This should not send a frame because of flow control write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame); @@ -268,7 +279,7 @@ TEST_CASE("QUICBidiStream", "[quic]") stream->recv(*std::make_shared(stream_id, 5120)); // This should send a frame - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); @@ -279,13 +290,13 @@ TEST_CASE("QUICBidiStream", "[quic]") // This should send a frame write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM_DATA_BLOCKED); @@ -293,7 +304,7 @@ TEST_CASE("QUICBidiStream", "[quic]") // Update window stream->recv(*std::make_shared(stream_id, 6144)); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); @@ -312,10 +323,12 @@ TEST_CASE("QUICBidiStream", "[quic]") MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream( new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX, UINT64_MAX)); - SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + SCOPED_MUTEX_LOCK(lock, adapter.mutex, this_ethread()); - MockContinuation mock_cont(stream->mutex); - stream->do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); + MockContinuation mock_cont(adapter.mutex); + adapter.do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; const char data1[] = "this is a test data"; @@ -327,7 +340,7 @@ TEST_CASE("QUICBidiStream", "[quic]") // Write data1 write_buffer->write(data1, sizeof(data1)); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); // Generate STREAM frame frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); frame1 = static_cast(frame); @@ -339,7 +352,7 @@ TEST_CASE("QUICBidiStream", "[quic]") // Write data2 write_buffer->write(data2, sizeof(data2)); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); // Lost the frame stream->on_frame_lost(frame->id()); // Regenerate a frame @@ -361,10 +374,12 @@ TEST_CASE("QUICBidiStream", "[quic]") MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream( new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX, UINT64_MAX)); - SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + SCOPED_MUTEX_LOCK(lock, adapter.mutex, this_ethread()); - MockContinuation mock_cont(stream->mutex); - stream->do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); + MockContinuation mock_cont(adapter.mutex); + adapter.do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; QUICFrame *frame = nullptr; @@ -392,10 +407,12 @@ TEST_CASE("QUICBidiStream", "[quic]") MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream( new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX, UINT64_MAX)); - SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + SCOPED_MUTEX_LOCK(lock, adapter.mutex, this_ethread()); - MockContinuation mock_cont(stream->mutex); - stream->do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); + MockContinuation mock_cont(adapter.mutex); + adapter.do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; QUICFrame *frame = nullptr; @@ -426,9 +443,11 @@ TEST_CASE("QUICBidiStream", "[quic]") // STOP_SENDING std::unique_ptr stream1( new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX, UINT64_MAX)); - MockContinuation mock_cont1(stream1->mutex); - stream1->do_io_write(&mock_cont1, INT64_MAX, write_buffer_reader); - SCOPED_MUTEX_LOCK(lock1, stream1->mutex, this_ethread()); + QUICStreamVCAdapter adapter1(*stream1); + stream1->set_io_adapter(&adapter1); + MockContinuation mock_cont1(adapter1.mutex); + adapter1.do_io_write(&mock_cont1, INT64_MAX, write_buffer_reader); + SCOPED_MUTEX_LOCK(lock1, adapter1.mutex, this_ethread()); stream1->stop_sending(QUICStreamErrorUPtr(new QUICStreamError(stream1.get(), QUIC_APP_ERROR_CODE_STOPPING))); frame = stream1->generate_frame(frame_buf, level, 4096, 0, 0, 0); CHECK(frame == nullptr); @@ -436,9 +455,11 @@ TEST_CASE("QUICBidiStream", "[quic]") // RESET_STREAM std::unique_ptr stream2( new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX, UINT64_MAX)); - MockContinuation mock_cont2(stream2->mutex); - stream2->do_io_write(&mock_cont2, INT64_MAX, write_buffer_reader); - SCOPED_MUTEX_LOCK(lock2, stream2->mutex, this_ethread()); + QUICStreamVCAdapter adapter2(*stream2); + stream2->set_io_adapter(&adapter2); + MockContinuation mock_cont2(adapter2.mutex); + adapter2.do_io_write(&mock_cont2, INT64_MAX, write_buffer_reader); + SCOPED_MUTEX_LOCK(lock2, adapter2.mutex, this_ethread()); stream2->reset(QUICStreamErrorUPtr(new QUICStreamError(stream2.get(), QUIC_APP_ERROR_CODE_STOPPING))); frame = stream2->generate_frame(frame_buf, level, 4096, 0, 0, 0); CHECK(frame == nullptr); @@ -446,12 +467,14 @@ TEST_CASE("QUICBidiStream", "[quic]") // STREAM std::unique_ptr stream3( new QUICBidirectionalStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX, UINT64_MAX)); - MockContinuation mock_cont3(stream3->mutex); - stream3->do_io_write(&mock_cont3, INT64_MAX, write_buffer_reader); - SCOPED_MUTEX_LOCK(lock3, stream3->mutex, this_ethread()); + QUICStreamVCAdapter adapter3(*stream3); + stream3->set_io_adapter(&adapter3); + MockContinuation mock_cont3(adapter3.mutex); + adapter3.do_io_write(&mock_cont3, INT64_MAX, write_buffer_reader); + SCOPED_MUTEX_LOCK(lock3, adapter3.mutex, this_ethread()); const char data[] = "this is a test data"; write_buffer->write(data, sizeof(data)); - stream3->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter3.handleEvent(VC_EVENT_WRITE_READY, nullptr); frame = stream3->generate_frame(frame_buf, level, 4096, 0, 0, 0); CHECK(frame == nullptr); } @@ -517,7 +540,9 @@ TEST_CASE("QUIC receive only stream", "[quic]") QUICRTTMeasure rtt_provider; MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICReceiveStream(&rtt_provider, &cinfo_provider, stream_id, 1024)); - stream->do_io_read(nullptr, INT64_MAX, read_buffer); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + adapter.do_io_read(nullptr, INT64_MAX, read_buffer); stream->recv(frame_1); stream->recv(frame_2); @@ -544,7 +569,9 @@ TEST_CASE("QUIC receive only stream", "[quic]") QUICRTTMeasure rtt_provider; MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICReceiveStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX)); - stream->do_io_read(nullptr, INT64_MAX, read_buffer); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + adapter.do_io_read(nullptr, INT64_MAX, read_buffer); stream->recv(frame_8); stream->recv(frame_7); @@ -571,7 +598,9 @@ TEST_CASE("QUIC receive only stream", "[quic]") QUICRTTMeasure rtt_provider; MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICReceiveStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX)); - stream->do_io_read(nullptr, INT64_MAX, read_buffer); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + adapter.do_io_read(nullptr, INT64_MAX, read_buffer); stream->recv(frame_8); stream->recv(frame_7); @@ -601,7 +630,9 @@ TEST_CASE("QUIC receive only stream", "[quic]") QUICRTTMeasure rtt_provider; MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICReceiveStream(&rtt_provider, &cinfo_provider, stream_id, 4096)); - stream->do_io_read(nullptr, INT64_MAX, read_buffer); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + adapter.do_io_read(nullptr, INT64_MAX, read_buffer); Ptr block = make_ptr(new_IOBufferBlock()); block->alloc(BUFFER_SIZE_INDEX_32K); @@ -635,7 +666,9 @@ TEST_CASE("QUIC receive only stream", "[quic]") QUICRTTMeasure rtt_provider; MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICReceiveStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX)); - SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + SCOPED_MUTEX_LOCK(lock, adapter.mutex, this_ethread()); QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; QUICFrame *frame = nullptr; @@ -663,8 +696,10 @@ TEST_CASE("QUIC receive only stream", "[quic]") // STOP_SENDING std::unique_ptr stream1(new QUICReceiveStream(&rtt_provider, &cinfo_provider, stream_id, UINT64_MAX)); - MockContinuation mock_cont1(stream1->mutex); - SCOPED_MUTEX_LOCK(lock1, stream1->mutex, this_ethread()); + QUICStreamVCAdapter adapter1(*stream1); + stream1->set_io_adapter(&adapter1); + MockContinuation mock_cont1(adapter1.mutex); + SCOPED_MUTEX_LOCK(lock1, adapter1.mutex, this_ethread()); stream1->stop_sending(QUICStreamErrorUPtr(new QUICStreamError(stream1.get(), QUIC_APP_ERROR_CODE_STOPPING))); frame = stream1->generate_frame(frame_buf, level, 4096, 0, 0, 0); CHECK(frame == nullptr); @@ -732,10 +767,12 @@ TEST_CASE("QUIC send only stream", "[quic]") MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICSendStream(&cinfo_provider, stream_id, 4096)); - SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + SCOPED_MUTEX_LOCK(lock, adapter.mutex, this_ethread()); - MockContinuation mock_cont(stream->mutex); - stream->do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); + MockContinuation mock_cont(adapter.mutex); + adapter.do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; @@ -743,28 +780,28 @@ TEST_CASE("QUIC send only stream", "[quic]") QUICFrame *frame = nullptr; write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); CHECK(stream->will_generate_frame(level, 0, false, 0) == false); write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); CHECK(stream->will_generate_frame(level, 0, false, 0) == false); write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); CHECK(stream->will_generate_frame(level, 0, false, 0) == false); write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); @@ -772,7 +809,7 @@ TEST_CASE("QUIC send only stream", "[quic]") // This should not send a frame because of flow control write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame); @@ -783,7 +820,7 @@ TEST_CASE("QUIC send only stream", "[quic]") stream->recv(*std::make_shared(stream_id, 5120)); // This should send a frame - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); @@ -794,13 +831,13 @@ TEST_CASE("QUIC send only stream", "[quic]") // This should send a frame write_buffer->write(data, 1024); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM_DATA_BLOCKED); @@ -808,7 +845,7 @@ TEST_CASE("QUIC send only stream", "[quic]") // Update window stream->recv(*std::make_shared(stream_id, 6144)); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); CHECK(stream->will_generate_frame(level, 0, false, 0) == true); frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); CHECK(frame->type() == QUICFrameType::STREAM); @@ -825,10 +862,12 @@ TEST_CASE("QUIC send only stream", "[quic]") MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICSendStream(&cinfo_provider, stream_id, UINT64_MAX)); - SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + SCOPED_MUTEX_LOCK(lock, adapter.mutex, this_ethread()); - MockContinuation mock_cont(stream->mutex); - stream->do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); + MockContinuation mock_cont(adapter.mutex); + adapter.do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; const char data1[] = "this is a test data"; @@ -840,7 +879,7 @@ TEST_CASE("QUIC send only stream", "[quic]") // Write data1 write_buffer->write(data1, sizeof(data1)); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); // Generate STREAM frame frame = stream->generate_frame(frame_buf, level, 4096, 4096, 0, 0); frame1 = static_cast(frame); @@ -852,7 +891,7 @@ TEST_CASE("QUIC send only stream", "[quic]") // Write data2 write_buffer->write(data2, sizeof(data2)); - stream->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter.handleEvent(VC_EVENT_WRITE_READY, nullptr); // Lost the frame stream->on_frame_lost(frame->id()); // Regenerate a frame @@ -872,10 +911,12 @@ TEST_CASE("QUIC send only stream", "[quic]") MockQUICConnectionInfoProvider cinfo_provider; std::unique_ptr stream(new QUICSendStream(&cinfo_provider, stream_id, UINT64_MAX)); - SCOPED_MUTEX_LOCK(lock, stream->mutex, this_ethread()); + QUICStreamVCAdapter adapter(*stream); + stream->set_io_adapter(&adapter); + SCOPED_MUTEX_LOCK(lock, adapter.mutex, this_ethread()); - MockContinuation mock_cont(stream->mutex); - stream->do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); + MockContinuation mock_cont(adapter.mutex); + adapter.do_io_write(&mock_cont, INT64_MAX, write_buffer_reader); QUICEncryptionLevel level = QUICEncryptionLevel::ONE_RTT; QUICFrame *frame = nullptr; @@ -904,21 +945,25 @@ TEST_CASE("QUIC send only stream", "[quic]") // RESET_STREAM std::unique_ptr stream2(new QUICSendStream(&cinfo_provider, stream_id, UINT64_MAX)); - MockContinuation mock_cont2(stream2->mutex); - stream2->do_io_write(&mock_cont2, INT64_MAX, write_buffer_reader); - SCOPED_MUTEX_LOCK(lock2, stream2->mutex, this_ethread()); + QUICStreamVCAdapter adapter2(*stream2); + stream2->set_io_adapter(&adapter2); + MockContinuation mock_cont2(adapter2.mutex); + adapter2.do_io_write(&mock_cont2, INT64_MAX, write_buffer_reader); + SCOPED_MUTEX_LOCK(lock2, adapter2.mutex, this_ethread()); stream2->reset(QUICStreamErrorUPtr(new QUICStreamError(stream2.get(), QUIC_APP_ERROR_CODE_STOPPING))); frame = stream2->generate_frame(frame_buf, level, 4096, 0, 0, 0); CHECK(frame == nullptr); // STREAM std::unique_ptr stream3(new QUICSendStream(&cinfo_provider, stream_id, UINT64_MAX)); - MockContinuation mock_cont3(stream3->mutex); - stream3->do_io_write(&mock_cont3, INT64_MAX, write_buffer_reader); - SCOPED_MUTEX_LOCK(lock3, stream3->mutex, this_ethread()); + QUICStreamVCAdapter adapter3(*stream3); + stream3->set_io_adapter(&adapter3); + MockContinuation mock_cont3(adapter3.mutex); + adapter3.do_io_write(&mock_cont3, INT64_MAX, write_buffer_reader); + SCOPED_MUTEX_LOCK(lock3, adapter3.mutex, this_ethread()); const char data[] = "this is a test data"; write_buffer->write(data, sizeof(data)); - stream3->handleEvent(VC_EVENT_WRITE_READY, nullptr); + adapter3.handleEvent(VC_EVENT_WRITE_READY, nullptr); frame = stream3->generate_frame(frame_buf, level, 4096, 0, 0, 0); CHECK(frame == nullptr); } diff --git a/iocore/net/quic/test/test_QUICStreamState.cc b/iocore/net/quic/test/test_QUICStreamState.cc index 9eb11281475..e6f9989fbc3 100644 --- a/iocore/net/quic/test/test_QUICStreamState.cc +++ b/iocore/net/quic/test/test_QUICStreamState.cc @@ -57,17 +57,17 @@ TEST_CASE("QUICSendStreamState", "[quic]") // Case2. Send STREAM CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM)); - ss.update_with_sending_frame(*stream_frame); + CHECK(ss.update_with_sending_frame(*stream_frame)); CHECK(ss.get() == QUICSendStreamState::Send); // Case3. Send STREAM_DATA_BLOCKED CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM_DATA_BLOCKED)); - ss.update_with_sending_frame(*stream_data_blocked_frame); + CHECK(!ss.update_with_sending_frame(*stream_data_blocked_frame)); CHECK(ss.get() == QUICSendStreamState::Send); // Case3. Send FIN in a STREAM CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM)); - ss.update_with_sending_frame(*stream_frame_with_fin); + CHECK(ss.update_with_sending_frame(*stream_frame_with_fin)); CHECK(ss.get() == QUICSendStreamState::DataSent); // Case4. STREAM is not allowed to send @@ -75,7 +75,7 @@ TEST_CASE("QUICSendStreamState", "[quic]") // Case5. Receive all ACKs pp.set_transfer_complete(true); - ss.update_on_ack(); + CHECK(ss.update_on_ack()); CHECK(ss.get() == QUICSendStreamState::DataRecvd); } @@ -87,7 +87,7 @@ TEST_CASE("QUICSendStreamState", "[quic]") // Case2. Send STREAM_DATA_BLOCKED CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM_DATA_BLOCKED)); - ss.update_with_sending_frame(*stream_data_blocked_frame); + CHECK(ss.update_with_sending_frame(*stream_data_blocked_frame)); CHECK(ss.get() == QUICSendStreamState::Send); } @@ -101,7 +101,7 @@ TEST_CASE("QUICSendStreamState", "[quic]") // Case2. Send RESET_STREAM CHECK(ss.is_allowed_to_send(QUICFrameType::RESET_STREAM)); - ss.update_with_sending_frame(*rst_stream_frame); + CHECK(ss.update_with_sending_frame(*rst_stream_frame)); CHECK(ss.get() == QUICSendStreamState::ResetSent); // Case3. Receive ACK for STREAM @@ -109,7 +109,7 @@ TEST_CASE("QUICSendStreamState", "[quic]") // Case4. Receive ACK for RESET_STREAM pp.set_cancelled(true); - ss.update_on_ack(); + CHECK(ss.update_on_ack()); CHECK(ss.get() == QUICSendStreamState::ResetRecvd); } @@ -121,21 +121,21 @@ TEST_CASE("QUICSendStreamState", "[quic]") // Case2. Send STREAM CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM)); - ss.update_with_sending_frame(*stream_frame); + CHECK(ss.update_with_sending_frame(*stream_frame)); CHECK(ss.get() == QUICSendStreamState::Send); // Case3. Send RESET_STREAM CHECK(ss.is_allowed_to_send(QUICFrameType::RESET_STREAM)); - ss.update_with_sending_frame(*rst_stream_frame); + CHECK(ss.update_with_sending_frame(*rst_stream_frame)); CHECK(ss.get() == QUICSendStreamState::ResetSent); // Case4. Receive ACK for STREAM - ss.update_on_ack(); + CHECK(!ss.update_on_ack()); CHECK(ss.get() == QUICSendStreamState::ResetSent); // Case5. Receive ACK for RESET_STREAM pp.set_cancelled(true); - ss.update_on_ack(); + CHECK(ss.update_on_ack()); CHECK(ss.get() == QUICSendStreamState::ResetRecvd); } @@ -147,17 +147,17 @@ TEST_CASE("QUICSendStreamState", "[quic]") // Case2. Send STREAM CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM)); - ss.update_with_sending_frame(*stream_frame); + CHECK(ss.update_with_sending_frame(*stream_frame)); CHECK(ss.get() == QUICSendStreamState::Send); // Case3. Send STREAM_DATA_BLOCKED CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM_DATA_BLOCKED)); - ss.update_with_sending_frame(*stream_data_blocked_frame); + CHECK(!ss.update_with_sending_frame(*stream_data_blocked_frame)); CHECK(ss.get() == QUICSendStreamState::Send); // Case3. Send FIN in a STREAM CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM)); - ss.update_with_sending_frame(*stream_frame_with_fin); + CHECK(ss.update_with_sending_frame(*stream_frame_with_fin)); CHECK(ss.get() == QUICSendStreamState::DataSent); // Case4. STREAM is not allowed to send @@ -165,16 +165,16 @@ TEST_CASE("QUICSendStreamState", "[quic]") // Case4. Send RESET_STREAM CHECK(ss.is_allowed_to_send(QUICFrameType::RESET_STREAM)); - ss.update_with_sending_frame(*rst_stream_frame); + CHECK(ss.update_with_sending_frame(*rst_stream_frame)); CHECK(ss.get() == QUICSendStreamState::ResetSent); // Case5. Receive ACK for STREAM - ss.update_on_ack(); + CHECK(!ss.update_on_ack()); CHECK(ss.get() == QUICSendStreamState::ResetSent); // Case6. Receive ACK for RESET_STREAM pp.set_cancelled(true); - ss.update_on_ack(); + CHECK(ss.update_on_ack()); CHECK(ss.get() == QUICSendStreamState::ResetRecvd); } } @@ -209,29 +209,29 @@ TEST_CASE("QUICReceiveStreamState", "[quic]") CHECK(ss.is_allowed_to_send(QUICFrameType::MAX_STREAM_DATA) == false); CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); in_progress.set_transfer_progress(1); - ss.update_with_receiving_frame(*stream_frame); + CHECK(ss.update_with_receiving_frame(*stream_frame)); CHECK(ss.get() == QUICReceiveStreamState::Recv); // Case2. Recv STREAM_DATA_BLOCKED CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM_DATA_BLOCKED)); - ss.update_with_receiving_frame(*stream_data_blocked_frame); + CHECK(!ss.update_with_receiving_frame(*stream_data_blocked_frame)); CHECK(ss.get() == QUICReceiveStreamState::Recv); // Case3. Recv FIN in a STREAM CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); in_progress.set_transfer_goal(3); - ss.update_with_receiving_frame(*stream_frame_with_fin); + CHECK(ss.update_with_receiving_frame(*stream_frame_with_fin)); CHECK(ss.get() == QUICReceiveStreamState::SizeKnown); // Case4. Recv ALL data CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); in_progress.set_transfer_progress(3); - ss.update_with_receiving_frame(*stream_frame_delayed); + CHECK(ss.update_with_receiving_frame(*stream_frame_delayed)); CHECK(ss.get() == QUICReceiveStreamState::DataRecvd); // Case5. Read data in_progress.set_transfer_complete(true); - ss.update_on_read(); + CHECK(ss.update_on_read()); CHECK(ss.get() == QUICReceiveStreamState::DataRead); } @@ -242,16 +242,16 @@ TEST_CASE("QUICReceiveStreamState", "[quic]") // Case1. Recv STREAM QUICReceiveStreamStateMachine ss(&in_progress, nullptr); CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); - ss.update_with_receiving_frame(*stream_frame); + CHECK(ss.update_with_receiving_frame(*stream_frame)); CHECK(ss.get() == QUICReceiveStreamState::Recv); // Case2. Recv RESET_STREAM CHECK(ss.is_allowed_to_receive(QUICFrameType::RESET_STREAM)); - ss.update_with_receiving_frame(*rst_stream_frame); + CHECK(ss.update_with_receiving_frame(*rst_stream_frame)); CHECK(ss.get() == QUICReceiveStreamState::ResetRecvd); // Case3. Handle reset - ss.update_on_eos(); + CHECK(ss.update_on_eos()); CHECK(ss.get() == QUICReceiveStreamState::ResetRead); } @@ -262,17 +262,17 @@ TEST_CASE("QUICReceiveStreamState", "[quic]") // Case1. Recv STREAM QUICReceiveStreamStateMachine ss(&in_progress, nullptr); CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); - ss.update_with_receiving_frame(*stream_frame); + CHECK(ss.update_with_receiving_frame(*stream_frame)); CHECK(ss.get() == QUICReceiveStreamState::Recv); // Case2. Recv FIN in a STREAM CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); - ss.update_with_receiving_frame(*stream_frame_with_fin); + CHECK(ss.update_with_receiving_frame(*stream_frame_with_fin)); CHECK(ss.get() == QUICReceiveStreamState::SizeKnown); // Case3. Recv RESET_STREAM CHECK(ss.is_allowed_to_receive(QUICFrameType::RESET_STREAM)); - ss.update_with_receiving_frame(*rst_stream_frame); + CHECK(ss.update_with_receiving_frame(*rst_stream_frame)); CHECK(ss.get() == QUICReceiveStreamState::ResetRecvd); } @@ -284,24 +284,24 @@ TEST_CASE("QUICReceiveStreamState", "[quic]") QUICReceiveStreamStateMachine ss(&in_progress, nullptr); CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); in_progress.set_transfer_progress(1); - ss.update_with_receiving_frame(*stream_frame); + CHECK(ss.update_with_receiving_frame(*stream_frame)); CHECK(ss.get() == QUICReceiveStreamState::Recv); // Case2. Recv FIN in a STREAM CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); in_progress.set_transfer_goal(3); - ss.update_with_receiving_frame(*stream_frame_with_fin); + CHECK(ss.update_with_receiving_frame(*stream_frame_with_fin)); CHECK(ss.get() == QUICReceiveStreamState::SizeKnown); // Case3. Recv ALL data CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); in_progress.set_transfer_progress(3); - ss.update_with_receiving_frame(*stream_frame_delayed); + CHECK(ss.update_with_receiving_frame(*stream_frame_delayed)); CHECK(ss.get() == QUICReceiveStreamState::DataRecvd); // Case4. Recv RESET_STREAM CHECK(ss.is_allowed_to_receive(QUICFrameType::RESET_STREAM)); - ss.update_with_receiving_frame(*rst_stream_frame); + CHECK(!ss.update_with_receiving_frame(*rst_stream_frame)); CHECK(ss.get() == QUICReceiveStreamState::DataRecvd); } @@ -313,24 +313,24 @@ TEST_CASE("QUICReceiveStreamState", "[quic]") QUICReceiveStreamStateMachine ss(&in_progress, nullptr); CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); in_progress.set_transfer_progress(1); - ss.update_with_receiving_frame(*stream_frame); + CHECK(ss.update_with_receiving_frame(*stream_frame)); CHECK(ss.get() == QUICReceiveStreamState::Recv); // Case2. Recv FIN in a STREAM CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); in_progress.set_transfer_goal(3); - ss.update_with_receiving_frame(*stream_frame_with_fin); + CHECK(ss.update_with_receiving_frame(*stream_frame_with_fin)); CHECK(ss.get() == QUICReceiveStreamState::SizeKnown); // Case3. Recv RESET_STREAM CHECK(ss.is_allowed_to_receive(QUICFrameType::RESET_STREAM)); - ss.update_with_receiving_frame(*rst_stream_frame); + CHECK(ss.update_with_receiving_frame(*rst_stream_frame)); CHECK(ss.get() == QUICReceiveStreamState::ResetRecvd); // Case4. Recv ALL data CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); in_progress.set_transfer_progress(3); - ss.update_with_receiving_frame(*stream_frame_delayed); + CHECK(!ss.update_with_receiving_frame(*stream_frame_delayed)); CHECK(ss.get() == QUICReceiveStreamState::ResetRecvd); CHECK(ss.is_allowed_to_send(QUICFrameType::STOP_SENDING) == false); } @@ -342,18 +342,18 @@ TEST_CASE("QUICReceiveStreamState", "[quic]") // Case1. Recv STREAM QUICReceiveStreamStateMachine ss(&in_progress, nullptr); CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); - ss.update_with_receiving_frame(*stream_frame); + CHECK(ss.update_with_receiving_frame(*stream_frame)); CHECK(ss.get() == QUICReceiveStreamState::Recv); // Case2. Recv FIN in a STREAM CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); - ss.update_with_receiving_frame(*stream_frame_with_fin); + CHECK(ss.update_with_receiving_frame(*stream_frame_with_fin)); CHECK(ss.get() == QUICReceiveStreamState::SizeKnown); // // Case3. Recv ALL data CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); in_progress.set_transfer_complete(true); - ss.update_with_receiving_frame(*stream_frame_delayed); + CHECK(ss.update_with_receiving_frame(*stream_frame_delayed)); // ss.update_on_transport_recv_event(); CHECK(ss.get() == QUICReceiveStreamState::DataRecvd); @@ -390,11 +390,11 @@ TEST_CASE("QUICBidiState", "[quic]") CHECK(ss.get() == QUICBidirectionalStreamState::Idle); CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); - ss.update_with_receiving_frame(*stream_frame); + CHECK(ss.update_with_receiving_frame(*stream_frame)); CHECK(ss.get() == QUICBidirectionalStreamState::Open); in_progress.set_transfer_complete(true); - ss.update_with_receiving_frame(*stream_frame_with_fin); + CHECK(ss.update_with_receiving_frame(*stream_frame_with_fin)); CHECK(ss.get() == QUICBidirectionalStreamState::HC_R); } @@ -407,10 +407,10 @@ TEST_CASE("QUICBidiState", "[quic]") CHECK(ss.get() == QUICBidirectionalStreamState::Idle); CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); - ss.update_with_receiving_frame(*stream_frame); + CHECK(ss.update_with_receiving_frame(*stream_frame)); CHECK(ss.get() == QUICBidirectionalStreamState::Open); - ss.update_with_receiving_frame(*rst_stream_frame); + CHECK(ss.update_with_receiving_frame(*rst_stream_frame)); CHECK(ss.get() == QUICBidirectionalStreamState::HC_R); } @@ -423,17 +423,17 @@ TEST_CASE("QUICBidiState", "[quic]") CHECK(ss.get() == QUICBidirectionalStreamState::Idle); CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM)); - ss.update_with_sending_frame(*stream_frame); - + CHECK(ss.update_with_sending_frame(*stream_frame)); CHECK(ss.get() == QUICBidirectionalStreamState::Open); - ss.update_with_sending_frame(*stream_frame_with_fin); + + CHECK(ss.update_with_sending_frame(*stream_frame_with_fin)); // internal state is changed CHECK(ss.get() == QUICBidirectionalStreamState::Open); out_progress.set_transfer_complete(true); - ss.update_on_ack(); + CHECK(ss.update_on_ack()); CHECK(ss.get() == QUICBidirectionalStreamState::HC_L); - ss.update_with_sending_frame(*stream_frame_delayed); + CHECK(!ss.update_with_sending_frame(*stream_frame_delayed)); CHECK(ss.get() == QUICBidirectionalStreamState::HC_L); } @@ -446,10 +446,10 @@ TEST_CASE("QUICBidiState", "[quic]") CHECK(ss.get() == QUICBidirectionalStreamState::Idle); CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM)); - ss.update_with_sending_frame(*stream_frame); - + CHECK(ss.update_with_sending_frame(*stream_frame)); CHECK(ss.get() == QUICBidirectionalStreamState::Open); - ss.update_with_sending_frame(*rst_stream_frame); + + CHECK(ss.update_with_sending_frame(*rst_stream_frame)); CHECK(ss.get() == QUICBidirectionalStreamState::HC_L); } @@ -462,19 +462,19 @@ TEST_CASE("QUICBidiState", "[quic]") CHECK(ss.get() == QUICBidirectionalStreamState::Idle); CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM)); - ss.update_with_sending_frame(*stream_frame); - + CHECK(ss.update_with_sending_frame(*stream_frame)); CHECK(ss.get() == QUICBidirectionalStreamState::Open); - ss.update_with_sending_frame(*rst_stream_frame); + + CHECK(ss.update_with_sending_frame(*rst_stream_frame)); CHECK(ss.get() == QUICBidirectionalStreamState::HC_L); CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); - ss.update_with_receiving_frame(*stream_frame); + CHECK(!ss.update_with_receiving_frame(*stream_frame)); - ss.update_with_receiving_frame(*rst_stream_frame); + CHECK(ss.update_with_receiving_frame(*rst_stream_frame)); CHECK(ss.get() == QUICBidirectionalStreamState::Closed); - ss.update_on_eos(); + CHECK(ss.update_on_eos()); // internal state is changed CHECK(ss.get() == QUICBidirectionalStreamState::Closed); } @@ -487,20 +487,20 @@ TEST_CASE("QUICBidiState", "[quic]") CHECK(ss.get() == QUICBidirectionalStreamState::Idle); CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM)); - ss.update_with_sending_frame(*stream_frame_with_fin); + CHECK(ss.update_with_sending_frame(*stream_frame_with_fin)); CHECK(ss.get() == QUICBidirectionalStreamState::Open); out_progress.set_transfer_complete(true); - ss.update_on_ack(); + CHECK(ss.update_on_ack()); CHECK(ss.get() == QUICBidirectionalStreamState::HC_L); CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); - ss.update_with_receiving_frame(*stream_frame); + CHECK(!ss.update_with_receiving_frame(*stream_frame)); - ss.update_with_receiving_frame(*rst_stream_frame); + CHECK(ss.update_with_receiving_frame(*rst_stream_frame)); CHECK(ss.get() == QUICBidirectionalStreamState::Closed); in_progress.set_transfer_complete(true); - ss.update_on_eos(); + CHECK(ss.update_on_eos()); // internal state is changed CHECK(ss.get() == QUICBidirectionalStreamState::Closed); } @@ -513,20 +513,20 @@ TEST_CASE("QUICBidiState", "[quic]") CHECK(ss.get() == QUICBidirectionalStreamState::Idle); CHECK(ss.is_allowed_to_send(QUICFrameType::STREAM)); - ss.update_with_sending_frame(*stream_frame_with_fin); + CHECK(ss.update_with_sending_frame(*stream_frame_with_fin)); CHECK(ss.get() == QUICBidirectionalStreamState::Open); out_progress.set_transfer_complete(true); - ss.update_on_ack(); + CHECK(ss.update_on_ack()); CHECK(ss.get() == QUICBidirectionalStreamState::HC_L); CHECK(ss.is_allowed_to_receive(QUICFrameType::STREAM)); - ss.update_with_receiving_frame(*stream_frame_delayed); + CHECK(!ss.update_with_receiving_frame(*stream_frame_delayed)); - ss.update_with_receiving_frame(*stream_frame_with_fin); + CHECK(ss.update_with_receiving_frame(*stream_frame_with_fin)); CHECK(ss.get() == QUICBidirectionalStreamState::HC_L); in_progress.set_transfer_complete(true); - ss.update_on_read(); + CHECK(ss.update_on_read()); CHECK(ss.get() == QUICBidirectionalStreamState::Closed); } } diff --git a/proxy/http3/Http09App.cc b/proxy/http3/Http09App.cc index cbde81222ab..b6dc02a9b2f 100644 --- a/proxy/http3/Http09App.cc +++ b/proxy/http3/Http09App.cc @@ -29,6 +29,7 @@ #include "P_VConnection.h" #include "P_QUICNetVConnection.h" #include "QUICDebugNames.h" +#include "QUICStreamVCAdapter.h" #include "Http3Session.h" #include "Http3Transaction.h" @@ -54,40 +55,68 @@ Http09App::~Http09App() delete this->_ssn; } +void +Http09App::on_new_stream(QUICStream &stream) +{ + auto ret = this->_streams.emplace(stream.id(), stream); + auto &info = ret.first->second; + + switch (stream.direction()) { + case QUICStreamDirection::BIDIRECTIONAL: + info.setup_read_vio(this); + info.setup_write_vio(this); + break; + case QUICStreamDirection::SEND: + info.setup_write_vio(this); + break; + case QUICStreamDirection::RECEIVE: + info.setup_read_vio(this); + break; + default: + ink_assert(false); + break; + } + + stream.set_io_adapter(&info.adapter); +} + int Http09App::main_event_handler(int event, Event *data) { Debug(debug_tag_v, "[%s] %s (%d)", this->_qc->cids().data(), get_vc_event_name(event), event); - VIO *vio = reinterpret_cast(data); - QUICStreamIO *stream_io = this->_find_stream_io(vio); + VIO *vio = reinterpret_cast(data->cookie); + QUICStreamVCAdapter *adapter = static_cast(vio->vc_server); - if (stream_io == nullptr) { + if (adapter == nullptr) { Debug(debug_tag, "[%s] Unknown Stream", this->_qc->cids().data()); return -1; } - QUICStreamId stream_id = stream_io->stream_id(); + bool is_bidirectional = adapter->stream().is_bidirectional(); + + QUICStreamId stream_id = adapter->stream().id(); Http09Transaction *txn = static_cast(this->_ssn->get_transaction(stream_id)); - uint8_t dummy; switch (event) { case VC_EVENT_READ_READY: case VC_EVENT_READ_COMPLETE: - if (!stream_io->is_bidirectional()) { + if (!is_bidirectional) { // FIXME Ignore unidirectional streams for now break; } - if (stream_io->peek(&dummy, 1)) { - if (txn == nullptr) { - txn = new Http09Transaction(this->_ssn, stream_io); - SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread()); + if (txn == nullptr) { + if (auto ret = this->_streams.find(stream_id); ret != this->_streams.end()) { + txn = new Http09Transaction(this->_ssn, ret->second); + SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread()); txn->new_transaction(); } else { - SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread()); - txn->handleEvent(event); + ink_assert(!"Stream info should exist"); } + } else { + SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread()); + txn->handleEvent(event); } break; case VC_EVENT_WRITE_READY: diff --git a/proxy/http3/Http09App.h b/proxy/http3/Http09App.h index ab35399e2c9..14ee8e647c1 100644 --- a/proxy/http3/Http09App.h +++ b/proxy/http3/Http09App.h @@ -28,6 +28,7 @@ #include "HttpSessionAccept.h" #include "QUICApplication.h" +#include "QUICStreamVCAdapter.h" class QUICNetVConnection; class Http09Session; @@ -44,8 +45,11 @@ class Http09App : public QUICApplication Http09App(QUICNetVConnection *client_vc, IpAllow::ACL &&session_acl, const HttpSessionAccept::Options &options); ~Http09App(); + void on_new_stream(QUICStream &stream) override; + int main_event_handler(int event, Event *data); private: Http09Session *_ssn = nullptr; + std::unordered_map _streams; }; diff --git a/proxy/http3/Http3App.cc b/proxy/http3/Http3App.cc index f24cc7442fc..b98230bee0c 100644 --- a/proxy/http3/Http3App.cc +++ b/proxy/http3/Http3App.cc @@ -23,11 +23,14 @@ #include "Http3App.h" +#include + #include "tscore/ink_resolver.h" #include "P_Net.h" #include "P_VConnection.h" #include "P_QUICNetVConnection.h" +#include "QUICStreamVCAdapter.h" #include "Http3.h" #include "Http3Config.h" @@ -71,10 +74,6 @@ Http3App::start() QUICConnectionErrorUPtr error; error = this->create_uni_stream(stream_id, Http3StreamType::CONTROL); - if (error == nullptr) { - this->_local_control_stream = this->_find_stream_io(stream_id); - this->_handle_uni_stream_on_write_ready(VC_EVENT_WRITE_READY, this->_local_control_stream); - } // TODO: Open uni streams for QPACK when dynamic table is used // error = this->create_uni_stream(stream_id, Http3StreamType::QPACK_ENCODER); @@ -88,41 +87,68 @@ Http3App::start() // } } +void +Http3App::on_new_stream(QUICStream &stream) +{ + auto ret = this->_streams.emplace(stream.id(), stream); + auto &info = ret.first->second; + + switch (stream.direction()) { + case QUICStreamDirection::BIDIRECTIONAL: + info.setup_read_vio(this); + info.setup_write_vio(this); + break; + case QUICStreamDirection::SEND: + info.setup_write_vio(this); + break; + case QUICStreamDirection::RECEIVE: + info.setup_read_vio(this); + break; + default: + ink_assert(false); + break; + } + + stream.set_io_adapter(&info.adapter); +} + int Http3App::main_event_handler(int event, Event *data) { Debug(debug_tag_v, "[%s] %s (%d)", this->_qc->cids().data(), get_vc_event_name(event), event); - VIO *vio = reinterpret_cast(data); - QUICStreamIO *stream_io = this->_find_stream_io(vio); + VIO *vio = reinterpret_cast(data->cookie); + QUICStreamVCAdapter *adapter = static_cast(vio->vc_server); - if (stream_io == nullptr) { + if (adapter == nullptr) { Debug(debug_tag, "[%s] Unknown Stream", this->_qc->cids().data()); return -1; } + bool is_bidirectional = adapter->stream().is_bidirectional(); + switch (event) { case VC_EVENT_READ_READY: case VC_EVENT_READ_COMPLETE: - if (stream_io->is_bidirectional()) { - this->_handle_bidi_stream_on_read_ready(event, stream_io); + if (is_bidirectional) { + this->_handle_bidi_stream_on_read_ready(event, vio); } else { - this->_handle_uni_stream_on_read_ready(event, stream_io); + this->_handle_uni_stream_on_read_ready(event, vio); } break; case VC_EVENT_WRITE_READY: case VC_EVENT_WRITE_COMPLETE: - if (stream_io->is_bidirectional()) { - this->_handle_bidi_stream_on_write_ready(event, stream_io); + if (is_bidirectional) { + this->_handle_bidi_stream_on_write_ready(event, vio); } else { - this->_handle_uni_stream_on_write_ready(event, stream_io); + this->_handle_uni_stream_on_write_ready(event, vio); } break; case VC_EVENT_EOS: - if (stream_io->is_bidirectional()) { - this->_handle_bidi_stream_on_eos(event, stream_io); + if (is_bidirectional) { + this->_handle_bidi_stream_on_eos(event, vio); } else { - this->_handle_uni_stream_on_eos(event, stream_io); + this->_handle_uni_stream_on_eos(event, vio); } break; case VC_EVENT_ERROR: @@ -143,11 +169,6 @@ Http3App::create_uni_stream(QUICStreamId &new_stream_id, Http3StreamType type) QUICConnectionErrorUPtr error = this->_qc->stream_manager()->create_uni_stream(new_stream_id); if (error == nullptr) { - QUICStreamIO *stream_io = this->_find_stream_io(new_stream_id); - - uint8_t buf[] = {static_cast(type)}; - stream_io->write(buf, sizeof(uint8_t)); - this->_local_uni_stream_map.insert(std::make_pair(new_stream_id, type)); Debug("http3", "[%" PRIu64 "] %s stream is created", new_stream_id, Http3DebugNames::stream_type(type)); @@ -159,26 +180,24 @@ Http3App::create_uni_stream(QUICStreamId &new_stream_id, Http3StreamType type) } void -Http3App::_handle_uni_stream_on_read_ready(int /* event */, QUICStreamIO *stream_io) +Http3App::_handle_uni_stream_on_read_ready(int /* event */, VIO *vio) { Http3StreamType type; - auto it = this->_remote_uni_stream_map.find(stream_io->stream_id()); + QUICStreamVCAdapter *adapter = static_cast(vio->vc_server); + auto it = this->_remote_uni_stream_map.find(adapter->stream().id()); if (it == this->_remote_uni_stream_map.end()) { // Set uni stream suitable app (HTTP/3 or QPACK) by stream type uint8_t buf; - stream_io->read(&buf, 1); + vio->get_reader()->read(&buf, 1); type = Http3Stream::type(&buf); - Debug("http3", "[%d] %s stream is opened", stream_io->stream_id(), Http3DebugNames::stream_type(type)); + Debug("http3", "[%" PRIu64 "] %s stream is opened", adapter->stream().id(), Http3DebugNames::stream_type(type)); - if (type == Http3StreamType::CONTROL) { - if (this->_remote_control_stream) { - // TODO: make error - } - this->_remote_control_stream = stream_io; + auto ret = this->_remote_uni_stream_map.insert(std::make_pair(adapter->stream().id(), type)); + if (!ret.second) { + // A stream for the type is already exisits + // TODO Return an error } - - this->_remote_uni_stream_map.insert(std::make_pair(stream_io->stream_id(), type)); } else { type = it->second; } @@ -187,13 +206,13 @@ Http3App::_handle_uni_stream_on_read_ready(int /* event */, QUICStreamIO *stream case Http3StreamType::CONTROL: case Http3StreamType::PUSH: { uint64_t nread = 0; - this->_control_stream_dispatcher.on_read_ready(*stream_io, nread); + this->_control_stream_dispatcher.on_read_ready(adapter->stream().id(), *vio->get_reader(), nread); // TODO: when PUSH comes from client, send stream error with HTTP_WRONG_STREAM_DIRECTION break; } case Http3StreamType::QPACK_ENCODER: case Http3StreamType::QPACK_DECODER: { - this->_set_qpack_stream(type, stream_io); + this->_set_qpack_stream(type, adapter); } case Http3StreamType::UNKNOWN: default: @@ -203,43 +222,57 @@ Http3App::_handle_uni_stream_on_read_ready(int /* event */, QUICStreamIO *stream } void -Http3App::_handle_bidi_stream_on_read_ready(int event, QUICStreamIO *stream_io) +Http3App::_handle_bidi_stream_on_read_ready(int event, VIO *vio) { - uint8_t dummy; - if (stream_io->peek(&dummy, 1)) { - QUICStreamId stream_id = stream_io->stream_id(); - Http3Transaction *txn = static_cast(this->_ssn->get_transaction(stream_id)); + QUICStreamVCAdapter *adapter = static_cast(vio->vc_server); - if (txn == nullptr) { - txn = new Http3Transaction(this->_ssn, stream_io); - SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread()); + QUICStreamId stream_id = adapter->stream().id(); + Http3Transaction *txn = static_cast(this->_ssn->get_transaction(stream_id)); + if (txn == nullptr) { + if (auto ret = this->_streams.find(stream_id); ret != this->_streams.end()) { + txn = new Http3Transaction(this->_ssn, ret->second); + SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread()); txn->new_transaction(); } else { - SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread()); - txn->handleEvent(event); + ink_assert(!"Stream info should exist"); } + } else { + SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread()); + txn->handleEvent(event); } } void -Http3App::_handle_uni_stream_on_write_ready(int /* event */, QUICStreamIO *stream_io) +Http3App::_handle_uni_stream_on_write_ready(int /* event */, VIO *vio) { - auto it = this->_local_uni_stream_map.find(stream_io->stream_id()); + QUICStreamVCAdapter *adapter = static_cast(vio->vc_server); + + auto it = this->_local_uni_stream_map.find(adapter->stream().id()); if (it == this->_local_uni_stream_map.end()) { ink_abort("stream not found"); return; } switch (it->second) { - case Http3StreamType::CONTROL: { - size_t nwritten = 0; - this->_control_stream_collector.on_write_ready(stream_io, nwritten); + case Http3StreamType::CONTROL: + if (!this->_is_control_stream_initialized) { + uint8_t buf[] = {static_cast(it->second)}; + vio->get_writer()->write(buf, sizeof(uint8_t)); + this->_is_control_stream_initialized = true; + } else { + size_t nwritten = 0; + bool all_done = false; + this->_control_stream_collector.on_write_ready(adapter->stream().id(), *vio->get_writer(), nwritten, all_done); + vio->nbytes += nwritten; + if (all_done) { + vio->done(); + } + } break; - } case Http3StreamType::QPACK_ENCODER: case Http3StreamType::QPACK_DECODER: { - this->_set_qpack_stream(it->second, stream_io); + this->_set_qpack_stream(it->second, adapter); } case Http3StreamType::UNKNOWN: case Http3StreamType::PUSH: @@ -249,32 +282,32 @@ Http3App::_handle_uni_stream_on_write_ready(int /* event */, QUICStreamIO *strea } void -Http3App::_handle_bidi_stream_on_eos(int /* event */, QUICStreamIO *stream_io) +Http3App::_handle_bidi_stream_on_eos(int /* event */, VIO *vio) { // TODO: handle eos } void -Http3App::_handle_uni_stream_on_eos(int /* event */, QUICStreamIO *stream_io) +Http3App::_handle_uni_stream_on_eos(int /* event */, VIO *v) { // TODO: handle eos } void -Http3App::_set_qpack_stream(Http3StreamType type, QUICStreamIO *stream_io) +Http3App::_set_qpack_stream(Http3StreamType type, QUICStreamVCAdapter *adapter) { // Change app to QPACK from Http3 if (type == Http3StreamType::QPACK_ENCODER) { if (this->_qc->direction() == NET_VCONNECTION_IN) { - this->_ssn->remote_qpack()->set_encoder_stream(stream_io); + this->_ssn->remote_qpack()->set_encoder_stream(adapter->stream().id()); } else { - this->_ssn->local_qpack()->set_encoder_stream(stream_io); + this->_ssn->local_qpack()->set_encoder_stream(adapter->stream().id()); } } else if (type == Http3StreamType::QPACK_DECODER) { if (this->_qc->direction() == NET_VCONNECTION_IN) { - this->_ssn->local_qpack()->set_decoder_stream(stream_io); + this->_ssn->local_qpack()->set_decoder_stream(adapter->stream().id()); } else { - this->_ssn->remote_qpack()->set_decoder_stream(stream_io); + this->_ssn->remote_qpack()->set_decoder_stream(adapter->stream().id()); } } else { ink_abort("unkown stream type"); @@ -282,9 +315,11 @@ Http3App::_set_qpack_stream(Http3StreamType type, QUICStreamIO *stream_io) } void -Http3App::_handle_bidi_stream_on_write_ready(int event, QUICStreamIO *stream_io) +Http3App::_handle_bidi_stream_on_write_ready(int event, VIO *vio) { - QUICStreamId stream_id = stream_io->stream_id(); + QUICStreamVCAdapter *adapter = static_cast(vio->vc_server); + + QUICStreamId stream_id = adapter->stream().id(); Http3Transaction *txn = static_cast(this->_ssn->get_transaction(stream_id)); if (txn != nullptr) { SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread()); @@ -353,7 +388,7 @@ Http3SettingsHandler::handle_frame(std::shared_ptr frame) // SETTINGS frame framer // Http3FrameUPtr -Http3SettingsFramer::generate_frame(uint16_t max_size) +Http3SettingsFramer::generate_frame() { if (this->_is_sent) { return Http3FrameFactory::create_null_frame(); diff --git a/proxy/http3/Http3App.h b/proxy/http3/Http3App.h index a48432a7dfe..5de5aea0513 100644 --- a/proxy/http3/Http3App.h +++ b/proxy/http3/Http3App.h @@ -23,9 +23,12 @@ #pragma once +#include + #include "IPAllow.h" #include "QUICApplication.h" +#include "QUICStreamVCAdapter.h" #include "HttpSessionAccept.h" @@ -48,6 +51,8 @@ class Http3App : public QUICApplication Http3App(QUICNetVConnection *client_vc, IpAllow::ACL &&session_acl, const HttpSessionAccept::Options &options); virtual ~Http3App(); + void on_new_stream(QUICStream &stream) override; + virtual void start(); virtual int main_event_handler(int event, Event *data); @@ -58,15 +63,17 @@ class Http3App : public QUICApplication protected: Http3Session *_ssn = nullptr; + std::unordered_map _streams; + private: - void _handle_uni_stream_on_read_ready(int event, QUICStreamIO *stream_io); - void _handle_uni_stream_on_write_ready(int event, QUICStreamIO *stream_io); - void _handle_uni_stream_on_eos(int event, QUICStreamIO *stream_io); - void _handle_bidi_stream_on_read_ready(int event, QUICStreamIO *stream_io); - void _handle_bidi_stream_on_write_ready(int event, QUICStreamIO *stream_io); - void _handle_bidi_stream_on_eos(int event, QUICStreamIO *stream_io); + void _handle_uni_stream_on_read_ready(int event, VIO *vio); + void _handle_uni_stream_on_write_ready(int event, VIO *vio); + void _handle_uni_stream_on_eos(int event, VIO *vio); + void _handle_bidi_stream_on_read_ready(int event, VIO *vio); + void _handle_bidi_stream_on_write_ready(int event, VIO *vio); + void _handle_bidi_stream_on_eos(int event, VIO *vio); - void _set_qpack_stream(Http3StreamType type, QUICStreamIO *stream_io); + void _set_qpack_stream(Http3StreamType type, QUICStreamVCAdapter *adapter); Http3FrameHandler *_settings_handler = nullptr; Http3FrameGenerator *_settings_framer = nullptr; @@ -74,11 +81,10 @@ class Http3App : public QUICApplication Http3FrameDispatcher _control_stream_dispatcher; Http3FrameCollector _control_stream_collector; - QUICStreamIO *_remote_control_stream; - QUICStreamIO *_local_control_stream; - std::map _remote_uni_stream_map; std::map _local_uni_stream_map; + + bool _is_control_stream_initialized = false; }; class Http3SettingsHandler : public Http3FrameHandler @@ -101,7 +107,7 @@ class Http3SettingsFramer : public Http3FrameGenerator Http3SettingsFramer(NetVConnectionContext_t context) : _context(context){}; // Http3FrameGenerator - Http3FrameUPtr generate_frame(uint16_t max_size) override; + Http3FrameUPtr generate_frame() override; bool is_done() const override; private: diff --git a/proxy/http3/Http3DataFramer.cc b/proxy/http3/Http3DataFramer.cc index eaf58627573..5fbb56dc8b1 100644 --- a/proxy/http3/Http3DataFramer.cc +++ b/proxy/http3/Http3DataFramer.cc @@ -28,7 +28,7 @@ Http3DataFramer::Http3DataFramer(Http3Transaction *transaction, VIO *source) : _transaction(transaction), _source_vio(source) {} Http3FrameUPtr -Http3DataFramer::generate_frame(uint16_t max_size) +Http3DataFramer::generate_frame() { if (!this->_transaction->is_response_header_sent()) { return Http3FrameFactory::create_null_frame(); @@ -37,11 +37,7 @@ Http3DataFramer::generate_frame(uint16_t max_size) Http3FrameUPtr frame = Http3FrameFactory::create_null_frame(); IOBufferReader *reader = this->_source_vio->get_reader(); - if (max_size <= Http3Frame::MAX_FRAM_HEADER_OVERHEAD) { - return frame; - } - - size_t payload_len = max_size - Http3Frame::MAX_FRAM_HEADER_OVERHEAD; + size_t payload_len = 128 * 1024; if (!reader->is_read_avail_more_than(payload_len)) { payload_len = reader->read_avail(); } diff --git a/proxy/http3/Http3DataFramer.h b/proxy/http3/Http3DataFramer.h index 045e86c6b47..0db8715a158 100644 --- a/proxy/http3/Http3DataFramer.h +++ b/proxy/http3/Http3DataFramer.h @@ -35,7 +35,7 @@ class Http3DataFramer : public Http3FrameGenerator Http3DataFramer(Http3Transaction *transaction, VIO *source); // Http3FrameGenerator - Http3FrameUPtr generate_frame(uint16_t max_size) override; + Http3FrameUPtr generate_frame() override; bool is_done() const override; private: diff --git a/proxy/http3/Http3Frame.cc b/proxy/http3/Http3Frame.cc index db8c98b1adf..6bb195e1a61 100644 --- a/proxy/http3/Http3Frame.cc +++ b/proxy/http3/Http3Frame.cc @@ -31,6 +31,8 @@ ClassAllocator http3DataFrameAllocator("http3DataFrameAllocator" ClassAllocator http3HeadersFrameAllocator("http3HeadersFrameAllocator"); ClassAllocator http3SettingsFrameAllocator("http3SettingsFrameAllocator"); +constexpr int HEADER_OVERHEAD = 10; // This should work as long as a payload length is less than 64 bits + // // Static functions // @@ -100,11 +102,11 @@ Http3Frame::type() const } } -void -Http3Frame::store(uint8_t *buf, size_t *len) const +Ptr +Http3Frame::to_io_buffer_block() const { - // If you really need this, you should keep the data passed to its constructor - ink_assert(!"Not supported"); + Ptr block; + return block; } void @@ -119,11 +121,20 @@ Http3Frame::reset(const uint8_t *buf, size_t len) // Http3UnknownFrame::Http3UnknownFrame(const uint8_t *buf, size_t buf_len) : Http3Frame(buf, buf_len), _buf(buf), _buf_len(buf_len) {} -void -Http3UnknownFrame::store(uint8_t *buf, size_t *len) const +Ptr +Http3UnknownFrame::to_io_buffer_block() const { - memcpy(buf, this->_buf, this->_buf_len); - *len = this->_buf_len; + Ptr block; + size_t n = 0; + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(HEADER_OVERHEAD + this->length(), BUFFER_SIZE_INDEX_32K)); + uint8_t *block_start = reinterpret_cast(block->start()); + memcpy(block_start, this->_buf, this->_buf_len); + n += this->_buf_len; + + block->fill(n); + return block; } // @@ -142,18 +153,26 @@ Http3DataFrame::Http3DataFrame(ats_unique_buf payload, size_t payload_len) this->_payload = this->_payload_uptr.get(); } -void -Http3DataFrame::store(uint8_t *buf, size_t *len) const +Ptr +Http3DataFrame::to_io_buffer_block() const { + Ptr block; + size_t n = 0; size_t written = 0; - size_t n; - QUICVariableInt::encode(buf, UINT64_MAX, n, static_cast(this->_type)); + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(HEADER_OVERHEAD + this->length(), BUFFER_SIZE_INDEX_32K)); + uint8_t *block_start = reinterpret_cast(block->start()); + + QUICVariableInt::encode(block_start, UINT64_MAX, n, static_cast(this->_type)); written += n; - QUICVariableInt::encode(buf + written, UINT64_MAX, n, this->_length); + QUICVariableInt::encode(block_start + written, UINT64_MAX, n, this->_length); written += n; - memcpy(buf + written, this->_payload, this->_payload_len); + memcpy(block_start + written, this->_payload, this->_payload_len); written += this->_payload_len; - *len = written; + + block->fill(written); + return block; } void @@ -191,18 +210,26 @@ Http3HeadersFrame::Http3HeadersFrame(ats_unique_buf header_block, size_t header_ this->_header_block = this->_header_block_uptr.get(); } -void -Http3HeadersFrame::store(uint8_t *buf, size_t *len) const +Ptr +Http3HeadersFrame::to_io_buffer_block() const { + Ptr block; + size_t n = 0; size_t written = 0; - size_t n; - QUICVariableInt::encode(buf, UINT64_MAX, n, static_cast(this->_type)); + + block = make_ptr(new_IOBufferBlock()); + block->alloc(iobuffer_size_to_index(HEADER_OVERHEAD + this->length(), BUFFER_SIZE_INDEX_32K)); + uint8_t *block_start = reinterpret_cast(block->start()); + + QUICVariableInt::encode(block_start, UINT64_MAX, n, static_cast(this->_type)); written += n; - QUICVariableInt::encode(buf + written, UINT64_MAX, n, this->_length); + QUICVariableInt::encode(block_start + written, UINT64_MAX, n, this->_length); written += n; - memcpy(buf + written, this->_header_block, this->_header_block_len); + memcpy(block_start + written, this->_header_block, this->_header_block_len); written += this->_header_block_len; - *len = written; + + block->fill(written); + return block; } void @@ -270,40 +297,48 @@ Http3SettingsFrame::Http3SettingsFrame(const uint8_t *buf, size_t buf_len, uint3 } } -void -Http3SettingsFrame::store(uint8_t *buf, size_t *len) const +Ptr +Http3SettingsFrame::to_io_buffer_block() const { - uint8_t payload[Http3SettingsFrame::MAX_PAYLOAD_SIZE] = {0}; - uint8_t *p = payload; - size_t l = 0; + Ptr header_block; + Ptr payload_block; + size_t n = 0; + size_t written = 0; + + payload_block = make_ptr(new_IOBufferBlock()); + payload_block->alloc(iobuffer_size_to_index(Http3SettingsFrame::MAX_PAYLOAD_SIZE, BUFFER_SIZE_INDEX_32K)); + uint8_t *payload_block_start = reinterpret_cast(payload_block->start()); for (auto &it : this->_settings) { - QUICIntUtil::write_QUICVariableInt(static_cast(it.first), p, &l); - p += l; - QUICIntUtil::write_QUICVariableInt(it.second, p, &l); - p += l; + QUICIntUtil::write_QUICVariableInt(static_cast(it.first), payload_block_start + written, &n); + written += n; + QUICIntUtil::write_QUICVariableInt(it.second, payload_block_start + written, &n); + written += n; } // Exercise the requirement that unknown identifiers be ignored. - 4.2.5.1. - QUICIntUtil::write_QUICVariableInt(static_cast(Http3SettingsId::UNKNOWN), p, &l); - p += l; - QUICIntUtil::write_QUICVariableInt(0, p, &l); - p += l; + QUICIntUtil::write_QUICVariableInt(static_cast(Http3SettingsId::UNKNOWN), payload_block_start + written, &n); + written += n; + QUICIntUtil::write_QUICVariableInt(0, payload_block_start + written, &n); + written += n; + payload_block->fill(written); - size_t written = 0; - size_t payload_len = p - payload; + size_t payload_len = written; + written = 0; + + header_block = make_ptr(new_IOBufferBlock()); + header_block->alloc(iobuffer_size_to_index(HEADER_OVERHEAD, BUFFER_SIZE_INDEX_32K)); + uint8_t *header_block_start = reinterpret_cast(header_block->start()); - size_t n; - QUICVariableInt::encode(buf, UINT64_MAX, n, static_cast(this->_type)); + QUICVariableInt::encode(header_block_start, UINT64_MAX, n, static_cast(this->_type)); written += n; - QUICVariableInt::encode(buf + written, UINT64_MAX, n, payload_len); + QUICVariableInt::encode(header_block_start + written, UINT64_MAX, n, payload_len); written += n; + header_block->fill(written); - // Payload - memcpy(buf + written, payload, payload_len); - written += payload_len; + header_block->next = payload_block; - *len = written; + return header_block; } void @@ -415,14 +450,14 @@ Http3FrameFactory::fast_create(const uint8_t *buf, size_t len) } std::shared_ptr -Http3FrameFactory::fast_create(QUICStreamIO &stream_io, size_t frame_len) +Http3FrameFactory::fast_create(IOBufferReader &reader, size_t frame_len) { uint8_t buf[65536]; // FIXME DATA frames can be giga bytes ink_assert(sizeof(buf) > frame_len); - if (stream_io.peek(buf, frame_len) < static_cast(frame_len)) { + if (reader.read(buf, frame_len) < static_cast(frame_len)) { // Return if whole frame data is not available return nullptr; } diff --git a/proxy/http3/Http3Frame.h b/proxy/http3/Http3Frame.h index 4ae00a9d33a..0207c2f609d 100644 --- a/proxy/http3/Http3Frame.h +++ b/proxy/http3/Http3Frame.h @@ -42,7 +42,7 @@ class Http3Frame uint64_t total_length() const; uint64_t length() const; Http3FrameType type() const; - virtual void store(uint8_t *buf, size_t *len) const; + virtual Ptr to_io_buffer_block() const; virtual void reset(const uint8_t *buf, size_t len); static int length(const uint8_t *buf, size_t buf_len, uint64_t &length); static Http3FrameType type(const uint8_t *buf, size_t buf_len); @@ -59,7 +59,7 @@ class Http3UnknownFrame : public Http3Frame Http3UnknownFrame() : Http3Frame() {} Http3UnknownFrame(const uint8_t *buf, size_t len); - void store(uint8_t *buf, size_t *len) const override; + Ptr to_io_buffer_block() const override; protected: const uint8_t *_buf = nullptr; @@ -77,7 +77,7 @@ class Http3DataFrame : public Http3Frame Http3DataFrame(const uint8_t *buf, size_t len); Http3DataFrame(ats_unique_buf payload, size_t payload_len); - void store(uint8_t *buf, size_t *len) const override; + Ptr to_io_buffer_block() const override; void reset(const uint8_t *buf, size_t len) override; const uint8_t *payload() const; @@ -100,7 +100,7 @@ class Http3HeadersFrame : public Http3Frame Http3HeadersFrame(const uint8_t *buf, size_t len); Http3HeadersFrame(ats_unique_buf header_block, size_t header_block_len); - void store(uint8_t *buf, size_t *len) const override; + Ptr to_io_buffer_block() const override; void reset(const uint8_t *buf, size_t len) override; const uint8_t *header_block() const; @@ -130,7 +130,7 @@ class Http3SettingsFrame : public Http3Frame Http3SettingsId::NUM_PLACEHOLDERS, }; - void store(uint8_t *buf, size_t *len) const override; + Ptr to_io_buffer_block() const override; void reset(const uint8_t *buf, size_t len) override; bool is_valid() const; @@ -223,7 +223,7 @@ class Http3FrameFactory * This works almost the same as create() but it reuses created objects for performance. * If you create a frame object which has the same frame type that you created before, the object will be reset by new data. */ - std::shared_ptr fast_create(QUICStreamIO &stream_io, size_t frame_len); + std::shared_ptr fast_create(IOBufferReader &reader, size_t frame_len); std::shared_ptr fast_create(const uint8_t *buf, size_t len); /* diff --git a/proxy/http3/Http3FrameCollector.cc b/proxy/http3/Http3FrameCollector.cc index 213a1e3e113..3118baca304 100644 --- a/proxy/http3/Http3FrameCollector.cc +++ b/proxy/http3/Http3FrameCollector.cc @@ -26,10 +26,9 @@ #include "Http3DebugNames.h" Http3ErrorUPtr -Http3FrameCollector::on_write_ready(QUICStreamIO *stream_io, size_t &nwritten) +Http3FrameCollector::on_write_ready(QUICStreamId stream_id, MIOBuffer &writer, size_t &nwritten, bool &all_done) { - bool all_done = true; - uint8_t tmp[32768]; + all_done = true; nwritten = 0; for (auto g : this->_generators) { @@ -37,26 +36,17 @@ Http3FrameCollector::on_write_ready(QUICStreamIO *stream_io, size_t &nwritten) continue; } size_t len = 0; - Http3FrameUPtr frame = g->generate_frame(sizeof(tmp) - nwritten); + Http3FrameUPtr frame = g->generate_frame(); if (frame) { - frame->store(tmp + nwritten, &len); + auto b = frame->to_io_buffer_block(); + len = writer.write(b.get(), INT64_MAX, 0); nwritten += len; - - Debug("http3", "[TX] [%d] | %s size=%zu", stream_io->stream_id(), Http3DebugNames::frame_type(frame->type()), len); + Debug("http3", "[TX] [%" PRIu64 "] | %s size=%zu", stream_id, Http3DebugNames::frame_type(frame->type()), len); } all_done &= g->is_done(); } - if (nwritten) { - int64_t len = stream_io->write(tmp, nwritten); - ink_assert(len > 0 && (uint64_t)len == nwritten); - } - - if (all_done) { - stream_io->write_done(); - } - return Http3ErrorUPtr(new Http3NoError()); } diff --git a/proxy/http3/Http3FrameCollector.h b/proxy/http3/Http3FrameCollector.h index 73b7c5c5ab2..3faaf5d4230 100644 --- a/proxy/http3/Http3FrameCollector.h +++ b/proxy/http3/Http3FrameCollector.h @@ -28,10 +28,12 @@ #include "Http3FrameGenerator.h" #include +class QUICStreamVCAdapter; + class Http3FrameCollector { public: - Http3ErrorUPtr on_write_ready(QUICStreamIO *stream_io, size_t &nread); + Http3ErrorUPtr on_write_ready(QUICStreamId stream_id, MIOBuffer &writer, size_t &nread, bool &all_done); void add_generator(Http3FrameGenerator *generator); diff --git a/proxy/http3/Http3FrameDispatcher.cc b/proxy/http3/Http3FrameDispatcher.cc index 64df8d5a5ed..5a578c2eacc 100644 --- a/proxy/http3/Http3FrameDispatcher.cc +++ b/proxy/http3/Http3FrameDispatcher.cc @@ -41,7 +41,7 @@ Http3FrameDispatcher::add_handler(Http3FrameHandler *handler) } Http3ErrorUPtr -Http3FrameDispatcher::on_read_ready(QUICStreamIO &stream_io, uint64_t &nread) +Http3FrameDispatcher::on_read_ready(QUICStreamId stream_id, IOBufferReader &reader, uint64_t &nread) { std::shared_ptr frame(nullptr); Http3ErrorUPtr error = Http3ErrorUPtr(new Http3NoError()); @@ -50,7 +50,8 @@ Http3FrameDispatcher::on_read_ready(QUICStreamIO &stream_io, uint64_t &nread) while (true) { // Read a length of Type field and hopefully a length of Length field too uint8_t head[16]; - int64_t read_len = stream_io.peek(head, 16); + auto p = reader.memcpy(head, 16); + int64_t read_len = p - reinterpret_cast(head); Debug("v_http3", "reading H3 frame: state=%d read_len=%" PRId64, this->_reading_state, read_len); if (this->_reading_state == READING_TYPE_LEN) { @@ -87,19 +88,21 @@ Http3FrameDispatcher::on_read_ready(QUICStreamIO &stream_io, uint64_t &nread) if (this->_reading_state == READING_PAYLOAD) { // Create a frame // Type field length + Length field length + Payload length - size_t frame_len = this->_reading_frame_type_len + this->_reading_frame_length_len + this->_reading_frame_payload_len; - frame = this->_frame_factory.fast_create(stream_io, frame_len); + size_t frame_len = this->_reading_frame_type_len + this->_reading_frame_length_len + this->_reading_frame_payload_len; + auto cloned_reader = reader.clone(); + frame = this->_frame_factory.fast_create(*cloned_reader, frame_len); + cloned_reader->dealloc(); if (frame == nullptr) { break; } // Consume buffer if frame is created nread += frame_len; - stream_io.consume(frame_len); + reader.consume(frame_len); // Dispatch Http3FrameType type = frame->type(); - Debug("http3", "[RX] [%d] | %s size=%zu", stream_io.stream_id(), Http3DebugNames::frame_type(type), frame_len); + Debug("http3", "[RX] [%" PRIu64 "] | %s size=%zu", stream_id, Http3DebugNames::frame_type(type), frame_len); std::vector handlers = this->_handlers[static_cast(type)]; for (auto h : handlers) { error = h->handle_frame(frame); diff --git a/proxy/http3/Http3FrameDispatcher.h b/proxy/http3/Http3FrameDispatcher.h index fae5121755b..315a5b0b6ee 100644 --- a/proxy/http3/Http3FrameDispatcher.h +++ b/proxy/http3/Http3FrameDispatcher.h @@ -28,10 +28,12 @@ #include "Http3FrameHandler.h" #include +class QUICStreamVCAdapter; + class Http3FrameDispatcher { public: - Http3ErrorUPtr on_read_ready(QUICStreamIO &stream_io, uint64_t &nread); + Http3ErrorUPtr on_read_ready(QUICStreamId stream_id, IOBufferReader &reader, uint64_t &nread); void add_handler(Http3FrameHandler *handler); diff --git a/proxy/http3/Http3FrameGenerator.h b/proxy/http3/Http3FrameGenerator.h index 6da188f6070..de5a4a14108 100644 --- a/proxy/http3/Http3FrameGenerator.h +++ b/proxy/http3/Http3FrameGenerator.h @@ -29,6 +29,6 @@ class Http3FrameGenerator { public: virtual ~Http3FrameGenerator(){}; - virtual Http3FrameUPtr generate_frame(uint16_t max_size) = 0; - virtual bool is_done() const = 0; + virtual Http3FrameUPtr generate_frame() = 0; + virtual bool is_done() const = 0; }; diff --git a/proxy/http3/Http3HeaderFramer.cc b/proxy/http3/Http3HeaderFramer.cc index b69f298c092..079c5d27440 100644 --- a/proxy/http3/Http3HeaderFramer.cc +++ b/proxy/http3/Http3HeaderFramer.cc @@ -38,8 +38,12 @@ Http3HeaderFramer::Http3HeaderFramer(Http3Transaction *transaction, VIO *source, } Http3FrameUPtr -Http3HeaderFramer::generate_frame(uint16_t max_size) +Http3HeaderFramer::generate_frame() { + if (!this->_source_vio->get_reader()) { + return Http3FrameFactory::create_null_frame(); + } + ink_assert(!this->_transaction->is_response_header_sent()); if (!this->_header_block) { @@ -48,8 +52,7 @@ Http3HeaderFramer::generate_frame(uint16_t max_size) } if (this->_header_block) { - // Create frames on demand base on max_size since we don't know how much we can write now - uint64_t len = std::min(this->_header_block_len - this->_header_block_wrote, static_cast(max_size)); + uint64_t len = std::min(this->_header_block_len - this->_header_block_wrote, UINT64_C(64 * 1024)); Http3FrameUPtr frame = Http3FrameFactory::create_headers_frame(this->_header_block_reader, len); diff --git a/proxy/http3/Http3HeaderFramer.h b/proxy/http3/Http3HeaderFramer.h index 2e70338586e..9559edba426 100644 --- a/proxy/http3/Http3HeaderFramer.h +++ b/proxy/http3/Http3HeaderFramer.h @@ -39,7 +39,7 @@ class Http3HeaderFramer : public Http3FrameGenerator Http3HeaderFramer(Http3Transaction *transaction, VIO *source, QPACK *qpack, uint64_t stream_id); // Http3FrameGenerator - Http3FrameUPtr generate_frame(uint16_t max_size) override; + Http3FrameUPtr generate_frame() override; bool is_done() const override; private: diff --git a/proxy/http3/Http3HeaderVIOAdaptor.cc b/proxy/http3/Http3HeaderVIOAdaptor.cc index 6a55ae7a6e3..0d246c5eeaf 100644 --- a/proxy/http3/Http3HeaderVIOAdaptor.cc +++ b/proxy/http3/Http3HeaderVIOAdaptor.cc @@ -25,10 +25,26 @@ #include "I_VIO.h" #include "HTTP.h" +#include "HTTP2.h" -Http3HeaderVIOAdaptor::Http3HeaderVIOAdaptor(HTTPHdr *hdr, QPACK *qpack, Continuation *cont, uint64_t stream_id) - : _request_header(hdr), _qpack(qpack), _cont(cont), _stream_id(stream_id) +// Constant strings for pseudo headers +const char *HTTP3_VALUE_SCHEME = ":scheme"; +const char *HTTP3_VALUE_AUTHORITY = ":authority"; + +const unsigned HTTP3_LEN_SCHEME = countof(":scheme") - 1; +const unsigned HTTP3_LEN_AUTHORITY = countof(":authority") - 1; + +Http3HeaderVIOAdaptor::Http3HeaderVIOAdaptor(VIO *sink, HTTPType http_type, QPACK *qpack, uint64_t stream_id) + : _sink_vio(sink), _qpack(qpack), _stream_id(stream_id) +{ + SET_HANDLER(&Http3HeaderVIOAdaptor::event_handler); + + this->_header.create(http_type); +} + +Http3HeaderVIOAdaptor::~Http3HeaderVIOAdaptor() { + this->_header.destroy(); } std::vector @@ -43,8 +59,7 @@ Http3HeaderVIOAdaptor::handle_frame(std::shared_ptr frame) ink_assert(frame->type() == Http3FrameType::HEADERS); const Http3HeadersFrame *hframe = dynamic_cast(frame.get()); - int res = this->_qpack->decode(this->_stream_id, hframe->header_block(), hframe->header_block_length(), *this->_request_header, - this->_cont); + int res = this->_qpack->decode(this->_stream_id, hframe->header_block(), hframe->header_block_length(), _header, this); if (res == 0) { // When decoding is not blocked, continuation should be called directly? @@ -59,3 +74,102 @@ Http3HeaderVIOAdaptor::handle_frame(std::shared_ptr frame) return Http3ErrorUPtr(new Http3NoError()); } + +bool +Http3HeaderVIOAdaptor::is_complete() +{ + return this->_is_complete; +} + +int +Http3HeaderVIOAdaptor::event_handler(int event, Event *data) +{ + switch (event) { + case QPACK_EVENT_DECODE_COMPLETE: + Debug("v_http3", "%s (%d)", "QPACK_EVENT_DECODE_COMPLETE", event); + if (this->_on_qpack_decode_complete()) { + // If READ_READY event is scheduled, should it be canceled? + } + break; + case QPACK_EVENT_DECODE_FAILED: + Debug("v_http3", "%s (%d)", "QPACK_EVENT_DECODE_FAILED", event); + // FIXME: handle error + break; + } + + return EVENT_DONE; +} + +int +Http3HeaderVIOAdaptor::_on_qpack_decode_complete() +{ + ParseResult res = this->_convert_header_from_3_to_1_1(&this->_header); + if (res == PARSE_RESULT_ERROR) { + Debug("http3", "PARSE_RESULT_ERROR"); + return -1; + } + + // FIXME: response header might be delayed from first response body because of callback from QPACK + // Workaround fix for mixed response header and body + if (http_hdr_type_get(this->_header.m_http) == HTTP_TYPE_RESPONSE) { + return 0; + } + + SCOPED_MUTEX_LOCK(lock, this->_sink_vio->mutex, this_ethread()); + MIOBuffer *writer = this->_sink_vio->get_writer(); + + // TODO: Http2Stream::send_request has same logic. It originally comes from HttpSM::write_header_into_buffer. + // a). Make HttpSM::write_header_into_buffer static + // or + // b). Add interface to HTTPHdr to dump data + // or + // c). Add interface to HttpSM to handle HTTPHdr directly + int bufindex; + int dumpoffset = 0; + int done, tmp; + IOBufferBlock *block; + do { + bufindex = 0; + tmp = dumpoffset; + block = writer->get_current_block(); + if (!block) { + writer->add_block(); + block = writer->get_current_block(); + } + done = this->_header.print(block->end(), block->write_avail(), &bufindex, &tmp); + dumpoffset += bufindex; + writer->fill(bufindex); + if (!done) { + writer->add_block(); + } + } while (!done); + + this->_is_complete = true; + return 1; +} + +ParseResult +Http3HeaderVIOAdaptor::_convert_header_from_3_to_1_1(HTTPHdr *hdrs) +{ + // TODO: do HTTP/3 specific convert, if there + + if (http_hdr_type_get(hdrs->m_http) == HTTP_TYPE_REQUEST) { + // Dirty hack to bypass checks + MIMEField *field; + if ((field = hdrs->field_find(HTTP3_VALUE_SCHEME, HTTP3_LEN_SCHEME)) == nullptr) { + char value_s[] = "https"; + MIMEField *scheme_field = hdrs->field_create(HTTP3_VALUE_SCHEME, HTTP3_LEN_SCHEME); + scheme_field->value_set(hdrs->m_heap, hdrs->m_mime, value_s, sizeof(value_s) - 1); + hdrs->field_attach(scheme_field); + } + + if ((field = hdrs->field_find(HTTP3_VALUE_AUTHORITY, HTTP3_LEN_AUTHORITY)) == nullptr) { + char value_a[] = "localhost"; + MIMEField *authority_field = hdrs->field_create(HTTP3_VALUE_AUTHORITY, HTTP3_LEN_AUTHORITY); + authority_field->value_set(hdrs->m_heap, hdrs->m_mime, value_a, sizeof(value_a) - 1); + hdrs->field_attach(authority_field); + } + } + + return http2_convert_header_from_2_to_1_1(hdrs); +} diff --git a/proxy/http3/Http3HeaderVIOAdaptor.h b/proxy/http3/Http3HeaderVIOAdaptor.h index f81456b02d1..4f064a2dac0 100644 --- a/proxy/http3/Http3HeaderVIOAdaptor.h +++ b/proxy/http3/Http3HeaderVIOAdaptor.h @@ -27,18 +27,27 @@ #include "Http3FrameHandler.h" -// TODO: rename, this is not VIOAdaptor anymore -class Http3HeaderVIOAdaptor : public Http3FrameHandler +class Http3HeaderVIOAdaptor : public Http3FrameHandler, public Continuation { public: - Http3HeaderVIOAdaptor(HTTPHdr *hdr, QPACK *qpack, Continuation *cont, uint64_t stream_id); + Http3HeaderVIOAdaptor(VIO *sink, HTTPType http_type, QPACK *qpack, uint64_t stream_id); + ~Http3HeaderVIOAdaptor(); + // Http3FrameHandler std::vector interests() override; Http3ErrorUPtr handle_frame(std::shared_ptr frame) override; + bool is_complete(); + int event_handler(int event, Event *data); + private: - HTTPHdr *_request_header = nullptr; - QPACK *_qpack = nullptr; - Continuation *_cont = nullptr; - uint64_t _stream_id = 0; + VIO *_sink_vio = nullptr; + QPACK *_qpack = nullptr; + uint64_t _stream_id = 0; + bool _is_complete = false; + + HTTPHdr _header; ///< HTTP header buffer for decoding + + int _on_qpack_decode_complete(); + ParseResult _convert_header_from_3_to_1_1(HTTPHdr *hdr); }; diff --git a/proxy/http3/Http3Transaction.cc b/proxy/http3/Http3Transaction.cc index 2e0b7435b8c..b8ffca7ee32 100644 --- a/proxy/http3/Http3Transaction.cc +++ b/proxy/http3/Http3Transaction.cc @@ -32,7 +32,6 @@ #include "Http3HeaderFramer.h" #include "Http3DataFramer.h" #include "HttpSM.h" -#include "HTTP2.h" #define Http3TransDebug(fmt, ...) \ Debug("http3_trans", "[%s] [%" PRIx32 "] " fmt, \ @@ -57,27 +56,15 @@ // // HQTransaction // -HQTransaction::HQTransaction(HQSession *session, QUICStreamIO *stream_io) : super(session), _stream_io(stream_io) +HQTransaction::HQTransaction(HQSession *session, QUICStreamVCAdapter::IOInfo &info) : super(session), _info(info) { this->mutex = new_ProxyMutex(); this->_thread = this_ethread(); this->_reader = this->_read_vio_buf.alloc_reader(); - - HTTPType http_type = HTTP_TYPE_UNKNOWN; - if (this->direction() == NET_VCONNECTION_OUT) { - http_type = HTTP_TYPE_RESPONSE; - } else { - http_type = HTTP_TYPE_REQUEST; - } - - this->_header.create(http_type); } -HQTransaction::~HQTransaction() -{ - this->_header.destroy(); -} +HQTransaction::~HQTransaction() {} void HQTransaction::set_active_timeout(ink_hrtime timeout_in) @@ -194,14 +181,14 @@ HQTransaction::reenable(VIO *vio) { if (vio->op == VIO::READ) { int64_t len = this->_process_read_vio(); - this->_stream_io->read_reenable(); + this->_info.read_vio->reenable(); if (len > 0) { this->_signal_read_event(); } } else if (vio->op == VIO::WRITE) { int64_t len = this->_process_write_vio(); - this->_stream_io->write_reenable(); + this->_info.write_vio->reenable(); if (len > 0) { this->_signal_write_event(); @@ -220,7 +207,7 @@ HQTransaction::transaction_done() int HQTransaction::get_transaction_id() const { - return this->_stream_io->stream_id(); + return this->_info.adapter.stream().id(); } void @@ -306,17 +293,24 @@ HQTransaction::_signal_write_event() // // Http3Transaction // -Http3Transaction::Http3Transaction(Http3Session *session, QUICStreamIO *stream_io) : super(session, stream_io) +Http3Transaction::Http3Transaction(Http3Session *session, QUICStreamVCAdapter::IOInfo &info) : super(session, info) { static_cast(this->_proxy_ssn)->add_transaction(static_cast(this)); + QUICStreamId stream_id = this->_info.adapter.stream().id(); - this->_header_framer = new Http3HeaderFramer(this, &this->_write_vio, session->local_qpack(), stream_io->stream_id()); + this->_header_framer = new Http3HeaderFramer(this, &this->_write_vio, session->local_qpack(), stream_id); this->_data_framer = new Http3DataFramer(this, &this->_write_vio); this->_frame_collector.add_generator(this->_header_framer); this->_frame_collector.add_generator(this->_data_framer); // this->_frame_collector.add_generator(this->_push_controller); - this->_header_handler = new Http3HeaderVIOAdaptor(&this->_header, session->remote_qpack(), this, stream_io->stream_id()); + HTTPType http_type = HTTP_TYPE_UNKNOWN; + if (this->direction() == NET_VCONNECTION_OUT) { + http_type = HTTP_TYPE_RESPONSE; + } else { + http_type = HTTP_TYPE_REQUEST; + } + this->_header_handler = new Http3HeaderVIOAdaptor(&this->_read_vio, http_type, session->remote_qpack(), stream_id); this->_data_handler = new Http3StreamDataVIOAdaptor(&this->_read_vio); this->_frame_dispatcher.add_handler(this->_header_handler); @@ -359,15 +353,20 @@ Http3Transaction::state_stream_open(int event, void *edata) if (this->_process_read_vio() > 0) { this->_signal_read_event(); } - this->_stream_io->read_reenable(); + this->_info.read_vio->reenable(); break; case VC_EVENT_READ_COMPLETE: + if (!this->_header_handler->is_complete()) { + // Delay processing READ_COMPLETE + this_ethread()->schedule_imm(this, VC_EVENT_READ_COMPLETE); + break; + } Http3TransVDebug("%s (%d)", get_vc_event_name(event), event); this->_process_read_vio(); this->_data_handler->finalize(); // always signal regardless of progress this->_signal_read_event(); - this->_stream_io->read_reenable(); + this->_info.read_vio->reenable(); break; case VC_EVENT_WRITE_READY: Http3TransVDebug("%s (%d)", get_vc_event_name(event), event); @@ -375,14 +374,14 @@ Http3Transaction::state_stream_open(int event, void *edata) if (this->_process_write_vio() > 0) { this->_signal_write_event(); } - this->_stream_io->write_reenable(); + this->_info.write_vio->reenable(); break; case VC_EVENT_WRITE_COMPLETE: Http3TransVDebug("%s (%d)", get_vc_event_name(event), event); this->_process_write_vio(); // always signal regardless of progress this->_signal_write_event(); - this->_stream_io->write_reenable(); + this->_info.write_vio->reenable(); break; case VC_EVENT_EOS: case VC_EVENT_ERROR: @@ -391,20 +390,6 @@ Http3Transaction::state_stream_open(int event, void *edata) Http3TransVDebug("%s (%d)", get_vc_event_name(event), event); break; } - case QPACK_EVENT_DECODE_COMPLETE: { - Http3TransVDebug("%s (%d)", "QPACK_EVENT_DECODE_COMPLETE", event); - int res = this->_on_qpack_decode_complete(); - if (res) { - // If READ_READY event is scheduled, should it be canceled? - this->_signal_read_event(); - } - break; - } - case QPACK_EVENT_DECODE_FAILED: { - Http3TransVDebug("%s (%d)", "QPACK_EVENT_DECODE_FAILED", event); - // FIXME: handle error - break; - } default: Http3TransDebug("Unknown event %d", event); } @@ -481,7 +466,7 @@ Http3Transaction::_process_read_vio() SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread()); uint64_t nread = 0; - this->_frame_dispatcher.on_read_ready(*this->_stream_io, nread); + this->_frame_dispatcher.on_read_ready(this->_info.adapter.stream().id(), *this->_info.read_vio->get_reader(), nread); return nread; } @@ -504,89 +489,15 @@ Http3Transaction::_process_write_vio() SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread()); size_t nwritten = 0; - this->_frame_collector.on_write_ready(this->_stream_io, nwritten); - - return nwritten; -} - -// Constant strings for pseudo headers -const char *HTTP3_VALUE_SCHEME = ":scheme"; -const char *HTTP3_VALUE_AUTHORITY = ":authority"; - -const unsigned HTTP3_LEN_SCHEME = countof(":scheme") - 1; -const unsigned HTTP3_LEN_AUTHORITY = countof(":authority") - 1; - -ParseResult -Http3Transaction::_convert_header_from_3_to_1_1(HTTPHdr *hdrs) -{ - // TODO: do HTTP/3 specific convert, if there - - if (http_hdr_type_get(hdrs->m_http) == HTTP_TYPE_REQUEST) { - // Dirty hack to bypass checks - MIMEField *field; - if ((field = hdrs->field_find(HTTP3_VALUE_SCHEME, HTTP3_LEN_SCHEME)) == nullptr) { - char value_s[] = "https"; - MIMEField *scheme_field = hdrs->field_create(HTTP3_VALUE_SCHEME, HTTP3_LEN_SCHEME); - scheme_field->value_set(hdrs->m_heap, hdrs->m_mime, value_s, sizeof(value_s) - 1); - hdrs->field_attach(scheme_field); - } - - if ((field = hdrs->field_find(HTTP3_VALUE_AUTHORITY, HTTP3_LEN_AUTHORITY)) == nullptr) { - char value_a[] = "localhost"; - MIMEField *authority_field = hdrs->field_create(HTTP3_VALUE_AUTHORITY, HTTP3_LEN_AUTHORITY); - authority_field->value_set(hdrs->m_heap, hdrs->m_mime, value_a, sizeof(value_a) - 1); - hdrs->field_attach(authority_field); - } + bool all_done = false; + this->_frame_collector.on_write_ready(this->_info.adapter.stream().id(), *this->_info.write_vio->get_writer(), nwritten, + all_done); + this->_info.write_vio->nbytes += nwritten; + if (all_done) { + this->_info.write_vio->done(); } - return http2_convert_header_from_2_to_1_1(hdrs); -} - -int -Http3Transaction::_on_qpack_decode_complete() -{ - ParseResult res = this->_convert_header_from_3_to_1_1(&this->_header); - if (res == PARSE_RESULT_ERROR) { - Http3TransDebug("PARSE_RESULT_ERROR"); - return -1; - } - - // FIXME: response header might be delayed from first response body because of callback from QPACK - // Workaround fix for mixed response header and body - if (http_hdr_type_get(this->_header.m_http) == HTTP_TYPE_RESPONSE) { - return 0; - } - - SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread()); - MIOBuffer *writer = this->_read_vio.get_writer(); - - // TODO: Http2Stream::send_request has same logic. It originally comes from HttpSM::write_header_into_buffer. - // a). Make HttpSM::write_header_into_buffer static - // or - // b). Add interface to HTTPHdr to dump data - // or - // c). Add interface to HttpSM to handle HTTPHdr directly - int bufindex; - int dumpoffset = 0; - int done, tmp; - IOBufferBlock *block; - do { - bufindex = 0; - tmp = dumpoffset; - block = writer->get_current_block(); - if (!block) { - writer->add_block(); - block = writer->get_current_block(); - } - done = this->_header.print(block->end(), block->write_avail(), &bufindex, &tmp); - dumpoffset += bufindex; - writer->fill(bufindex); - if (!done) { - writer->add_block(); - } - } while (!done); - - return 1; + return nwritten; } // TODO: Just a place holder for now @@ -599,7 +510,7 @@ Http3Transaction::has_request_body(int64_t content_length, bool is_chunked_set) // // Http09Transaction // -Http09Transaction::Http09Transaction(Http09Session *session, QUICStreamIO *stream_io) : super(session, stream_io) +Http09Transaction::Http09Transaction(Http09Session *session, QUICStreamVCAdapter::IOInfo &info) : super(session, info) { static_cast(this->_proxy_ssn)->add_transaction(static_cast(this)); @@ -637,7 +548,7 @@ Http09Transaction::state_stream_open(int event, void *edata) if (len > 0) { this->_signal_read_event(); } - this->_stream_io->read_reenable(); + this->_info.read_vio->reenable(); break; } @@ -647,7 +558,7 @@ Http09Transaction::state_stream_open(int event, void *edata) if (len > 0) { this->_signal_write_event(); } - this->_stream_io->write_reenable(); + this->_info.write_vio->reenable(); break; } @@ -720,13 +631,15 @@ Http09Transaction::_process_read_vio() } SCOPED_MUTEX_LOCK(lock, this->_read_vio.mutex, this_ethread()); + IOBufferReader *reader = this->_info.read_vio->get_reader(); // Nuke this block when we drop 0.9 support if (!this->_protocol_detected) { uint8_t start[3]; - if (this->_stream_io->peek(start, 3) < 3) { + if (!reader->is_read_avail_more_than(3)) { return 0; } + reader->memcpy(start, 3); // If the first two bit are 0 and 1, the 3rd byte is type field. // Because there is no type value larger than 0x20, we can assume that the // request is HTTP/0.9 if the value is larger than 0x20. @@ -739,16 +652,14 @@ Http09Transaction::_process_read_vio() if (this->_legacy_request) { uint64_t nread = 0; MIOBuffer *writer = this->_read_vio.get_writer(); - // Nuke this branch when we drop 0.9 support if (!this->_client_req_header_complete) { uint8_t buf[4096]; - int len = this->_stream_io->peek(buf, 4096); + int len = reader->read(buf, 4096); // Check client request is complete or not if (len < 2 || buf[len - 1] != '\n') { return 0; } - this->_stream_io->consume(len); nread += len; this->_client_req_header_complete = true; @@ -765,7 +676,7 @@ Http09Transaction::_process_read_vio() } else { uint8_t buf[4096]; int len; - while ((len = this->_stream_io->read(buf, 4096)) > 0) { + while ((len = reader->read(buf, 4096)) > 0) { nread += len; writer->write(buf, len); } @@ -779,7 +690,7 @@ Http09Transaction::_process_read_vio() int len; uint64_t nread = 0; - while ((len = this->_stream_io->read(buf, 4096)) > 0) { + while ((len = reader->read(buf, 4096)) > 0) { nread += len; } @@ -810,6 +721,9 @@ Http09Transaction::_process_write_vio() SCOPED_MUTEX_LOCK(lock, this->_write_vio.mutex, this_ethread()); IOBufferReader *reader = this->_write_vio.get_reader(); + if (!reader) { + return 0; + } if (this->_legacy_request) { // This branch is for HTTP/0.9 @@ -830,7 +744,7 @@ Http09Transaction::_process_write_vio() while (total_written < bytes_avail) { int64_t data_len = reader->block_read_avail(); - int64_t bytes_written = this->_stream_io->write(reader, data_len); + int64_t bytes_written = this->_info.write_vio->get_writer()->write(reader, data_len); if (bytes_written <= 0) { break; } @@ -844,7 +758,7 @@ Http09Transaction::_process_write_vio() // is CHUNK_READ_DONE and set FIN flag if (this->_write_vio.ntodo() == 0) { // The size of respons to client - this->_stream_io->write_done(); + this->_info.write_vio->done(); } return total_written; diff --git a/proxy/http3/Http3Transaction.h b/proxy/http3/Http3Transaction.h index 8dccccfe83f..b1ddbb6e719 100644 --- a/proxy/http3/Http3Transaction.h +++ b/proxy/http3/Http3Transaction.h @@ -25,6 +25,7 @@ #include "I_VConnection.h" #include "ProxyTransaction.h" +#include "quic/QUICStreamVCAdapter.h" #include "Http3FrameDispatcher.h" #include "Http3FrameCollector.h" @@ -34,6 +35,7 @@ class Http09Session; class Http3Session; class Http3HeaderFramer; class Http3DataFramer; +class Http3HeaderVIOAdaptor; class Http3StreamDataVIOAdaptor; class HQTransaction : public ProxyTransaction @@ -41,7 +43,7 @@ class HQTransaction : public ProxyTransaction public: using super = ProxyTransaction; - HQTransaction(HQSession *session, QUICStreamIO *stream_io); + HQTransaction(HQSession *session, QUICStreamVCAdapter::IOInfo &info); virtual ~HQTransaction(); // Implement ProxyClienTransaction interface @@ -77,15 +79,13 @@ class HQTransaction : public ProxyTransaction EThread *_thread = nullptr; Event *_cross_thread_event = nullptr; - MIOBuffer _read_vio_buf = CLIENT_CONNECTION_FIRST_READ_BUFFER_SIZE_INDEX; - QUICStreamIO *_stream_io = nullptr; + MIOBuffer _read_vio_buf = CLIENT_CONNECTION_FIRST_READ_BUFFER_SIZE_INDEX; + QUICStreamVCAdapter::IOInfo &_info; VIO _read_vio; VIO _write_vio; Event *_read_event = nullptr; Event *_write_event = nullptr; - - HTTPHdr _header; ///< HTTP header buffer for decoding }; class Http3Transaction : public HQTransaction @@ -93,7 +93,7 @@ class Http3Transaction : public HQTransaction public: using super = HQTransaction; - Http3Transaction(Http3Session *session, QUICStreamIO *stream_io); + Http3Transaction(Http3Session *session, QUICStreamVCAdapter::IOInfo &info); ~Http3Transaction(); int state_stream_open(int event, void *data) override; @@ -111,15 +111,12 @@ class Http3Transaction : public HQTransaction int64_t _process_read_vio() override; int64_t _process_write_vio() override; - ParseResult _convert_header_from_3_to_1_1(HTTPHdr *hdr); - int _on_qpack_decode_complete(); - // These are for HTTP/3 Http3FrameDispatcher _frame_dispatcher; Http3FrameCollector _frame_collector; Http3FrameGenerator *_header_framer = nullptr; Http3FrameGenerator *_data_framer = nullptr; - Http3FrameHandler *_header_handler = nullptr; + Http3HeaderVIOAdaptor *_header_handler = nullptr; Http3StreamDataVIOAdaptor *_data_handler = nullptr; }; @@ -131,7 +128,7 @@ class Http09Transaction : public HQTransaction public: using super = HQTransaction; - Http09Transaction(Http09Session *session, QUICStreamIO *stream_io); + Http09Transaction(Http09Session *session, QUICStreamVCAdapter::IOInfo &info); ~Http09Transaction(); int state_stream_open(int event, void *data) override; diff --git a/proxy/http3/QPACK.cc b/proxy/http3/QPACK.cc index 483cfd015e9..c44317ddd0c 100644 --- a/proxy/http3/QPACK.cc +++ b/proxy/http3/QPACK.cc @@ -149,22 +149,47 @@ QPACK::QPACK(QUICConnection *qc, uint32_t max_header_list_size, uint16_t max_tab QPACK::~QPACK() {} +void +QPACK::on_new_stream(QUICStream &stream) +{ + auto *info = new QUICStreamVCAdapter::IOInfo(stream); + + switch (stream.direction()) { + case QUICStreamDirection::BIDIRECTIONAL: + // ink_assert(!"QPACK does not use bidirectional streams"); + // QPACK offline interop uses stream 0 as a encoder stream. + info->setup_write_vio(this); + info->setup_read_vio(this); + break; + case QUICStreamDirection::SEND: + info->setup_write_vio(this); + break; + case QUICStreamDirection::RECEIVE: + info->setup_read_vio(this); + break; + default: + ink_assert(false); + break; + } + + stream.set_io_adapter(&info->adapter); +} + int QPACK::event_handler(int event, Event *data) { - VIO *vio = reinterpret_cast(data); - QUICStreamIO *stream_io = this->_find_stream_io(vio); + VIO *vio = reinterpret_cast(data); int ret; switch (event) { case VC_EVENT_READ_READY: - ret = this->_on_read_ready(*stream_io); + ret = this->_on_read_ready(vio); break; case VC_EVENT_READ_COMPLETE: ret = EVENT_DONE; break; case VC_EVENT_WRITE_READY: - ret = this->_on_write_ready(*stream_io); + ret = this->_on_write_ready(vio); break; case VC_EVENT_WRITE_COMPLETE: ret = EVENT_DONE; @@ -254,17 +279,15 @@ QPACK::decode(uint64_t stream_id, const uint8_t *header_block, size_t header_blo } void -QPACK::set_encoder_stream(QUICStreamIO *stream_io) +QPACK::set_encoder_stream(QUICStreamId id) { - this->_encoder_stream_id = stream_io->stream_id(); - this->set_stream(stream_io); + this->_encoder_stream_id = id; } void -QPACK::set_decoder_stream(QUICStreamIO *stream_io) +QPACK::set_decoder_stream(QUICStreamId id) { - this->_decoder_stream_id = stream_io->stream_id(); - this->set_stream(stream_io); + this->_decoder_stream_id = id; } void @@ -1008,27 +1031,32 @@ QPACK::_abort_decode() } int -QPACK::_on_read_ready(QUICStreamIO &stream_io) +QPACK::_on_read_ready(VIO *vio) { - QUICStreamId stream_id = stream_io.stream_id(); + int nread = 0; + QUICStreamId stream_id = static_cast(vio->vc_server)->stream().id(); + if (stream_id == this->_decoder_stream_id) { - return this->_on_decoder_stream_read_ready(stream_io); + nread = this->_on_decoder_stream_read_ready(*vio->get_reader()); } else if (stream_id == this->_encoder_stream_id) { - return this->_on_encoder_stream_read_ready(stream_io); + nread = this->_on_encoder_stream_read_ready(*vio->get_reader()); } else { ink_assert(!"The stream ID must match either encoder stream id or decoder stream id"); - return EVENT_DONE; } + + vio->ndone += nread; + return EVENT_DONE; } int -QPACK::_on_write_ready(QUICStreamIO &stream_io) +QPACK::_on_write_ready(VIO *vio) { - QUICStreamId stream_id = stream_io.stream_id(); + QUICStreamId stream_id = static_cast(vio->vc_server)->stream().id(); + if (stream_id == this->_decoder_stream_id) { - return this->_on_decoder_write_ready(stream_io); + return this->_on_decoder_write_ready(*vio->get_writer()); } else if (stream_id == this->_encoder_stream_id) { - return this->_on_encoder_write_ready(stream_io); + return this->_on_encoder_write_ready(*vio->get_writer()); } else { ink_assert(!"The stream ID must match either decoder stream id or decoder stream id"); return EVENT_DONE; @@ -1036,13 +1064,14 @@ QPACK::_on_write_ready(QUICStreamIO &stream_io) } int -QPACK::_on_decoder_stream_read_ready(QUICStreamIO &stream_io) +QPACK::_on_decoder_stream_read_ready(IOBufferReader &reader) { - uint8_t buf; - if (stream_io.peek(&buf, 1) > 0) { + if (reader.is_read_avail_more_than(0)) { + uint8_t buf; + reader.memcpy(&buf, 1); if (buf & 0x80) { // Header Acknowledgement uint64_t stream_id; - if (this->_read_header_acknowledgement(stream_io, stream_id) >= 0) { + if (this->_read_header_acknowledgement(reader, stream_id) >= 0) { QPACKDebug("Received Header Acknowledgement: stream_id=%" PRIu64, stream_id); this->_update_largest_known_received_index_by_stream_id(stream_id); this->_update_reference_counts(stream_id); @@ -1050,14 +1079,14 @@ QPACK::_on_decoder_stream_read_ready(QUICStreamIO &stream_io) } } else if (buf & 0x40) { // Stream Cancellation uint64_t stream_id; - if (this->_read_stream_cancellation(stream_io, stream_id) >= 0) { + if (this->_read_stream_cancellation(reader, stream_id) >= 0) { QPACKDebug("Received Stream Cancellation: stream_id=%" PRIu64, stream_id); this->_update_reference_counts(stream_id); this->_references.erase(stream_id); } } else { // Table State Synchronize uint16_t insert_count; - if (this->_read_table_state_synchronize(stream_io, insert_count) >= 0) { + if (this->_read_table_state_synchronize(reader, insert_count) >= 0) { QPACKDebug("Received Table State Synchronize: inserted_count=%d", insert_count); this->_update_largest_known_received_index_by_insert_count(insert_count); } @@ -1068,18 +1097,18 @@ QPACK::_on_decoder_stream_read_ready(QUICStreamIO &stream_io) } int -QPACK::_on_encoder_stream_read_ready(QUICStreamIO &stream_io) +QPACK::_on_encoder_stream_read_ready(IOBufferReader &reader) { - uint8_t buf; - - while (stream_io.peek(&buf, 1) > 0) { + while (reader.is_read_avail_more_than(0)) { + uint8_t buf; + reader.memcpy(&buf, 1); if (buf & 0x80) { // Insert With Name Reference bool is_static; uint16_t index; Arena arena; char *value; uint16_t value_len; - if (this->_read_insert_with_name_ref(stream_io, is_static, index, arena, &value, value_len) < 0) { + if (this->_read_insert_with_name_ref(reader, is_static, index, arena, &value, value_len) < 0) { this->_abort_decode(); return EVENT_DONE; } @@ -1091,7 +1120,7 @@ QPACK::_on_encoder_stream_read_ready(QUICStreamIO &stream_io) uint16_t name_len; char *value; uint16_t value_len; - if (this->_read_insert_without_name_ref(stream_io, arena, &name, name_len, &value, value_len) < 0) { + if (this->_read_insert_without_name_ref(reader, arena, &name, name_len, &value, value_len) < 0) { this->_abort_decode(); return EVENT_DONE; } @@ -1099,7 +1128,7 @@ QPACK::_on_encoder_stream_read_ready(QUICStreamIO &stream_io) this->_dynamic_table.insert_entry(name, name_len, value, value_len); } else if (buf & 0x20) { // Dynamic Table Size Update uint16_t max_size; - if (this->_read_dynamic_table_size_update(stream_io, max_size) < 0) { + if (this->_read_dynamic_table_size_update(reader, max_size) < 0) { this->_abort_decode(); return EVENT_DONE; } @@ -1107,7 +1136,7 @@ QPACK::_on_encoder_stream_read_ready(QUICStreamIO &stream_io) this->_dynamic_table.update_size(max_size); } else { // Duplicates uint16_t index; - if (this->_read_duplicate(stream_io, index) < 0) { + if (this->_read_duplicate(reader, index) < 0) { this->_abort_decode(); return EVENT_DONE; } @@ -1122,17 +1151,17 @@ QPACK::_on_encoder_stream_read_ready(QUICStreamIO &stream_io) } int -QPACK::_on_decoder_write_ready(QUICStreamIO &stream_io) +QPACK::_on_decoder_write_ready(MIOBuffer &writer) { - int64_t written_len = stream_io.write(this->_decoder_stream_sending_instructions_reader, INT64_MAX); + int64_t written_len = writer.write(this->_decoder_stream_sending_instructions_reader, INT64_MAX); this->_decoder_stream_sending_instructions_reader->consume(written_len); return written_len; } int -QPACK::_on_encoder_write_ready(QUICStreamIO &stream_io) +QPACK::_on_encoder_write_ready(MIOBuffer &writer) { - int64_t written_len = stream_io.write(this->_encoder_stream_sending_instructions_reader, INT64_MAX); + int64_t written_len = writer.write(this->_encoder_stream_sending_instructions_reader, INT64_MAX); this->_encoder_stream_sending_instructions_reader->consume(written_len); return written_len; } @@ -1611,14 +1640,14 @@ QPACK::_write_stream_cancellation(uint64_t stream_id) } int -QPACK::_read_insert_with_name_ref(QUICStreamIO &stream_io, bool &is_static, uint16_t &index, Arena &arena, char **value, +QPACK::_read_insert_with_name_ref(IOBufferReader &reader, bool &is_static, uint16_t &index, Arena &arena, char **value, uint16_t &value_len) { size_t read_len = 0; int ret; uint8_t input[16384]; - int input_len; - input_len = stream_io.peek(input, sizeof(input)); + uint8_t *p = reinterpret_cast(reader.memcpy(input, sizeof(input))); + int input_len = p - input; // S flag is_static = input[0] & 0x40; @@ -1638,20 +1667,20 @@ QPACK::_read_insert_with_name_ref(QUICStreamIO &stream_io, bool &is_static, uint value_len = tmp; read_len += ret; - stream_io.consume(read_len); + reader.consume(read_len); return 0; } int -QPACK::_read_insert_without_name_ref(QUICStreamIO &stream_io, Arena &arena, char **name, uint16_t &name_len, char **value, +QPACK::_read_insert_without_name_ref(IOBufferReader &reader, Arena &arena, char **name, uint16_t &name_len, char **value, uint16_t &value_len) { size_t read_len = 0; int ret; uint8_t input[16384]; - int input_len; - input_len = stream_io.peek(input, sizeof(input)); + uint8_t *p = reinterpret_cast(reader.memcpy(input, sizeof(input))); + int input_len = p - input; // Name uint64_t tmp; @@ -1668,19 +1697,19 @@ QPACK::_read_insert_without_name_ref(QUICStreamIO &stream_io, Arena &arena, char value_len = tmp; read_len += ret; - stream_io.consume(read_len); + reader.consume(read_len); return 0; } int -QPACK::_read_duplicate(QUICStreamIO &stream_io, uint16_t &index) +QPACK::_read_duplicate(IOBufferReader &reader, uint16_t &index) { size_t read_len = 0; int ret; uint8_t input[16]; - int input_len; - input_len = stream_io.peek(input, sizeof(input)); + uint8_t *p = reinterpret_cast(reader.memcpy(input, sizeof(input))); + int input_len = p - input; // Index uint64_t tmp; @@ -1690,19 +1719,19 @@ QPACK::_read_duplicate(QUICStreamIO &stream_io, uint16_t &index) index = tmp; read_len += ret; - stream_io.consume(read_len); + reader.consume(read_len); return 0; } int -QPACK::_read_dynamic_table_size_update(QUICStreamIO &stream_io, uint16_t &max_size) +QPACK::_read_dynamic_table_size_update(IOBufferReader &reader, uint16_t &max_size) { size_t read_len = 0; int ret; uint8_t input[16]; - int input_len; - input_len = stream_io.peek(input, sizeof(input)); + uint8_t *p = reinterpret_cast(reader.memcpy(input, sizeof(input))); + int input_len = p - input; uint64_t tmp; // Max Size @@ -1712,19 +1741,19 @@ QPACK::_read_dynamic_table_size_update(QUICStreamIO &stream_io, uint16_t &max_si max_size = tmp; read_len += ret; - stream_io.consume(read_len); + reader.consume(read_len); return 0; } int -QPACK::_read_table_state_synchronize(QUICStreamIO &stream_io, uint16_t &insert_count) +QPACK::_read_table_state_synchronize(IOBufferReader &reader, uint16_t &insert_count) { size_t read_len = 0; int ret; uint8_t input[16]; - int input_len; - input_len = stream_io.peek(input, sizeof(input)); + uint8_t *p = reinterpret_cast(reader.memcpy(input, sizeof(input))); + int input_len = p - input; uint64_t tmp; // Insert Count @@ -1734,19 +1763,19 @@ QPACK::_read_table_state_synchronize(QUICStreamIO &stream_io, uint16_t &insert_c insert_count = tmp; read_len += ret; - stream_io.consume(read_len); + reader.consume(read_len); return 0; } int -QPACK::_read_header_acknowledgement(QUICStreamIO &stream_io, uint64_t &stream_id) +QPACK::_read_header_acknowledgement(IOBufferReader &reader, uint64_t &stream_id) { size_t read_len = 0; int ret; uint8_t input[16]; - int input_len; - input_len = stream_io.peek(input, sizeof(input)); + uint8_t *p = reinterpret_cast(reader.memcpy(input, sizeof(input))); + int input_len = p - input; // Stream ID // FIXME xpack_decode_integer does not support uint64_t @@ -1755,19 +1784,19 @@ QPACK::_read_header_acknowledgement(QUICStreamIO &stream_io, uint64_t &stream_id } read_len += ret; - stream_io.consume(read_len); + reader.consume(read_len); return 0; } int -QPACK::_read_stream_cancellation(QUICStreamIO &stream_io, uint64_t &stream_id) +QPACK::_read_stream_cancellation(IOBufferReader &reader, uint64_t &stream_id) { size_t read_len = 0; int ret; uint8_t input[16]; - int input_len; - input_len = stream_io.peek(input, sizeof(input)); + uint8_t *p = reinterpret_cast(reader.memcpy(input, sizeof(input))); + int input_len = p - input; // Stream ID // FIXME xpack_decode_integer does not support uint64_t @@ -1776,7 +1805,7 @@ QPACK::_read_stream_cancellation(QUICStreamIO &stream_io, uint64_t &stream_id) } read_len += ret; - stream_io.consume(read_len); + reader.consume(read_len); return 0; } diff --git a/proxy/http3/QPACK.h b/proxy/http3/QPACK.h index e81b4ab0673..43244501311 100644 --- a/proxy/http3/QPACK.h +++ b/proxy/http3/QPACK.h @@ -33,6 +33,7 @@ #include "MIME.h" #include "HTTP.h" #include "QUICApplication.h" +#include "QUICStreamVCAdapter.h" #include "QUICConnection.h" class HTTPHdr; @@ -48,6 +49,8 @@ class QPACK : public QUICApplication QPACK(QUICConnection *qc, uint32_t max_header_list_size, uint16_t max_table_size, uint16_t max_blocking_streams); virtual ~QPACK(); + void on_new_stream(QUICStream &stream) override; + int event_handler(int event, Event *data); /* @@ -66,8 +69,8 @@ class QPACK : public QUICApplication int cancel(uint64_t stream_id); - void set_encoder_stream(QUICStreamIO *stream_io); - void set_decoder_stream(QUICStreamIO *stream_io); + void set_encoder_stream(QUICStreamId id); + void set_decoder_stream(QUICStreamId id); void update_max_header_list_size(uint32_t max_header_list_size); void update_max_table_size(uint16_t max_table_size); @@ -266,21 +269,21 @@ class QPACK : public QUICApplication void _update_reference_counts(uint64_t stream_id); // Encoder Stream - int _read_insert_with_name_ref(QUICStreamIO &stream_io, bool &is_static, uint16_t &index, Arena &arena, char **value, + int _read_insert_with_name_ref(IOBufferReader &reader, bool &is_static, uint16_t &index, Arena &arena, char **value, uint16_t &value_len); - int _read_insert_without_name_ref(QUICStreamIO &stream_io, Arena &arena, char **name, uint16_t &name_len, char **value, + int _read_insert_without_name_ref(IOBufferReader &reader, Arena &arena, char **name, uint16_t &name_len, char **value, uint16_t &value_len); - int _read_duplicate(QUICStreamIO &stream_io, uint16_t &index); - int _read_dynamic_table_size_update(QUICStreamIO &stream_io, uint16_t &max_size); + int _read_duplicate(IOBufferReader &reader, uint16_t &index); + int _read_dynamic_table_size_update(IOBufferReader &reader, uint16_t &max_size); int _write_insert_with_name_ref(uint16_t index, bool dynamic, const char *value, uint16_t value_len); int _write_insert_without_name_ref(const char *name, int name_len, const char *value, uint16_t value_len); int _write_duplicate(uint16_t index); int _write_dynamic_table_size_update(uint16_t max_size); // Decoder Stream - int _read_table_state_synchronize(QUICStreamIO &stream_io, uint16_t &insert_count); - int _read_header_acknowledgement(QUICStreamIO &stream_io, uint64_t &stream_id); - int _read_stream_cancellation(QUICStreamIO &stream_io, uint64_t &stream_id); + int _read_table_state_synchronize(IOBufferReader &reader, uint16_t &insert_count); + int _read_header_acknowledgement(IOBufferReader &reader, uint64_t &stream_id); + int _read_stream_cancellation(IOBufferReader &reader, uint64_t &stream_id); int _write_table_state_synchronize(uint16_t insert_count); int _write_header_acknowledgement(uint64_t stream_id); int _write_stream_cancellation(uint64_t stream_id); @@ -317,13 +320,13 @@ class QPACK : public QUICApplication uint16_t _calc_postbase_index_from_absolute_index(uint16_t base_index, uint16_t absolute_index); void _attach_header(HTTPHdr &hdr, const char *name, int name_len, const char *value, int value_len, bool never_index); - int _on_read_ready(QUICStreamIO &stream_io); - int _on_decoder_stream_read_ready(QUICStreamIO &stream_io); - int _on_encoder_stream_read_ready(QUICStreamIO &stream_io); + int _on_read_ready(VIO *vio); + int _on_decoder_stream_read_ready(IOBufferReader &reader); + int _on_encoder_stream_read_ready(IOBufferReader &reader); - int _on_write_ready(QUICStreamIO &stream_io); - int _on_decoder_write_ready(QUICStreamIO &stream_io); - int _on_encoder_write_ready(QUICStreamIO &stream_io); + int _on_write_ready(VIO *vio); + int _on_decoder_write_ready(MIOBuffer &writer); + int _on_encoder_write_ready(MIOBuffer &writer); // Stream numbers // FIXME How are these stream ids negotiated? In interop, encoder stream id have to be 0 and decoder stream id must not be used. diff --git a/proxy/http3/test/main.cc b/proxy/http3/test/main.cc index 247e118ea92..b0d4fa107f9 100644 --- a/proxy/http3/test/main.cc +++ b/proxy/http3/test/main.cc @@ -32,6 +32,8 @@ #include "RecordsConfig.h" #include "Http3Config.h" +#define TEST_THREADS 1 + struct EventProcessorListener : Catch::TestEventListenerBase { using TestEventListenerBase::TestEventListenerBase; // inherit constructor @@ -48,6 +50,12 @@ struct EventProcessorListener : Catch::TestEventListenerBase { RecProcessInit(RECM_STAND_ALONE); LibRecordsConfigInit(); + ink_event_system_init(EVENT_SYSTEM_MODULE_PUBLIC_VERSION); + eventProcessor.start(TEST_THREADS); + + Thread *main_thread = new EThread; + main_thread->set_specific(); + Http3Config::startup(); } }; diff --git a/proxy/http3/test/test_Http3Frame.cc b/proxy/http3/test/test_Http3Frame.cc index 9aa4efd4c9d..68b93b5fdfd 100644 --- a/proxy/http3/test/test_Http3Frame.cc +++ b/proxy/http3/test/test_Http3Frame.cc @@ -90,7 +90,11 @@ TEST_CASE("Store DATA Frame", "[http3]") Http3DataFrame data_frame(std::move(payload1), 4); CHECK(data_frame.length() == 4); - data_frame.store(buf, &len); + auto ibb = data_frame.to_io_buffer_block(); + IOBufferReader reader; + reader.block = ibb.get(); + len = reader.read_avail(); + reader.read(buf, sizeof(buf)); CHECK(len == 6); CHECK(memcmp(buf, expected1, len) == 0); } @@ -115,7 +119,11 @@ TEST_CASE("Store HEADERS Frame", "[http3]") Http3HeadersFrame hdrs_frame(std::move(header_block), 4); CHECK(hdrs_frame.length() == 4); - hdrs_frame.store(buf, &len); + auto ibb = hdrs_frame.to_io_buffer_block(); + IOBufferReader reader; + reader.block = ibb.get(); + len = reader.read_avail(); + reader.read(buf, sizeof(buf)); CHECK(len == 6); CHECK(memcmp(buf, expected1, len) == 0); } @@ -169,7 +177,11 @@ TEST_CASE("Store SETTINGS Frame", "[http3]") uint8_t buf[32] = {0}; size_t len; - settings_frame.store(buf, &len); + auto ibb = settings_frame.to_io_buffer_block(); + IOBufferReader reader; + reader.block = ibb.get(); + len = reader.read_avail(); + reader.read(buf, sizeof(buf)); CHECK(len == sizeof(expected)); CHECK(memcmp(buf, expected, len) == 0); } @@ -190,7 +202,11 @@ TEST_CASE("Store SETTINGS Frame", "[http3]") uint8_t buf[32] = {0}; size_t len; - settings_frame.store(buf, &len); + auto ibb = settings_frame.to_io_buffer_block(); + IOBufferReader reader; + reader.block = ibb.get(); + len = reader.read_avail(); + reader.read(buf, sizeof(buf)); CHECK(len == sizeof(expected)); CHECK(memcmp(buf, expected, len) == 0); } diff --git a/proxy/http3/test/test_QPACK.cc b/proxy/http3/test/test_QPACK.cc index 167ab7fea22..3faee2af65b 100644 --- a/proxy/http3/test/test_QPACK.cc +++ b/proxy/http3/test/test_QPACK.cc @@ -76,16 +76,18 @@ class TestQUICStream : public QUICBidirectionalStream void write(const uint8_t *buf, size_t buf_len, QUICOffset offset, bool last) { - this->_write_to_read_vio(offset, buf, buf_len, last); - this->_signal_read_event(); + this->_adapter->write(offset, buf, buf_len, last); + this->_adapter->encourge_read(); } size_t read(uint8_t *buf, size_t buf_len) { - this->_signal_write_event(); - IOBufferReader *reader = this->_write_vio.get_reader(); - return reader->read(buf, buf_len); + this->_adapter->encourge_read(); + auto ibb = this->_adapter->read(buf_len); + IOBufferReader reader; + reader.block = ibb; + return reader.read(buf, buf_len); } }; @@ -295,9 +297,11 @@ test_encode(const char *qif_file, const char *out_file, int dts, int mbs, int am QUICApplicationDriver driver; QPACK *qpack = new QPACK(driver.get_connection(), UINT32_MAX, dts, mbs); TestQUICStream *encoder_stream = new TestQUICStream(0); - TestQUICStream *decoder_stream = new TestQUICStream(9999); - qpack->set_stream(encoder_stream); - qpack->set_stream(decoder_stream); + TestQUICStream *decoder_stream = new TestQUICStream(10); + qpack->on_new_stream(*encoder_stream); + qpack->on_new_stream(*decoder_stream); + qpack->set_encoder_stream(encoder_stream->id()); + qpack->set_decoder_stream(decoder_stream->id()); uint64_t stream_id = 1; MIOBuffer *header_block = new_MIOBuffer(BUFFER_SIZE_INDEX_32K); @@ -354,7 +358,7 @@ test_decode(const char *enc_file, const char *out_file, int dts, int mbs, int am QUICApplicationDriver driver; QPACK *qpack = new QPACK(driver.get_connection(), UINT32_MAX, dts, mbs); TestQUICStream *encoder_stream = new TestQUICStream(0); - qpack->set_stream(encoder_stream); + qpack->on_new_stream(*encoder_stream); int offset = 0; uint8_t *block = nullptr; diff --git a/src/traffic_quic/quic_client.cc b/src/traffic_quic/quic_client.cc index 04fb44d654c..8b3301a65cd 100644 --- a/src/traffic_quic/quic_client.cc +++ b/src/traffic_quic/quic_client.cc @@ -151,6 +151,31 @@ Http09ClientApp::Http09ClientApp(QUICNetVConnection *qvc, const QUICClientConfig SET_HANDLER(&Http09ClientApp::main_event_handler); } +void +Http09ClientApp::on_new_stream(QUICStream &stream) +{ + auto ret = this->_streams.emplace(stream.id(), stream); + auto &info = ret.first->second; + + switch (stream.direction()) { + case QUICStreamDirection::BIDIRECTIONAL: + info.setup_read_vio(this); + info.setup_write_vio(this); + break; + case QUICStreamDirection::SEND: + info.setup_write_vio(this); + break; + case QUICStreamDirection::RECEIVE: + info.setup_read_vio(this); + break; + default: + ink_assert(false); + break; + } + + stream.set_io_adapter(&info.adapter); +} + void Http09ClientApp::start() { @@ -183,11 +208,11 @@ Http09ClientApp::_do_http_request() Http09ClientAppDebug("\n%s", request); - QUICStreamIO *stream_io = this->_find_stream_io(stream_id); - - stream_io->write(reinterpret_cast(request), request_len); - stream_io->write_done(); - stream_io->write_reenable(); + auto ite = this->_streams.find(stream_id); + VIO *write_vio = ite->second.write_vio; + write_vio->get_writer()->write(reinterpret_cast(request), request_len); + write_vio->done(); + write_vio->reenable(); } int @@ -195,10 +220,10 @@ Http09ClientApp::main_event_handler(int event, Event *data) { Http09ClientAppVDebug("%s (%d)", get_vc_event_name(event), event); - VIO *vio = reinterpret_cast(data); - QUICStreamIO *stream_io = this->_find_stream_io(vio); + VIO *vio = reinterpret_cast(data->cookie); + QUICStreamVCAdapter *adapter = static_cast(vio->vc_server); - if (stream_io == nullptr) { + if (adapter == nullptr) { Http09ClientAppDebug("Unknown Stream"); return -1; } @@ -217,8 +242,10 @@ Http09ClientApp::main_event_handler(int event, Event *data) uint8_t buf[8192] = {0}; int64_t nread; - while ((nread = stream_io->read(buf, sizeof(buf))) > 0) { + auto reader = vio->get_reader(); + while ((nread = reader->read(buf, sizeof(buf))) > 0) { std::cout.write(reinterpret_cast(buf), nread); + vio->ndone += nread; } std::cout.flush(); @@ -227,7 +254,7 @@ Http09ClientApp::main_event_handler(int event, Event *data) std::cout.rdbuf(default_stream); } - if (stream_io->is_read_done() && this->_config->close) { + if (vio->ntodo() == 0 && this->_config->close) { // Connection Close Exercise this->_qc->close_quic_connection( QUICConnectionErrorUPtr(new QUICConnectionError(QUICTransErrorCode::NO_ERROR, "Close Exercise"))); @@ -278,7 +305,7 @@ Http3ClientApp::start() this->_resp_buf = new_MIOBuffer(BUFFER_SIZE_INDEX_32K); IOBufferReader *resp_buf_reader = _resp_buf->alloc_reader(); - this->_resp_handler = new RespHandler(this->_config, resp_buf_reader, [&](void) { + this->_resp_handler = new RespHandler(this->_config, resp_buf_reader, [&](void) { if (this->_config->close) { // Connection Close Exercise this->_qc->close_quic_connection( @@ -288,6 +315,7 @@ Http3ClientApp::start() this->_qc->reset_quic_connection(); } }); + this->_req_generator = new ReqGenerator(); super::start(); this->_do_http_request(); @@ -304,10 +332,10 @@ Http3ClientApp::_do_http_request() ink_abort("Could not create bidi stream : %s", error->msg); } - QUICStreamIO *stream_io = this->_find_stream_io(stream_id); + auto ite = this->_streams.find(stream_id); // TODO: create Http3ServerTransaction - Http3Transaction *txn = new Http3Transaction(this->_ssn, stream_io); + Http3Transaction *txn = new Http3Transaction(this->_ssn, ite->second); SCOPED_MUTEX_LOCK(lock, txn->mutex, this_ethread()); // TODO: fix below issue with H2 origin conn stuff @@ -337,7 +365,7 @@ Http3ClientApp::_do_http_request() // TODO: check write avail size int64_t nbytes = this->_req_buf->write(request, request_len); IOBufferReader *buf_start = this->_req_buf->alloc_reader(); - txn->do_io_write(this, nbytes, buf_start); + txn->do_io_write(this->_req_generator, nbytes, buf_start); } // @@ -407,3 +435,14 @@ RespHandler::main_event_handler(int event, Event *data) return EVENT_CONT; } + +ReqGenerator::ReqGenerator() : Continuation(new_ProxyMutex()) +{ + SET_HANDLER(&ReqGenerator::main_event_handler); +} + +int +ReqGenerator::main_event_handler(int event, Event *data) +{ + return EVENT_CONT; +} diff --git a/src/traffic_quic/quic_client.h b/src/traffic_quic/quic_client.h index fc12dbc436c..3a524503b32 100644 --- a/src/traffic_quic/quic_client.h +++ b/src/traffic_quic/quic_client.h @@ -61,6 +61,13 @@ class RespHandler : public Continuation std::function _on_complete; }; +class ReqGenerator : public Continuation +{ +public: + ReqGenerator(); + int main_event_handler(int event, Event *data); +}; + class QUICClient : public Continuation { public: @@ -81,6 +88,8 @@ class Http09ClientApp : public QUICApplication public: Http09ClientApp(QUICNetVConnection *qvc, const QUICClientConfig *config); + void on_new_stream(QUICStream &stream) override; + void start(); int main_event_handler(int event, Event *data); @@ -89,6 +98,7 @@ class Http09ClientApp : public QUICApplication const QUICClientConfig *_config = nullptr; const char *_filename = nullptr; + std::unordered_map _streams; }; class Http3ClientApp : public Http3App @@ -106,6 +116,7 @@ class Http3ClientApp : public Http3App void _do_http_request(); RespHandler *_resp_handler = nullptr; + ReqGenerator *_req_generator = nullptr; const QUICClientConfig *_config = nullptr; MIOBuffer *_req_buf = nullptr; diff --git a/src/traffic_quic/traffic_quic.cc b/src/traffic_quic/traffic_quic.cc index f3ceaa6f9b6..4ee2f5fb0ac 100644 --- a/src/traffic_quic/traffic_quic.cc +++ b/src/traffic_quic/traffic_quic.cc @@ -277,6 +277,11 @@ HttpDebugNames::get_api_hook_name(TSHttpHookID t) { return "dummy"; } +const char * +HttpDebugNames::get_event_name(int) +{ + return "dummy"; +} #include "HttpSM.h" HttpSM::HttpSM() : Continuation(nullptr), vc_table(this) {}