├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── plot-benchmark.py │ ├── plot-fct.py │ ├── plot-goodput.py │ ├── plot-interop.py │ ├── rust.yml │ ├── tquic-benchmark.yml │ ├── tquic-fct.yml │ ├── tquic-features.yml │ ├── tquic-goodput.yml │ ├── tquic-integration.yml │ ├── tquic-interop-all.yml │ └── tquic-interop-main.yaml ├── .gitignore ├── .gitmodules ├── ADOPTERS.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CODE_OF_CONDUCT_CN.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README-CN.md ├── README.md ├── SECURITY.md ├── benches └── timer_queue.rs ├── cbindgen.toml ├── fuzz ├── .gitignore ├── Cargo.toml ├── conf │ ├── cert.crt │ └── cert.key ├── corpus │ ├── client_conn │ │ ├── 06c7e51da071b51b0bd9f05c467766814d7840ff │ │ ├── 0c0115f3f2f2822441ccc9580598600025877957 │ │ ├── 0c0d089bbc4a38d9c94b2c23bb7ba5ae7d259d95 │ │ ├── 0dbba87ef4f636de7768c54b23e37075a66d0481 │ │ ├── 13b4139b69898ee60debc69261488eb3a8dc655f │ │ ├── 159b1e8215cd92085c0343086871d160b10bf63f │ │ ├── 21db02d1f26881c7a25287ce5dd706cc67c9f1c6 │ │ ├── 27521b988366cfdeeb4465be1f8f7f35e1de653d │ │ ├── 2c3e5db89134587979040c5f14bce40457cdda39 │ │ ├── 2dbb0e7c3a9b4f87efb23ad375e7ad3b84aec283 │ │ ├── 2e8d6f4b15ed4cd7518b285f609351b594adf48a │ │ ├── 3107abb7e994e78d5c8fd11143379755c75a1251 │ │ ├── 320355ced694aa69924f6bb82e7b74f420303fd9 │ │ ├── 383e66fb4eea8b5831c1424d9ac2694d10c00b45 │ │ ├── 3b44037df6f169aeebe7dfba6325a25d4d06e89b │ │ ├── 3d87c9a28cef2784c95327423fd2ff8ec5ee5b8e │ │ ├── 42f1412408e1048cde002d1d0b2d34a0f8128599 │ │ ├── 4b5270c3472b06ffdc3cbb22fc6d1b09e75d6eb9 │ │ ├── 50551d8c0efc6cc8802bc144a80b0bfdea2323b0 │ │ ├── 52ecf9828429e0e026af45e9baace5802dfb1a58 │ │ ├── 538f7f3abf0082d1ae2975384482d423db8f5ac2 │ │ ├── 558880ed1bb9c3cee71fab46adde8dab25a7d9ab │ │ ├── 56d6b6519797cf028b002162b67a7b91dbbcf72c │ │ ├── 59940caa4c4f6c325dcd0070efe150143e245ce3 │ │ ├── 698d4af2ade4c0f4072f4eb9c8510b8068a9484f │ │ ├── 6f3fcdf109818d22bda7b5f2840fc8dc1973fbd8 │ │ ├── 75d364c7d29deb69bea633c56888358c2d6feee2 │ │ ├── 78869c048e17acc249acd453dd0a5a63cf30d7c4 │ │ ├── 7c338ed2840d2bf55f9f5e4eed04f66c80840eb3 │ │ ├── 8b1b3fa0ba0c9a5ee75430a0ce38bfdc8ff9bcb8 │ │ ├── 8d883f1577ca8c334b7c6d75ccb71209d71ced13 │ │ ├── 8fcd5cb4a24ad84d54edd3ee673af39ff6ba39aa │ │ ├── 9cd3a828018a63403e31c7b8ce1290a30880f1d0 │ │ ├── a59973e52b215fc0e5832aeb1454c3dcfb2f1824 │ │ ├── a6e25fe7a05836fb1299f5c0424e7eaea37893c2 │ │ ├── a9229d7b0d783054221257c7b3ef7a6bd8461d8f │ │ ├── adc83b19e793491b1c6ea0fd8b46cd9f32e592fc │ │ ├── b1db8fcc3a1b49c6accfe9f2e8460cfd53dedf2f │ │ ├── b41b5c840ad818c050ceff997e268a75cbc251ae │ │ ├── b4f99baa2d5f90f3235627ad00041e7061928bc8 │ │ ├── b51ba23be45260059d9d136d97ba6bdd8067404c │ │ ├── baefee12fa5ab8d50dc94b29bec7ae29c9ed1fa2 │ │ ├── bd274f83d6def3036774be2e8530cf711088b2ba │ │ ├── c121507916e1bc850cfa9741548a0d2c2e65c576 │ │ ├── c44265b18c6bfd540afed08edff805a2a6352f11 │ │ ├── c4488af0c158e8c2832cb927cfb3ce534104cd1e │ │ ├── c4ea21bb365bbeeaf5f2c654883e56d11e43c44e │ │ ├── c5c376a52033aa37edc5c058fb2f27880a930a33 │ │ ├── c6296108bb04cc8431be6399a58c75ae93c2e31f │ │ ├── d5a1c62d3dda615c891a38c2774086bbdade08c2 │ │ ├── e594b5cc4a21c43f4ad265ba100647a2a17adfcc │ │ ├── e727f9853e252169c167dfdcde6590ec31b9b979 │ │ ├── ec371eb5a99f3f17e8b11a7d236d048e141f93ca │ │ ├── efd973b00000df29da84cfa144fbc787a58c800a │ │ ├── f5f6c1e300e30ea1d0c7b01b260898a775f8fe1a │ │ └── f6804467c8235fb6e92d19c3e15ccaacb4c8d978 │ └── server_conn │ │ ├── 0aae5fe5dd50c78303fa5f19d4e1bad3ca722127 │ │ ├── 0db97c523238b85abddbd148a826fe4be4f5be4f │ │ ├── 189ebf93be3966e53e508d694226af884595c91e │ │ ├── 235f358d52e6afcb7334bb034961a3818396d55a │ │ ├── 2436421711303dc563eb6a553a99f454c247a46f │ │ ├── 25b2ccccb6178fab66b3b90646d18147f53656b4 │ │ ├── 286ac037c78f26d0fd6cc951a5b4cc9a7b1e1ab8 │ │ ├── 2c05f87e9ff39c6c2b89037072ba7e0c5fd2bed2 │ │ ├── 2ed787b25adae9312140e3f6d5160698d4496e81 │ │ ├── 30305e2cfa75d8b8faa1ea3307672300b59f72bd │ │ ├── 3032c6afcf48ee056a8694e5f82e0a33428e411e │ │ ├── 3088280ece3e8bdbf06026fb2c5d3c467719c8bf │ │ ├── 35f596229085838104079f4eae97171221cc58f9 │ │ ├── 37bf5e8a0fffd5843f1b6f9a62db451d18a99326 │ │ ├── 39092634e7e5ce7f0fda5515d8b24534fb6231f7 │ │ ├── 3c5502403e8913e1078c07aa5cf629255c6bad9a │ │ ├── 3d065fe052576fd3b071c1f005ad97f4783be460 │ │ ├── 43840b579b9ec553a87f100e415fcc88459be79c │ │ ├── 447d244508c689e8be77c40b98f5d8f76f68deb9 │ │ ├── 4534b4102502b3c52e0706afd270da266cf2ce1e │ │ ├── 4bbe1db89d7f007b334f6096609aad9bc4ab873f │ │ ├── 520ac5451fcc3d6780170c312c75da34bd923a54 │ │ ├── 57737e5a20e4adc7a2592ce326e7bcbdf56ca033 │ │ ├── 5b177e6336f955a4f30ccab6146fbc8d67e087f2 │ │ ├── 61a54ef870dda545eadc0e264f91c6f08dc3fc29 │ │ ├── 68466d46121341d45797a5b425d86bfd9021e881 │ │ ├── 6eacfd79fd62de7f5b0d0d26f644941ac112941b │ │ ├── 79534b64d01967c9b30895dc272b651913e81b2d │ │ ├── 7c176de6d502d7584d2f44b99083694f2c2a7e82 │ │ ├── 7c4d33785daa5c2370201ffa236b427aa37c9996 │ │ ├── 7efe94e87547f33eb9c28df1a0d97900d144396e │ │ ├── 814710547121f82d710c0a9785ebfe33e220250c │ │ ├── 8503132b75717cb65921651c678f8fd896a5e812 │ │ ├── 8bf403b9d53b00fbe0b58397405c87f45316f588 │ │ ├── 8f799ae02983c136ae268e1fc4ab87e3a671d0c6 │ │ ├── 94a66a555fe7ffb6109b57fe18b4f5b74612728b │ │ ├── 9a328460aaccbfe4736174b5bf928f04ec544755 │ │ ├── 9a3f49c6608954ca470e14126868be14a4bbe1bf │ │ ├── a91c84f158d1cf2e9e7f095be94b4347ad8dc234 │ │ ├── ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4 │ │ ├── adc83b19e793491b1c6ea0fd8b46cd9f32e592fc │ │ ├── b07fb17867a2dceff5c0da8cb11db54309712d30 │ │ ├── b230c817815cf64a53b53577b158e0044bc67c91 │ │ ├── b68542373c05c0ed25231d09955b2c699d37c45b │ │ ├── b8cd821899f42e9036271f730be064ae124fc6b6 │ │ ├── c2586e32c4c2badbc19cebac3e4f919cfc4f55b4 │ │ ├── c36c0706fda85a4852fc3d9fe1d9be0aa22e88c9 │ │ ├── c4e037b3827601c42ebde93c4a80372eb80bc52f │ │ ├── cac7c88b76341fc980146dce9f7cc1ddf8208ea9 │ │ ├── cb4c0995126a188ab6ce182b04921b22358409f6 │ │ ├── d0f971251d6b6cb1fcb3a27bf208df4ebf46c715 │ │ ├── f0173820ad43f6a413eee70c69a82dca39904a0f │ │ └── fb4b1879ee1cbc70aefa25646d18f6b0b4fdafcf └── fuzz_targets │ ├── client_conn.rs │ └── server_conn.rs ├── include ├── tquic.h └── tquic_def.h ├── interop ├── Dockerfile ├── README.md └── run_endpoint.sh ├── src ├── build.rs ├── codec.rs ├── congestion_control │ ├── bbr.rs │ ├── bbr3.rs │ ├── congestion_control.rs │ ├── copa.rs │ ├── cubic.rs │ ├── delivery_rate.rs │ ├── dummy.rs │ ├── hystart_plus_plus.rs │ ├── minmax.rs │ └── pacing.rs ├── connection │ ├── cid.rs │ ├── connection.rs │ ├── flowcontrol.rs │ ├── path.rs │ ├── pmtu.rs │ ├── recovery.rs │ ├── rtt.rs │ ├── space.rs │ ├── stream.rs │ └── timer.rs ├── endpoint.rs ├── error.rs ├── ffi.rs ├── frame.rs ├── h3 │ ├── connection.rs │ ├── error.rs │ ├── frame.rs │ ├── h3.rs │ ├── qpack │ │ ├── huffman.rs │ │ ├── prefix_int.rs │ │ ├── qpack.rs │ │ └── static_table.rs │ └── stream.rs ├── lib.rs ├── multipath_scheduler │ ├── multipath_scheduler.rs │ ├── scheduler_minrtt.rs │ ├── scheduler_redundant.rs │ └── scheduler_rr.rs ├── packet.rs ├── qlog │ ├── events.rs │ └── qlog.rs ├── ranges.rs ├── timer_queue.rs ├── tls │ ├── boringssl │ │ ├── boringssl.rs │ │ ├── crypto.rs │ │ └── tls.rs │ ├── key.rs │ ├── testdata │ │ ├── ca.crt │ │ ├── ca.key │ │ ├── cas │ │ │ └── 56c899cd.0 │ │ ├── cert.crt │ │ ├── cert.key │ │ ├── cert2.crt │ │ ├── cert2.key │ │ ├── cert3.crt │ │ ├── cert3.key │ │ ├── error.crt │ │ └── error.key │ └── tls.rs ├── token.rs ├── trans_param.rs └── window.rs ├── tools ├── Cargo.toml ├── README.md ├── script │ ├── tquic_qvis.sh │ ├── tquic_time_cwnd.py │ └── tquic_time_offset.py ├── src │ ├── bin │ │ ├── tquic_client.rs │ │ └── tquic_server.rs │ └── common.rs └── tests │ └── tquic_tools_test.sh └── typos.toml /.dockerignore: -------------------------------------------------------------------------------- 1 | fuzz/ 2 | include/ 3 | target/ 4 | !target/release/tquic_client 5 | !target/release/tquic_server 6 | .git/ 7 | .github/ 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/plot-benchmark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | 6 | import datetime 7 | import matplotlib.pyplot as plt 8 | import numpy as np 9 | import prettytable 10 | import termcolor 11 | 12 | # Benchmark scenarios 13 | SCENARIO = ["long", "short"] 14 | 15 | # QUIC implementations. 16 | # The first element is used as the normalization base. 17 | IMPLS = ["lsquic", "tquic"] 18 | 19 | # Round of benchmark in one scenario. 20 | ROUND = 5 21 | 22 | # File sizes in long connection scenario benchmark. 23 | LONG_FILE_SIZES = ["15K", "50K", "2M"] 24 | 25 | # File sizes in short connection scenario benchmark. 26 | SHORT_FILE_SIZES = ["1K"] 27 | 28 | # Different concurrent connections. 29 | LONG_CONNS = [10] 30 | 31 | # Different concurrent connections. 32 | SHORT_CONNS = [10] 33 | 34 | # Different concurrent streams. 35 | LONG_STREAMS = [1, 10] 36 | 37 | # Different concurrent streams. 38 | SHORT_STREAMS = [1] 39 | 40 | # Time span of the trend chart. 41 | DAYS = 90 42 | 43 | # Read data from benchmark result file. 44 | def read_data(data_dir, scen, impl, size, conn, stream, round, date): 45 | dirname = "benchmark_%s_%s_%d_%d.%s" % (scen, size, conn, stream, date) 46 | filename = "benchmark_%s_%s_%s_%d_%d.%d.%s" % (scen, impl, size, conn, stream, round, date) 47 | path = os.path.join(data_dir, dirname, filename) 48 | try: 49 | with open(path) as f: 50 | data = f.read().strip() 51 | return float(data) 52 | except: 53 | return 0.0 54 | 55 | # Load benchmark results into array. 56 | def prepare_data(data_dir): 57 | titles = [' ' for _ in range((len(LONG_FILE_SIZES)*len(LONG_CONNS)*len(LONG_STREAMS) + len(SHORT_FILE_SIZES)*len(SHORT_CONNS)*len(SHORT_STREAMS)))] 58 | result = [[[[0.0 for _ in range(len(LONG_FILE_SIZES)*len(LONG_CONNS)*len(LONG_STREAMS) + len(SHORT_FILE_SIZES)*len(SHORT_CONNS)*len(SHORT_STREAMS))] for _ in range(len(IMPLS))] for _ in range(DAYS)] for _ in range(ROUND) ] 59 | 60 | # Load long connection scenario result. 61 | I = len(LONG_FILE_SIZES) 62 | J = len(LONG_CONNS) 63 | K = len(LONG_STREAMS) 64 | N = len(IMPLS) 65 | D = DAYS 66 | for i in range(I): 67 | for j in range(J): 68 | for k in range(K): 69 | titles[i*J*K+j*K+k] = "long %s %d %d" % (LONG_FILE_SIZES[i], LONG_CONNS[j], LONG_STREAMS[k]) 70 | for n in range(N): 71 | for d in range(D): 72 | for r in range(ROUND): 73 | date = (datetime.datetime.now() - datetime.timedelta(days=d)).strftime('%Y-%m-%d') 74 | result[r][D-1-d][n][i*J*K+j*K+k] = read_data(data_dir, "long", IMPLS[n], LONG_FILE_SIZES[i], LONG_CONNS[j], LONG_STREAMS[k], r, date) 75 | 76 | # Load short connection scenario result. 77 | M = len(LONG_FILE_SIZES)*len(LONG_CONNS)*len(LONG_STREAMS) 78 | I = len(SHORT_FILE_SIZES) 79 | J = len(SHORT_CONNS) 80 | K = len(SHORT_STREAMS) 81 | N = len(IMPLS) 82 | D = DAYS 83 | for i in range(I): 84 | for j in range(J): 85 | for k in range(K): 86 | titles[M+i*J*K+j*K+k] = "short %s %d %d" % (SHORT_FILE_SIZES[i], SHORT_CONNS[j], SHORT_STREAMS[k]) 87 | for n in range(N): 88 | for d in range(D): 89 | for r in range(ROUND): 90 | date = (datetime.datetime.now() - datetime.timedelta(days=d)).strftime('%Y-%m-%d') 91 | result[r][D-1-d][n][M+i*J*K+j*K+k] = read_data(data_dir, "short", IMPLS[n], SHORT_FILE_SIZES[i], SHORT_CONNS[j], LONG_STREAMS[k], r, date) 92 | 93 | # Average by rounds. 94 | result_avg = np.mean(np.array(result), axis=0).tolist() 95 | 96 | # Normalize benchmark result. 97 | for d in range(D): 98 | base = result_avg[d][0] 99 | for i in range(1, len(result_avg[d])): 100 | result_avg[d][i] = [round(x/y, 4) if y != 0 else 0 for x, y in zip(result_avg[d][i], base)] 101 | for i in range(len(result_avg[d][0])): 102 | if result_avg[d][0][i] != 0: 103 | result_avg[d][0][i] = 1 104 | 105 | return titles, result_avg 106 | 107 | # Print benchmark performance result to stdout. 108 | def show(titles, result): 109 | table = prettytable.PrettyTable() 110 | table.field_names = titles 111 | 112 | for i in range(len(result)): 113 | colored_row_name = termcolor.colored(IMPLS[i], 'green') 114 | table.add_row([colored_row_name] + result[i]) 115 | 116 | print(table) 117 | 118 | # Plot graph according to benchmark performance result. 119 | def plot(titles, result): 120 | 121 | N = len(titles) 122 | M = len(result) 123 | 124 | width = 0.35 125 | gap = 0.5 126 | 127 | ind = np.arange(N) * (width * M + gap) 128 | 129 | fig, ax = plt.subplots() 130 | fig.set_size_inches(10, 5) 131 | for i in range(M): 132 | ax.bar(ind + i*width, result[i], width, label=IMPLS[i]) 133 | 134 | ax.set_ylabel('RPS') 135 | ax.set_title('TQUIC benchmark') 136 | ax.set_xticks(ind + width * M / 2) 137 | ax.set_xticklabels(titles, rotation=45, fontsize=6) 138 | 139 | ax.legend() 140 | 141 | plt.savefig("benchmark_all.png", dpi=300) 142 | 143 | # Plot trend of latest days. 144 | def trend(titles, result): 145 | num_scenarios = len(result[0][0]) 146 | num_curves = len(result[0]) 147 | 148 | fig = plt.figure(figsize=(10, num_scenarios*5)) 149 | 150 | for s in range(num_scenarios): 151 | ax = fig.add_subplot(num_scenarios, 1, s+1) 152 | ax.set_title(titles[s]) 153 | ax.set_xlabel("Date") 154 | ax.set_ylabel("RPS") 155 | 156 | for c in range(num_curves): 157 | y_values = [result[d][c][s] for d in range(DAYS)] 158 | ax.plot(list(range(DAYS)), y_values, label=IMPLS[c]) 159 | 160 | ax.legend() 161 | 162 | plt.tight_layout() 163 | plt.savefig("benchmark_all_trend.png", dpi=300) 164 | 165 | if __name__ == '__main__': 166 | if len(sys.argv) < 2: 167 | print("Usage: %s [data_dir]" % (sys.argv[0])) 168 | exit(1) 169 | 170 | data_dir= sys.argv[1] 171 | titles, result = prepare_data(data_dir) 172 | plot(titles, result[DAYS-1]) 173 | trend(titles, result) 174 | titles.insert(0, '') 175 | show(titles, result[DAYS-1]) 176 | 177 | -------------------------------------------------------------------------------- /.github/workflows/plot-fct.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | 6 | import json 7 | import matplotlib.pyplot as plt 8 | import numpy as np 9 | 10 | # QUIC implementations 11 | IMPLS = ["tquic", "gquiche", "lsquic", "picoquic", "quiche"] 12 | 13 | # Different modes 14 | MODES = ["1rtt", "0rtt"] 15 | 16 | # Different loss rates 17 | RATES = [0, 1, 3, 5] 18 | 19 | # Running count for each test 20 | COUNT = 30 21 | 22 | 23 | # Read a measurement file generated by fct testing 24 | def read_data(data_dir, impl, cc, mode, loss): 25 | dirname = "fct%s-%s-%s" % (mode, cc, impl) 26 | filename = "fct%s-%s-%s-%s.json" % (mode, loss, cc, impl) 27 | path = os.path.join(data_dir, dirname, filename) 28 | try: 29 | with open(path) as f: 30 | data = json.load(f) 31 | return data["measurements"][0][0]["data"] 32 | except: 33 | return None 34 | 35 | 36 | # Plot the throughput graph for the specified CC algorithm under different file 37 | # modes and packet loss rates. 38 | def plot(data_dir, cc): 39 | fig, axs = plt.subplots(len(MODES), len(RATES), figsize=(15,10)) 40 | x = np.linspace(0, COUNT, COUNT) 41 | for i in range(len(MODES)): 42 | for j in range(len(RATES)): 43 | for impl in IMPLS: 44 | data = read_data(data_dir, impl, cc, MODES[i], RATES[j]) 45 | if data is None or len(data) != COUNT: 46 | continue 47 | axs[i, j].plot(x, data, label=impl, marker=".") 48 | axs[i, j].set_xlabel("Run #") 49 | axs[i, j].set_ylabel("FCT") 50 | axs[i, j].set_title("%s loss rate %s%%" % (MODES[i], RATES[j])) 51 | axs[i, j].legend() 52 | plt.suptitle(cc.upper()) 53 | plt.tight_layout() 54 | plt.savefig("fct-%s.png" % (cc), dpi=300) 55 | 56 | 57 | if __name__ == '__main__': 58 | if len(sys.argv) < 2: 59 | print("Usage: %s [data_dir]" % (sys.argv[0])) 60 | exit(1) 61 | 62 | data_dir= sys.argv[1] 63 | plot(data_dir, "bbr") 64 | plot(data_dir, "cubic") 65 | 66 | -------------------------------------------------------------------------------- /.github/workflows/plot-goodput.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | 6 | import json 7 | import matplotlib.pyplot as plt 8 | import numpy as np 9 | 10 | # QUIC implementations 11 | IMPLS = ["tquic", "gquiche", "lsquic", "picoquic", "quiche"] 12 | 13 | # Different file sizes 14 | SIZES = ["10m", "1m", "100k"] 15 | 16 | # Different loss rates 17 | RATES = [0, 1, 3, 5] 18 | 19 | # Running count for each test 20 | COUNT = 10 21 | 22 | 23 | # Read a measurement file generated by goodput testing 24 | def read_data(data_dir, impl, cc, size, loss): 25 | dirname = "goodput%s-%s-%s" % (size, cc, impl) 26 | filename = "goodput%s-%s-%s-%s.json" % (size, loss, cc, impl) 27 | path = os.path.join(data_dir, dirname, filename) 28 | try: 29 | with open(path) as f: 30 | data = json.load(f) 31 | return data["measurements"][0][0]["data"] 32 | except: 33 | return None 34 | 35 | 36 | # Plot the throughput graph for the specified CC algorithm under different file 37 | # sizes and packet loss rates. 38 | def plot(data_dir, cc): 39 | fig, axs = plt.subplots(len(SIZES), len(RATES), figsize=(15,10)) 40 | x = np.linspace(0, COUNT, COUNT) 41 | for i in range(len(SIZES)): 42 | for j in range(len(RATES)): 43 | for impl in IMPLS: 44 | data = read_data(data_dir, impl, cc, SIZES[i], RATES[j]) 45 | if data is None or len(data) != COUNT: 46 | continue 47 | axs[i, j].plot(x, data, label=impl, marker=".") 48 | axs[i, j].set_xlabel("Run #") 49 | axs[i, j].set_ylabel("Goodput") 50 | axs[i, j].set_title("%s loss rate %s%%" % (SIZES[i], RATES[j])) 51 | axs[i, j].legend() 52 | plt.suptitle(cc.upper()) 53 | plt.tight_layout() 54 | plt.savefig("goodput-%s.png" % (cc), dpi=300) 55 | 56 | 57 | if __name__ == '__main__': 58 | if len(sys.argv) < 2: 59 | print("Usage: %s [data_dir]" % (sys.argv[0])) 60 | exit(1) 61 | 62 | data_dir= sys.argv[1] 63 | plot(data_dir, "bbr") 64 | plot(data_dir, "cubic") 65 | 66 | -------------------------------------------------------------------------------- /.github/workflows/plot-interop.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | 6 | import json 7 | import matplotlib.pyplot as plt 8 | import numpy as np 9 | from matplotlib.colors import ListedColormap 10 | 11 | # QUIC implementations 12 | CLIENT_IMPLS = ["tquic", "lsquic", "quiche", "picoquic", "ngtcp2", "msquic", 13 | "s2n-quic", "quinn", "neqo", "kwik", "aioquic", "chrome", 14 | "go-x-net", "quic-go", "mvfst"] 15 | 16 | SERVER_IMPLS = ["lsquic", "quiche", "picoquic", "ngtcp2", "msquic", "s2n-quic", 17 | "quinn", "neqo", "kwik", "aioquic", "nginx", "haproxy", 18 | "go-x-net", "quic-go", "mvfst"] 19 | 20 | # Interop test cases 21 | INTEROP_TESTS = ["handshake", "retry", "resumption", "zerortt", "amplificationlimit", "http3", 22 | "ipv6", "chacha20", "keyupdate", "transfer", "multiplexing", "longrtt", "blackhole", 23 | "handshakeloss", "handshakecorruption", "transferloss","transfercorruption"] 24 | 25 | 26 | # Read a interop file generated by interop testing 27 | def read_data(data_dir, server, client): 28 | dirname = "%s-%s/%s-%s-logs" % (server, client, server, client) 29 | path = os.path.join(data_dir, dirname, "interop.json") 30 | try: 31 | with open(path) as f: 32 | data = json.load(f) 33 | return convert_data(data["results"][0]) 34 | except: 35 | return [0] * len(INTEROP_TESTS) 36 | 37 | 38 | # Convert interop results to a vector 39 | def convert_data(result): 40 | data = [0] * len(INTEROP_TESTS) 41 | for i, name in enumerate(INTEROP_TESTS): 42 | for item in result: 43 | if item["name"] != name: 44 | continue 45 | if item["result"] == "succeeded": 46 | data[i] = 3 47 | elif item["result"] == "unsupported": 48 | data[i] = 2 49 | else: 50 | data[i] = 1 51 | break 52 | return data 53 | 54 | 55 | # Convert test result to a letter 56 | def convert_text(value): 57 | if value == 3: 58 | return "Y" # success 59 | elif value == 2: 60 | return "U" # unsupported 61 | elif value == 1: 62 | return "N" # failure 63 | else: 64 | return "-" # unknown 65 | 66 | 67 | # Plot the interop graph 68 | def plot(data_dir, is_tquic_server): 69 | impls = CLIENT_IMPLS if is_tquic_server else SERVER_IMPLS 70 | name = "server" if is_tquic_server else "client" 71 | if is_tquic_server: 72 | data = [read_data(data_dir, "tquic", impl) for impl in impls] 73 | else: 74 | data = [read_data(data_dir, impl, "tquic") for impl in impls] 75 | interop_result = np.array(data) 76 | print(interop_result) 77 | 78 | fig, ax = plt.subplots() 79 | im = ax.imshow(interop_result, cmap=ListedColormap(['gray', 'red', 'blue', 'green']), 80 | interpolation='nearest') 81 | ax.set_xticks(np.arange(len(INTEROP_TESTS)), labels=INTEROP_TESTS) 82 | ax.set_yticks(np.arange(len(impls)), labels=impls) 83 | plt.setp(ax.get_xticklabels(), rotation=45, ha="right", 84 | rotation_mode="anchor") 85 | for i in range(len(INTEROP_TESTS)): 86 | for j in range(len(impls)): 87 | text = ax.text(i, j, convert_text(interop_result[j, i]), 88 | ha="center", va="center", color="w") 89 | ax.set_title("TQUIC %s interop results" % (name)) 90 | fig.tight_layout() 91 | 92 | filename = "interop-tquic-%s.png" % (name) 93 | plt.savefig(filename, dpi=300) 94 | 95 | 96 | if __name__ == '__main__': 97 | if len(sys.argv) < 2: 98 | print("Usage: %s [data_dir]" % (sys.argv[0])) 99 | exit(1) 100 | 101 | data_dir= sys.argv[1] 102 | plot(data_dir, True) 103 | plot(data_dir, False) 104 | 105 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "develop" ] 6 | pull_request: 7 | branches: [ "develop" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build_linux: 14 | name: Build for Linux 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | submodules: 'recursive' 20 | - name: Update rust 21 | run: rustup update 22 | - name: Build TQUIC library and tools 23 | run: cargo build --all -F ffi --verbose 24 | 25 | build_macos: 26 | name: Build for MacOS 27 | runs-on: macos-latest 28 | steps: 29 | - uses: actions/checkout@v4 30 | with: 31 | submodules: 'recursive' 32 | - name: Update rust 33 | run: rustup update 34 | - name: Build TQUIC library and tools 35 | run: cargo build --all -F ffi --verbose && cargo test 36 | 37 | build_freebsd: 38 | name: Build for FreeBSD 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v4 42 | with: 43 | submodules: 'recursive' 44 | - name: Build in FreeBSD VM 45 | uses: vmactions/freebsd-vm@v1 46 | with: 47 | usesh: true 48 | prepare: | 49 | freebsd-version 50 | pkg install -y curl gmake cmake 51 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 52 | run: | 53 | . "$HOME/.cargo/env" 54 | cargo build --all -F ffi --verbose && cargo test 55 | 56 | build_windows: 57 | name: Build for Windows 58 | runs-on: windows-2022 59 | env: 60 | TARGET: "x86_64-pc-windows-msvc" 61 | steps: 62 | - uses: actions/checkout@v4 63 | with: 64 | submodules: 'recursive' 65 | - name: Install rust toolchain 66 | uses: dtolnay/rust-toolchain@master 67 | with: 68 | toolchain: stable 69 | targets: ${{ env.TARGET }} 70 | - name: Install dependencies 71 | uses: crazy-max/ghaction-chocolatey@v3 72 | with: 73 | args: install nasm 74 | - name: Build TQUIC library and tools 75 | run: cargo build --all -F ffi --verbose && cargo test 76 | 77 | build_ios: 78 | name: Build for iOS 79 | runs-on: macos-latest 80 | env: 81 | TARGET: "x86_64-apple-ios" 82 | steps: 83 | - uses: actions/checkout@v3 84 | with: 85 | submodules: 'recursive' 86 | - name: Install rust toolchain 87 | run: rustup target add ${{ env.TARGET }} 88 | - name: Remove cdylib from iOS build 89 | run: sed -i -e 's/, "cdylib"//g' Cargo.toml 90 | - name: Run cargo build 91 | uses: actions-rs/cargo@v1 92 | with: 93 | command: build 94 | args: --target=${{ env.TARGET }} --verbose --features ffi 95 | 96 | build_android: 97 | name: Build for Android 98 | runs-on: ubuntu-latest 99 | env: 100 | NDK_LTS_VER: "25" 101 | API_LEVEL: "21" 102 | TARGET: "aarch64-linux-android" 103 | ARCH: "arm64-v8a" 104 | steps: 105 | - uses: actions/checkout@v3 106 | with: 107 | submodules: 'recursive' 108 | - name: Install rust toolchain 109 | run: rustup target add ${{ env.TARGET }} 110 | - name: Download Android NDK 111 | run: | 112 | curl --http1.1 -O https://dl.google.com/android/repository/android-ndk-r${{ env.NDK_LTS_VER }}-linux.zip 113 | unzip -q android-ndk-r${{ env.NDK_LTS_VER }}-linux.zip 114 | - name: Install cargo-ndk 115 | uses: actions-rs/install@v0.1 116 | with: 117 | crate: cargo-ndk 118 | - name: Run cargo ndk 119 | uses: actions-rs/cargo@v1 120 | with: 121 | command: ndk 122 | args: -t ${{ env.ARCH }} -p ${{ env.API_LEVEL }} -- build --verbose --features ffi 123 | env: 124 | ANDROID_NDK_HOME: ${{ github.workspace }}/android-ndk-r${{ env.NDK_LTS_VER }} 125 | 126 | build_harmony: 127 | name: Build for Harmony 128 | runs-on: ubuntu-latest 129 | env: 130 | TARGET: "aarch64-unknown-linux-ohos" 131 | steps: 132 | - uses: actions/checkout@v3 133 | with: 134 | submodules: 'recursive' 135 | 136 | - name: Setup OpenHarmony SDK 137 | uses: openharmony-rs/setup-ohos-sdk@v0.1.3 138 | with: 139 | version: '4.1' 140 | 141 | - name: Setup Rust environment 142 | uses: dtolnay/rust-toolchain@stable 143 | with: 144 | toolchain: stable 145 | target: 'aarch64-unknown-linux-ohos,armv7-unknown-linux-ohos,x86_64-unknown-linux-ohos' 146 | 147 | - name: Install ohrs 148 | run: cargo install ohrs 149 | 150 | - name: Run ohrs build 151 | run: ohrs build -- --verbose --features ffi --release 152 | 153 | static_analysis: 154 | name: Static analysis 155 | runs-on: ubuntu-latest 156 | steps: 157 | - uses: actions/checkout@v4 158 | with: 159 | submodules: 'recursive' 160 | - name: Update rust 161 | run: rustup update 162 | - name: Code format check 163 | run: cargo fmt --check 164 | - name: Code spell check 165 | run: | 166 | cargo install typos-cli 167 | typos -c ./typos.toml 168 | - name: Code lint check 169 | run: cargo clippy --all -- -D warnings 170 | - name: Code lint check (FFI) 171 | run: cargo clippy --all -F ffi -- -D warnings 172 | 173 | unit_testing: 174 | name: Unit testing 175 | runs-on: ubuntu-latest 176 | steps: 177 | - uses: actions/checkout@v4 178 | with: 179 | submodules: 'recursive' 180 | - name: Update rust 181 | run: rustup update 182 | - name: Install cargo-llvm-cov 183 | uses: taiki-e/install-action@cargo-llvm-cov 184 | - name: Unit testing 185 | run: cargo test --verbose 186 | - name: Generate code coverage 187 | run: cargo llvm-cov --lcov --output-path lcov.info 188 | - name: Upload coverage to Codecov 189 | uses: codecov/codecov-action@v4 190 | with: 191 | token: ${{ secrets.CODECOV_TOKEN }} 192 | files: lcov.info 193 | fail_ci_if_error: true 194 | 195 | fuzz_testing: 196 | name: Fuzz testing 197 | runs-on: ubuntu-latest 198 | steps: 199 | - uses: actions/checkout@v4 200 | with: 201 | submodules: 'recursive' 202 | - name: Fuzz testing 203 | run: | 204 | rustup install nightly 205 | rustup default nightly 206 | cargo install cargo-fuzz 207 | cargo fuzz run client_conn -- -max_total_time=30 208 | cargo fuzz run server_conn -- -max_total_time=30 209 | 210 | -------------------------------------------------------------------------------- /.github/workflows/tquic-fct.yml: -------------------------------------------------------------------------------- 1 | name: FCT 2 | 3 | on: 4 | schedule: 5 | - cron: '30 1 * * *' 6 | workflow_dispatch: 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | QUIC_IMAGES: gquiche=tquicgroup/qirgq,lsquic=tquicgroup/qirls,picoquic=tquicgroup/qirpq,quiche=tquicgroup/qircq 11 | 12 | jobs: 13 | measure: 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | impl: [tquic,gquiche,lsquic,picoquic] 20 | case: [fct1rtt,fct0rtt] 21 | cc: [cubic, bbr] 22 | 23 | # The scheduled workflow only runs for the main repository. 24 | # You can manually trigger it if necessary. 25 | if: ${{ ( github.event_name == 'schedule' && github.repository == 'tencent/tquic' ) || github.event_name == 'workflow_dispatch' }} 26 | steps: 27 | - uses: actions/checkout@v4 28 | with: 29 | submodules: 'recursive' 30 | 31 | - name: Build docker image 32 | run: docker build -t tquic_interop:v1 -f interop/Dockerfile . 33 | 34 | - name: Install quic-interop-runner 35 | run: | 36 | git clone https://github.com/iyangsj/quic-interop-runner.git 37 | cd quic-interop-runner 38 | pip3 install -r requirements.txt 39 | 40 | - name: Install dependencies 41 | run: | 42 | sudo modprobe ip6table_filter 43 | sudo add-apt-repository -y ppa:wireshark-dev/stable 44 | sudo apt install -y tshark 45 | 46 | - name: Run the interop tests 47 | run: | 48 | cd quic-interop-runner 49 | python3 run.py -r "$QUIC_IMAGES,tquic=tquic_interop:v1" -s ${{ matrix.impl }} -c ${{ matrix.impl }} -t ${{ matrix.case }} -a ${{ matrix.cc }} -d -n "drop-rate --delay=15ms --bandwidth=10Mbps --queue=25 --rate_to_server=0 --rate_to_client=0" -j ${{ matrix.case }}-0-${{ matrix.cc }}-${{ matrix.impl }}.json 50 | python3 run.py -r "$QUIC_IMAGES,tquic=tquic_interop:v1" -s ${{ matrix.impl }} -c ${{ matrix.impl }} -t ${{ matrix.case }} -a ${{ matrix.cc }} -d -n "drop-rate --delay=15ms --bandwidth=10Mbps --queue=25 --rate_to_server=1 --rate_to_client=1" -j ${{ matrix.case }}-1-${{ matrix.cc }}-${{ matrix.impl }}.json 51 | python3 run.py -r "$QUIC_IMAGES,tquic=tquic_interop:v1" -s ${{ matrix.impl }} -c ${{ matrix.impl }} -t ${{ matrix.case }} -a ${{ matrix.cc }} -d -n "drop-rate --delay=15ms --bandwidth=10Mbps --queue=25 --rate_to_server=3 --rate_to_client=3" -j ${{ matrix.case }}-3-${{ matrix.cc }}-${{ matrix.impl }}.json 52 | python3 run.py -r "$QUIC_IMAGES,tquic=tquic_interop:v1" -s ${{ matrix.impl }} -c ${{ matrix.impl }} -t ${{ matrix.case }} -a ${{ matrix.cc }} -d -n "drop-rate --delay=15ms --bandwidth=10Mbps --queue=25 --rate_to_server=5 --rate_to_client=5" -j ${{ matrix.case }}-5-${{ matrix.cc }}-${{ matrix.impl }}.json 53 | 54 | - name: Store measurement results 55 | uses: actions/upload-artifact@v4 56 | with: 57 | name: ${{ matrix.case }}-${{ matrix.cc }}-${{ matrix.impl }} 58 | path: quic-interop-runner/fct*.json 59 | 60 | result: 61 | runs-on: ubuntu-latest 62 | needs: measure 63 | if: ${{ (( github.event_name == 'schedule' && github.repository == 'tencent/tquic' ) || github.event_name == 'workflow_dispatch') && !cancelled() }} 64 | steps: 65 | - name: Download all workflow run artifacts 66 | uses: actions/download-artifact@v4 67 | 68 | - name: Display structure of downloaded files 69 | run: ls -R 70 | 71 | - name: Display all measurement details 72 | run: grep "details.*" . -Ro 73 | 74 | - name: Download plot tools 75 | uses: actions/checkout@v4 76 | with: 77 | path: tools 78 | 79 | - name: Install dependencies 80 | run: | 81 | sudo apt install python3-matplotlib 82 | 83 | - name: Plot all measurement results 84 | run: python3 tools/.github/workflows/plot-fct.py . 85 | 86 | - name: Store all measurement results 87 | uses: actions/upload-artifact@v4 88 | with: 89 | name: fct-all-result 90 | path: fct* 91 | -------------------------------------------------------------------------------- /.github/workflows/tquic-features.yml: -------------------------------------------------------------------------------- 1 | name: Features 2 | 3 | on: 4 | push: 5 | branches: [ "develop" ] 6 | pull_request: 7 | branches: [ "develop" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | features: 14 | name: Build and test 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | submodules: 'recursive' 20 | - name: Update rust 21 | run: rustup update 22 | - name: Build without default features 23 | run: cargo build --all --no-default-features && cargo test --no-default-features 24 | - name: Build with feature(s) ffi 25 | run: cargo build --all --no-default-features -F ffi && cargo test --no-default-features -F ffi 26 | - name: Build with feature(s) qlog 27 | run: cargo build --all --no-default-features -F qlog && cargo test --no-default-features -F qlog 28 | - name: Build with feature(s) h3 29 | run: cargo build --all --no-default-features -F h3 && cargo test --no-default-features -F h3 30 | - name: Build with feature(s) ffi,qlog 31 | run: cargo build --all --no-default-features -F ffi,qlog && cargo test --no-default-features -F ffi,qlog 32 | - name: Build with feature(s) ffi,h3 33 | run: cargo build --all --no-default-features -F ffi,h3 && cargo test --no-default-features -F ffi,h3 34 | - name: Build with feature(s) qlog,h3 35 | run: cargo build --all --no-default-features -F qlog,h3 && cargo test --no-default-features -F qlog,h3 36 | - name: Build with feature(s) ffi,qlog,h3 37 | run: cargo build --all --no-default-features -F ffi,qlog,h3 && cargo test --no-default-features -F ffi,qlog,h3 38 | 39 | -------------------------------------------------------------------------------- /.github/workflows/tquic-goodput.yml: -------------------------------------------------------------------------------- 1 | name: Goodput 2 | 3 | on: 4 | schedule: 5 | - cron: '30 1 * * *' 6 | workflow_dispatch: 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | QUIC_IMAGES: gquiche=tquicgroup/qirgq,lsquic=tquicgroup/qirls,picoquic=tquicgroup/qirpq,quiche=tquicgroup/qircq 11 | 12 | jobs: 13 | measure: 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | matrix: 18 | impl: [tquic,gquiche,lsquic,picoquic,quiche] 19 | case: [goodput100k,goodput1m,goodput10m] 20 | cc: [cubic, bbr] 21 | 22 | # The scheduled workflow only runs for the main repository. 23 | # You can manually trigger it if necessary. 24 | if: ${{ ( github.event_name == 'schedule' && github.repository == 'tencent/tquic' ) || github.event_name == 'workflow_dispatch' }} 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | submodules: 'recursive' 29 | 30 | - name: Build docker image 31 | run: docker build -t tquic_interop:v1 -f interop/Dockerfile . 32 | 33 | - name: Install quic-interop-runner 34 | run: | 35 | git clone https://github.com/tquic-group/quic-interop-runner.git 36 | cd quic-interop-runner 37 | pip3 install -r requirements.txt 38 | 39 | - name: Install dependencies 40 | run: | 41 | sudo modprobe ip6table_filter 42 | sudo add-apt-repository -y ppa:wireshark-dev/stable 43 | sudo apt install -y tshark 44 | 45 | - name: Run the interop tests 46 | run: | 47 | cd quic-interop-runner 48 | python3 run.py -r "$QUIC_IMAGES,tquic=tquic_interop:v1" -s ${{ matrix.impl }} -c ${{ matrix.impl }} -t ${{ matrix.case }} -a ${{ matrix.cc }} -d -n "drop-rate --delay=15ms --bandwidth=10Mbps --queue=25 --rate_to_server=0 --rate_to_client=0" -j ${{ matrix.case }}-0-${{ matrix.cc }}-${{ matrix.impl }}.json 49 | python3 run.py -r "$QUIC_IMAGES,tquic=tquic_interop:v1" -s ${{ matrix.impl }} -c ${{ matrix.impl }} -t ${{ matrix.case }} -a ${{ matrix.cc }} -d -n "drop-rate --delay=15ms --bandwidth=10Mbps --queue=25 --rate_to_server=1 --rate_to_client=1" -j ${{ matrix.case }}-1-${{ matrix.cc }}-${{ matrix.impl }}.json 50 | python3 run.py -r "$QUIC_IMAGES,tquic=tquic_interop:v1" -s ${{ matrix.impl }} -c ${{ matrix.impl }} -t ${{ matrix.case }} -a ${{ matrix.cc }} -d -n "drop-rate --delay=15ms --bandwidth=10Mbps --queue=25 --rate_to_server=3 --rate_to_client=3" -j ${{ matrix.case }}-3-${{ matrix.cc }}-${{ matrix.impl }}.json 51 | python3 run.py -r "$QUIC_IMAGES,tquic=tquic_interop:v1" -s ${{ matrix.impl }} -c ${{ matrix.impl }} -t ${{ matrix.case }} -a ${{ matrix.cc }} -d -n "drop-rate --delay=15ms --bandwidth=10Mbps --queue=25 --rate_to_server=5 --rate_to_client=5" -j ${{ matrix.case }}-5-${{ matrix.cc }}-${{ matrix.impl }}.json 52 | 53 | - name: Store measurement results 54 | uses: actions/upload-artifact@v4 55 | with: 56 | name: ${{ matrix.case }}-${{ matrix.cc }}-${{ matrix.impl }} 57 | path: quic-interop-runner/goodput*.json 58 | 59 | result: 60 | runs-on: ubuntu-latest 61 | needs: measure 62 | steps: 63 | - name: Download all workflow run artifacts 64 | uses: actions/download-artifact@v4 65 | 66 | - name: Display structure of downloaded files 67 | run: ls -R 68 | 69 | - name: Display all measurement details 70 | run: grep "details.*" . -Ro 71 | 72 | - name: Download plot tools 73 | uses: actions/checkout@v4 74 | with: 75 | path: tools 76 | 77 | - name: Install dependencies 78 | run: | 79 | sudo apt install python3-matplotlib 80 | 81 | - name: Plot all measurement results 82 | run: python3 tools/.github/workflows/plot-goodput.py . 83 | 84 | - name: Store all measurement results 85 | uses: actions/upload-artifact@v4 86 | with: 87 | name: goodput-all-result 88 | path: goodput* 89 | -------------------------------------------------------------------------------- /.github/workflows/tquic-integration.yml: -------------------------------------------------------------------------------- 1 | name: Integration 2 | 3 | on: 4 | push: 5 | branches: [ "develop" ] 6 | pull_request: 7 | branches: [ "develop" ] 8 | workflow_dispatch: 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | extra: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | submodules: 'recursive' 20 | - name: Build 21 | run: cargo build --release --all 22 | - name: Run integration tests for multipath 23 | run: | 24 | cd tools/tests/ 25 | bash ./tquic_tools_test.sh -b ../../target/release/ -t multipath_redundant,multipath_minrtt,multipath_roundrobin -f 1000M -p 5 26 | - name: Run integration tests for disable_1rtt_encryption 27 | run: | 28 | cd tools/tests/ 29 | bash ./tquic_tools_test.sh -b ../../target/release/ -t multipath_minrtt -c '~~disable-encryption' -s '~~disable-encryption' 30 | -------------------------------------------------------------------------------- /.github/workflows/tquic-interop-all.yml: -------------------------------------------------------------------------------- 1 | name: InteropAll 2 | 3 | on: 4 | schedule: 5 | - cron: '30 3 * * *' 6 | workflow_dispatch: 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | tquic_interop_testing: 13 | name: Interop testing 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | include: 20 | - server: tquic 21 | client: tquic 22 | - server: tquic 23 | client: lsquic 24 | - server: tquic 25 | client: quiche 26 | - server: tquic 27 | client: picoquic 28 | - server: tquic 29 | client: ngtcp2 30 | - server: tquic 31 | client: msquic 32 | - server: tquic 33 | client: mvfst 34 | - server: tquic 35 | client: s2n-quic 36 | - server: tquic 37 | client: quinn 38 | - server: tquic 39 | client: neqo 40 | - server: tquic 41 | client: go-x-net 42 | - server: tquic 43 | client: quic-go 44 | - server: tquic 45 | client: kwik 46 | - server: tquic 47 | client: aioquic 48 | - server: tquic 49 | client: chrome 50 | - server: lsquic 51 | client: tquic 52 | - server: quiche 53 | client: tquic 54 | - server: picoquic 55 | client: tquic 56 | - server: ngtcp2 57 | client: tquic 58 | - server: msquic 59 | client: tquic 60 | - server: mvfst 61 | client: tquic 62 | - server: s2n-quic 63 | client: tquic 64 | - server: quinn 65 | client: tquic 66 | - server: neqo 67 | client: tquic 68 | - server: go-x-net 69 | client: tquic 70 | - server: quic-go 71 | client: tquic 72 | - server: kwik 73 | client: tquic 74 | - server: aioquic 75 | client: tquic 76 | - server: nginx 77 | client: tquic 78 | - server: haproxy 79 | client: tquic 80 | 81 | # The scheduled workflow only runs for the main repository. 82 | # You can manually trigger it if necessary. 83 | if: ${{ ( github.event_name == 'schedule' && github.repository == 'tencent/tquic' ) || github.event_name == 'workflow_dispatch' }} 84 | steps: 85 | - uses: actions/checkout@v4 86 | with: 87 | submodules: 'recursive' 88 | 89 | - name: Build docker image 90 | run: docker build -t tquic_interop:v1 -f interop/Dockerfile . 91 | 92 | - name: Install quic-interop-runner 93 | run: | 94 | git clone https://github.com/tquic-group/quic-interop-runner.git 95 | cd quic-interop-runner 96 | pip3 install -r requirements.txt 97 | 98 | - name: Install dependencies 99 | run: | 100 | sudo modprobe ip6table_filter 101 | sudo add-apt-repository -y ppa:wireshark-dev/stable 102 | sudo apt install -y tshark 103 | 104 | - name: Run the interop tests 105 | run: | 106 | cd quic-interop-runner 107 | python3 run.py -s ${{ matrix.server }} -c ${{ matrix.client }} -t handshake,handshakeloss,handshakecorruption,retry,resumption,zerortt,amplificationlimit,http3,ipv6,chacha20,keyupdate,transfer,multiplexing,longrtt,blackhole,transferloss,transfercorruption,goodput,crosstraffic -d -r tquic=tquic_interop:v1 -l ${{ matrix.server }}-${{ matrix.client }}-logs -j ${{ matrix.server }}-${{ matrix.client }}-logs/interop.json 108 | 109 | - name: Dump the interop result 110 | if: ${{ always() }} 111 | run: | 112 | cd quic-interop-runner 113 | python3 -m json.tool ${{ matrix.server }}-${{ matrix.client }}-logs/interop.json 114 | 115 | - name: Store interop logs 116 | if: ${{ always() }} 117 | uses: actions/upload-artifact@v4 118 | with: 119 | name: ${{ matrix.server }}-${{ matrix.client }} 120 | path: | 121 | quic-interop-runner/*logs/* 122 | !quic-interop-runner/*logs/**/crosstraffic/ 123 | !quic-interop-runner/*logs/**/goodput/ 124 | 125 | result: 126 | runs-on: ubuntu-latest 127 | needs: tquic_interop_testing 128 | if: ${{ (( github.event_name == 'schedule' && github.repository == 'tencent/tquic' ) || github.event_name == 'workflow_dispatch') && !cancelled() }} 129 | steps: 130 | - name: Download all workflow run artifacts 131 | uses: actions/download-artifact@v4 132 | 133 | - name: Display structure of downloaded files 134 | run: ls -R 135 | 136 | - name: Display failed interop tests 137 | run: grep -Ho "{[^{]*failed" */*/*.json 138 | 139 | - name: Download plot tools 140 | uses: actions/checkout@v4 141 | with: 142 | path: tools 143 | 144 | - name: Install dependencies 145 | run: | 146 | sudo apt install python3-matplotlib 147 | 148 | - name: Plot all interop results 149 | run: python3 tools/.github/workflows/plot-interop.py ./ 150 | 151 | - name: Store all interop results 152 | uses: actions/upload-artifact@v4 153 | with: 154 | name: tquic-interop-all-result 155 | path: | 156 | interop*.png 157 | */*logs/* 158 | !*/*logs/**/**/sim/ 159 | -------------------------------------------------------------------------------- /.github/workflows/tquic-interop-main.yaml: -------------------------------------------------------------------------------- 1 | name: Interop 2 | 3 | on: 4 | push: 5 | branches: [ "develop" ] 6 | pull_request: 7 | branches: [ "develop" ] 8 | workflow_dispatch: 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | tquic_interop_testing: 15 | name: Interop testing 16 | runs-on: ubuntu-latest 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | include: 22 | - server: tquic 23 | client: tquic 24 | - server: tquic 25 | client: lsquic 26 | - server: lsquic 27 | client: tquic 28 | - server: tquic 29 | client: ngtcp2 30 | 31 | steps: 32 | - uses: actions/checkout@v4 33 | with: 34 | submodules: 'recursive' 35 | 36 | - name: Build docker image 37 | run: docker build -t tquic_interop:v1 -f interop/Dockerfile . 38 | 39 | - name: Install quic-interop-runner 40 | run: | 41 | git clone https://github.com/tquic-group/quic-interop-runner.git 42 | cd quic-interop-runner 43 | pip3 install -r requirements.txt 44 | 45 | - name: Install dependencies 46 | run: | 47 | sudo modprobe ip6table_filter 48 | sudo add-apt-repository -y ppa:wireshark-dev/stable 49 | sudo apt install -y tshark 50 | 51 | - name: Run the interop tests 52 | run: | 53 | cd quic-interop-runner 54 | python3 run.py -s ${{ matrix.server }} -c ${{ matrix.client }} -t handshake,handshakeloss,handshakecorruption,retry,resumption,zerortt,amplificationlimit,http3,ipv6,transfer,multiplexing,longrtt,blackhole,transferloss,transfercorruption,goodput,crosstraffic -d -r tquic=tquic_interop:v1 -l ${{ matrix.server }}-${{ matrix.client }}-logs -j ${{ matrix.server }}-${{ matrix.client }}-logs/interop.json 55 | 56 | - name: Dump the interop result 57 | if: ${{ always() }} 58 | run: | 59 | cd quic-interop-runner 60 | python3 -m json.tool ${{ matrix.server }}-${{ matrix.client }}-logs/interop.json 61 | 62 | - name: Store interop logs 63 | if: ${{ failure() }} 64 | uses: actions/upload-artifact@v4 65 | with: 66 | name: ${{ matrix.server }}-${{ matrix.client }} 67 | path: | 68 | quic-interop-runner/*logs/* 69 | !quic-interop-runner/*logs/**/crosstraffic/ 70 | !quic-interop-runner/*logs/**/goodput/ 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # MSVC Windows builds of rustc generate these, which store debugging information 13 | *.pdb 14 | 15 | # ignore VIM swap files 16 | *.swp 17 | *.swo 18 | 19 | # Misc 20 | .DS_Store 21 | 22 | dist 23 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/third_party/boringssl"] 2 | path = deps/boringssl 3 | url = https://github.com/google/boringssl.git 4 | ignore = dirty 5 | -------------------------------------------------------------------------------- /ADOPTERS.md: -------------------------------------------------------------------------------- 1 | Some known adopters of TQUIC are currently under confirmation to make their information public. 2 | 3 | If you have adopted TQUIC and would like to be included in this list, please feel free to submit a PR updating this file. 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [iyangsj@gmail.com]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT_CN.md: -------------------------------------------------------------------------------- 1 | 2 | # 贡献者公约 3 | 4 | ## 我们的承诺 5 | 6 | 身为社区成员、贡献者和领袖,我们承诺使社区参与者不受骚扰,无论其年龄、体型、可见或不可见的缺陷、族裔、性征、性别认同和表达、经验水平、教育程度、社会与经济地位、国籍、相貌、种族、种姓、肤色、宗教信仰、性倾向或性取向如何。 7 | 8 | 我们承诺以有助于建立开放、友善、多样化、包容、健康社区的方式行事和互动。 9 | 10 | ## 我们的准则 11 | 12 | 有助于为我们的社区创造积极环境的行为例子包括但不限于: 13 | 14 | * 表现出对他人的同情和善意 15 | * 尊重不同的主张、观点和感受 16 | * 提出和大方接受建设性意见 17 | * 承担责任并向受我们错误影响的人道歉 18 | * 注重社区共同诉求,而非个人得失 19 | 20 | 不当行为例子包括: 21 | 22 | * 使用情色化的语言或图像,及性引诱或挑逗 23 | * 嘲弄、侮辱或诋毁性评论,以及人身或政治攻击 24 | * 公开或私下的骚扰行为 25 | * 未经他人明确许可,公布他人的私人信息,如物理或电子邮件地址 26 | * 其他有理由认定为违反职业操守的不当行为 27 | 28 | ## 责任和权力 29 | 30 | 社区领袖有责任解释和落实我们所认可的行为准则,并妥善公正地对他们认为不当、威胁、冒犯或有害的任何行为采取纠正措施。 31 | 32 | 社区领导有权力和责任删除、编辑或拒绝或拒绝与本行为准则不相符的评论(comment)、提交(commits)、代码、维基(wiki)编辑、议题(issues)或其他贡献,并在适当时机知采取措施的理由。 33 | 34 | ## 适用范围 35 | 36 | 本行为准则适用于所有社区场合,也适用于在公共场所代表社区时的个人。 37 | 38 | 代表社区的情形包括使用官方电子邮件地址、通过官方社交媒体帐户发帖或在线上或线下活动中担任指定代表。 39 | 40 | ## 监督 41 | 42 | 辱骂、骚扰或其他不可接受的行为可通过 [iyangsj@gmail.com] 向负责监督的社区领袖报告。 43 | 所有投诉都将得到及时和公平的审查和调查。 44 | 45 | 所有社区领袖都有义务尊重任何事件报告者的隐私和安全。 46 | 47 | ## 处理方针 48 | 49 | 社区领袖将遵循下列社区处理方针来明确他们所认定违反本行为准则的行为的处理方式: 50 | 51 | ### 1. 纠正 52 | 53 | **社区影响**:使用不恰当的语言或其他在社区中被认定为不符合职业道德或不受欢迎的行为。 54 | 55 | **处理意见**:由社区领袖发出非公开的书面警告,明确说明违规行为的性质,并解释举止如何不妥。或将要求公开道歉。 56 | 57 | ### 2. 警告 58 | 59 | **社区影响**:单个或一系列违规行为。 60 | 61 | **处理意见**:警告并对连续性行为进行处理。在指定时间内,不得与相关人员互动,包括主动与行为准则执行者互动。这包括避免在社区场所和外部渠道中的互动。违反这些条款可能会导致临时或永久封禁。 62 | 63 | ### 3. 临时封禁 64 | 65 | **社区影响**: 严重违反社区准则,包括持续的不当行为。 66 | 67 | **处理意见**: 在指定时间内,暂时禁止与社区进行任何形式的互动或公开交流。在此期间,不得与相关人员进行公开或私下互动,包括主动与行为准则执行者互动。违反这些条款可能会导致永久封禁。 68 | 69 | ### 4. 永久封禁 70 | 71 | **社区影响**:行为模式表现出违反社区准则,包括持续的不当行为、骚扰个人或攻击或贬低某个类别的个体。 72 | 73 | **处理意见**:永久禁止在社区内进行任何形式的公开互动。 74 | 75 | ## 参见 76 | 77 | 本行为准则改编自 [Contributor Covenant][homepage] 2.1 版, 参见 [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]。 78 | 79 | 社区处理方针灵感来源于 [Mozilla's code of conduct enforcement ladder][Mozilla CoC]。 80 | 81 | 有关本行为准则的常见问题的答案,参见 [https://www.contributor-covenant.org/faq][FAQ]。 82 | 其他语言翻译参见 [https://www.contributor-covenant.org/translations][translations]。 83 | 84 | [homepage]: https://www.contributor-covenant.org 85 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 86 | [Mozilla CoC]: https://github.com/mozilla/diversity 87 | [FAQ]: https://www.contributor-covenant.org/faq 88 | [translations]: https://www.contributor-covenant.org/translations 89 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | You are welcome to contribute to TQUIC project. This following documents explains our workflow and work style. 4 | 5 | 6 | ## How to contribute codes 7 | 8 | - [English version](https://tquic.net/docs/how_to_contribute/contribute_codes) 9 | 10 | - [Chinese version](https://tquic.net/zh/docs/how_to_contribute/contribute_codes) 11 | 12 | 13 | ## How to contribute documents 14 | 15 | - [English version](https://tquic.net/docs/how_to_contribute/contribute_docs) 16 | 17 | - [Chinese version](https://tquic.net/zh/docs/how_to_contribute/contribute_docs) 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tquic" 3 | version = "1.6.0" 4 | edition = "2021" 5 | rust-version = "1.70.0" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/tencent/tquic" 8 | homepage = "https://tquic.net" 9 | description = "A high-performance, lightweight, and cross-platform QUIC library" 10 | keywords = ["quic"] 11 | categories = ["network-programming"] 12 | build = "src/build.rs" 13 | include = [ 14 | "/*.md", 15 | "/*.toml", 16 | "/LICENSE", 17 | "/src", 18 | "/benches", 19 | "/include", 20 | "/deps/boringssl/**/*.[chS]", 21 | "/deps/boringssl/**/*.asm", 22 | "/deps/boringssl/src/**/*.cc", 23 | "/deps/boringssl/**/CMakeLists.txt", 24 | "/deps/boringssl/**/sources.cmake", 25 | "/deps/boringssl/LICENSE", 26 | ] 27 | 28 | [package.metadata.docs.rs] 29 | no-default-features = true 30 | features = ["qlog", "h3"] 31 | 32 | [features] 33 | default = ["qlog", "h3"] 34 | 35 | # build the FFI API 36 | ffi = [] 37 | 38 | # enable support for the qlog 39 | qlog = ["dep:serde", "dep:serde_json", "dep:serde_derive", "dep:serde_with"] 40 | 41 | # enable support for h3 42 | h3 = ["dep:sfv"] 43 | 44 | [dependencies] 45 | bytes = "1" 46 | rustc-hash = "1.1" 47 | slab = "0.4" 48 | enumflags2 = "0.7.5" 49 | ring = "0.17" 50 | libc = "0.2" 51 | lazy_static = "1" 52 | log = { version = "0.4", features = ["std"] } 53 | strum = "0.24" 54 | strum_macros = "0.24" 55 | rand = "0.8.5" 56 | smallvec = { version = "1.10", features = ["serde", "union"] } 57 | lru = "0.12" 58 | serde = { version = "1.0.139", features = ["derive"], optional=true } 59 | serde_json = { version = "1.0", features = ["preserve_order"], optional=true } 60 | serde_derive = { version = "1.0", optional=true } 61 | serde_with = { version="3.0.0", optional=true } 62 | hex = "0.4" 63 | priority-queue = "1.3.2" 64 | sfv = { version = "0.9", optional=true } 65 | 66 | [target."cfg(windows)".dependencies] 67 | winapi = { version = "0.3", features = ["wincrypt", "ws2def", "ws2ipdef", "ws2tcpip"] } 68 | 69 | [dev-dependencies] 70 | env_logger = "0.10.0" 71 | mio = { version = "0.8", features = ["net", "os-poll"] } 72 | tempfile = "3.5.0" 73 | ctor = "0.2.2" 74 | criterion = "0.3" 75 | timer_heap = "0.3.0" 76 | 77 | [build-dependencies] 78 | cmake = "0.1" 79 | 80 | [lib] 81 | crate-type = ["lib", "staticlib", "cdylib"] 82 | 83 | [workspace] 84 | members = ["tools"] 85 | 86 | [[bench]] 87 | name = "timer_queue" 88 | harness = false 89 | -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 | # TQUIC 2 | 3 | [![License](https://img.shields.io/badge/license-Apache%202.0-green)](https://github.com/Tencent/tquic/blob/develop/LICENSE) 4 | [![Build Status](https://img.shields.io/github/actions/workflow/status/tencent/tquic/rust.yml)](https://github.com/Tencent/tquic/actions/workflows/rust.yml) 5 | [![codecov](https://codecov.io/gh/tencent/tquic/graph/badge.svg)](https://codecov.io/gh/tencent/tquic) 6 | [![docs.rs](https://docs.rs/tquic/badge.svg)](https://docs.rs/tquic) 7 | [![Website](https://img.shields.io/website?url=https%3A%2F%2Ftquic.net&up_message=tquic.net)](https://tquic.net) 8 | [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8010/badge)](https://www.bestpractices.dev/projects/8010) 9 | 10 | [English](README.md) | 中文 11 | 12 | TQUIC是IETF QUIC协议的实现。它是一个高性能、轻量级、跨平台的QUIC库。 13 | 14 | 15 | ## 特性及优点 16 | 17 | * **高性能**:TQUIC是为高性能和低延迟而设计的。相关细节可以参考[基准测试结果](https://tquic.net/zh/docs/further_readings/benchmark)。 18 | 19 | * **可插拔拥塞控制**:TQUIC支持多种拥塞控制算法,包括CUBIC,BBR,BBRv3,COPA。 20 | 21 | * **多路径传输**:TQUIC支持多路径,一个连接可同时使用多个路径提高性能及可靠性。 22 | 23 | * **易用性**:TQUIC提供易用的接口,支持灵活的配置参数,提供丰富的可观测性。 24 | 25 | * **跨平台**:TQUIC可运行在Rust语言支持的各种平台,同时提供了Rust/C/C++语言接口。 26 | 27 | * **基于Rust**:TQUIC基于用内存安全语言编写,可避免缓冲区溢出漏洞和其他内存相关错误的影响。 28 | 29 | * **高质量**:TQUIC包括充分的自动化测试,包括单元测试、模糊测试、集成测试、性能基准测试、互操作性测试等。 30 | 31 | * **协议一致性**:TQUIC已通过基于Ivy的形式化规范验证。同时通过了IETF互操作性测试。 32 | 33 | * **丰富的功能**:TQUIC 支持所有QUIC、HTTP/3规范中的重大功能。 34 | 35 | 36 | ## 开始使用 37 | - [编译及运行](https://tquic.net/zh/docs/getting_started/installation) 38 | 39 | 40 | ## 运行测试 41 | - 请参考[编译及运行](https://tquic.net/zh/docs/getting_started/installation) 42 | 43 | 44 | ## 文档 45 | 46 | - [英文版](https://tquic.net/docs/intro) 47 | - [中文版](https://tquic.net/zh/docs/intro) 48 | 49 | 50 | ## 参与贡献 51 | 52 | - 请首先在[issue列表](http://github.com/tencent/tquic/issues)中创建一个issue 53 | - 如有必要,请联系项目维护者/负责人进行进一步讨论 54 | - 详情请参阅[参与贡献指南](https://tquic.net/zh/docs/category/contributing/) 55 | 56 | 57 | ## 社区交流 58 | 59 | - [开源TQUIC用户论坛](https://github.com/tencent/tquic/discussions) 60 | - 开源TQUIC开发者微信群: [发送邮件](mailto:iyangsj@gmail.com)说明您的微信号及贡献(例如PR/Issue),我们将及时邀请您加入 61 | 62 | 63 | ## 许可 64 | 65 | TQUIC基于Apache 2.0许可证,详见[LICENSE](LICENSE)文件说明 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TQUIC 2 | 3 | [![License](https://img.shields.io/badge/license-Apache%202.0-green)](https://github.com/Tencent/tquic/blob/develop/LICENSE) 4 | [![Build Status](https://img.shields.io/github/actions/workflow/status/tencent/tquic/rust.yml)](https://github.com/Tencent/tquic/actions/workflows/rust.yml) 5 | [![codecov](https://codecov.io/gh/tencent/tquic/graph/badge.svg)](https://codecov.io/gh/tencent/tquic) 6 | [![docs.rs](https://docs.rs/tquic/badge.svg)](https://docs.rs/tquic) 7 | [![Website](https://img.shields.io/website?url=https%3A%2F%2Ftquic.net&up_message=tquic.net)](https://tquic.net) 8 | [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8010/badge)](https://www.bestpractices.dev/projects/8010) 9 | 10 | English | [中文](README-CN.md) 11 | 12 | TQUIC is a high-performance, lightweight, and cross-platform library for the [IETF QUIC](https://datatracker.ietf.org/wg/quic/about/) protocol. 13 | 14 | 15 | ## Advantages 16 | 17 | * **High performance**: TQUIC is designed for high performance and low latency. Relevant details can be found in the [benchmark result](https://tquic.net/docs/further_readings/benchmark). 18 | 19 | * **Pluggable congestion control**: TQUIC supports various congestion control algorithms, including CUBIC, BBR, BBRv3, and COPA. 20 | 21 | * **Multipath QUIC**: TQUIC supports Multipath to enable the simultaneous usage of multiple paths for a single connection. 22 | 23 | * **Easy to Use**: TQUIC is easy to use. It supports flexible settings and detailed observability. 24 | 25 | * **Cross platform**: TQUIC runs on almost anything to which Rust compiles. It provides APIs for Rust/C/C++. 26 | 27 | * **Powered by Rust**: TQUIC is written in a memory safety language and immune to Buffer Overflow vulnerability and other memory-related bugs. 28 | 29 | * **High quality**: Extensive automated testing, including unit testing, fuzz testing, integration testing, performance benchmarking, interoperability testing, and more. 30 | 31 | * **Protocol Compliance**: TQUIC has been verified by formal specification using the Ivy tool. It has also passed IETF interoperability tests. 32 | 33 | * **Rich features**: TQUIC supports all big features conforming with QUIC, HTTP/3 RFCs. 34 | 35 | 36 | ## Getting Started 37 | - [Build and run](https://tquic.net/docs/getting_started/installation) 38 | 39 | 40 | ## Running the tests 41 | - See [Build and run](https://tquic.net/docs/getting_started/installation) 42 | 43 | 44 | ## Documentation 45 | 46 | - [English version](https://tquic.net/docs/intro) 47 | - [Chinese version](https://tquic.net/zh/docs/intro) 48 | 49 | 50 | ## Contributing 51 | - Please create an issue in [issue list](http://github.com/tencent/tquic/issues). 52 | - Contact Committers/Owners for further discussion if needed. 53 | - See the [CONTRIBUTING](https://tquic.net/docs/category/contributing/) file for details. 54 | 55 | 56 | ## Communication 57 | 58 | - [TQUIC community on github](https://github.com/tencent/tquic/discussions) 59 | - TQUIC developer group on WeChat: [Send a request mail](mailto:iyangsj@gmail.com) with your WeChat ID and a contribution you've made to TQUIC(such as a PR/Issue). We will invite you right away. 60 | 61 | 62 | ## License 63 | 64 | TQUIC is under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details. 65 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | Please do not open issues for anything you think might have a security implication. 6 | 7 | Security issues and bugs should be reported privately to g_capd_ac_af@tencent.com. 8 | You should receive a response within 24 hours. If for some reason you do not, 9 | please follow up via email to ensure we received your original message. 10 | -------------------------------------------------------------------------------- /benches/timer_queue.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::time::Duration; 16 | use std::time::Instant; 17 | 18 | use criterion::criterion_group; 19 | use criterion::criterion_main; 20 | use criterion::Criterion; 21 | 22 | use timer_heap::TimerHeap; 23 | use timer_heap::TimerType; 24 | use tquic::timer_queue::TimerQueue; 25 | 26 | pub fn time_remaining_benchmark_with_same_timer(c: &mut Criterion) { 27 | let mut tq = TimerQueue::new(); 28 | const RANGE: u64 = 10_000; 29 | for i in 0..RANGE { 30 | tq.add(0, Duration::from_secs(i), Instant::now()); 31 | } 32 | c.bench_function("timer queue remaining", |b| { 33 | b.iter(|| tq.time_remaining(Instant::now())) 34 | }); 35 | 36 | let mut th = TimerHeap::new(); 37 | for i in 0..RANGE { 38 | th.upsert(0, Duration::from_secs(i), TimerType::Oneshot); 39 | } 40 | c.bench_function("timer heap remaining", |b| b.iter(|| th.time_remaining())); 41 | } 42 | 43 | pub fn time_remaining_benchmark_with_diff_timer(c: &mut Criterion) { 44 | let mut tq = TimerQueue::new(); 45 | const RANGE: u64 = 10_000; 46 | for i in 0..RANGE { 47 | tq.add(i, Duration::from_secs(i), Instant::now()); 48 | } 49 | c.bench_function("timer queue remaining", |b| { 50 | b.iter(|| tq.time_remaining(Instant::now())) 51 | }); 52 | 53 | let mut th = TimerHeap::new(); 54 | for i in 0..RANGE { 55 | th.upsert(i, Duration::from_secs(i), TimerType::Oneshot); 56 | } 57 | c.bench_function("timer heap remaining", |b| b.iter(|| th.time_remaining())); 58 | } 59 | 60 | pub fn time_expired_benchmark_with_same_timer(c: &mut Criterion) { 61 | let mut tq = TimerQueue::new(); 62 | const RANGE: u64 = 10_000; 63 | for _ in 0..RANGE { 64 | tq.add(0, Duration::from_secs(0), Instant::now()); 65 | } 66 | c.bench_function("timer queue expired", |b| { 67 | b.iter(|| tq.next_expire(Instant::now())) 68 | }); 69 | 70 | let mut th = TimerHeap::new(); 71 | for _ in 0..RANGE { 72 | th.upsert(0, Duration::from_secs(0), TimerType::Oneshot); 73 | } 74 | c.bench_function("timer heap expired", |b| b.iter(|| th.expired().next())); 75 | } 76 | 77 | pub fn time_expired_benchmark_with_diff_timer(c: &mut Criterion) { 78 | let mut tq = TimerQueue::new(); 79 | const RANGE: u64 = 10_000; 80 | for i in 0..RANGE { 81 | tq.add(i, Duration::from_secs(0), Instant::now()); 82 | } 83 | c.bench_function("timer queue expired", |b| { 84 | b.iter(|| tq.next_expire(Instant::now())) 85 | }); 86 | 87 | let mut th = TimerHeap::new(); 88 | for i in 0..RANGE { 89 | th.upsert(i, Duration::from_secs(0), TimerType::Oneshot); 90 | } 91 | c.bench_function("timer heap expired", |b| b.iter(|| th.expired().next())); 92 | } 93 | 94 | criterion_group!( 95 | benches, 96 | time_remaining_benchmark_with_same_timer, 97 | time_remaining_benchmark_with_diff_timer, 98 | time_expired_benchmark_with_same_timer, 99 | time_expired_benchmark_with_diff_timer, 100 | ); 101 | criterion_main!(benches); 102 | -------------------------------------------------------------------------------- /cbindgen.toml: -------------------------------------------------------------------------------- 1 | # The language to output bindings in 2 | language = "C" 3 | 4 | # An optional string of text to output between major sections of the generated 5 | # file as a warning against manual editing 6 | autogen_warning = "/* Don't modify this file manually. It is autogenerated by cbindgen. */" 7 | 8 | # An optional name to use as an include guard 9 | include_guard = "_TQUIC_H_" 10 | 11 | # Whether to make a C header C++ compatible. 12 | cpp_compat = true 13 | 14 | # Include doc comments from Rust as documentation 15 | documentation = true 16 | 17 | # A list of headers to #include (with quotes) 18 | includes = ["openssl/ssl.h", "tquic_def.h"] 19 | 20 | [export] 21 | exclude = ["MIN_CLIENT_INITIAL_LEN", "VINT_MAX"] 22 | 23 | [export.rename] 24 | "Config" = "quic_config_t" 25 | "TlsConfig" = "quic_tls_config_t" 26 | "SslCtx" = "SSL_CTX" 27 | "Connection" = "quic_conn_t" 28 | "ConnectionStats" = "quic_conn_stats_t" 29 | "Endpoint" = "quic_endpoint_t" 30 | "PacketOutSpec" = "quic_packet_out_spec_t" 31 | "PacketInfo" = "quic_packet_info_t" 32 | "PathAddress" = "quic_path_address_t" 33 | "PathStats" = "quic_path_stats_t" 34 | "FourTupleIter" = "quic_path_address_iter_t" 35 | "Shutdown" = "quic_shutdown" 36 | "TransportHandler" = "quic_transport_handler_t" 37 | "TransportMethods" = "quic_transport_methods_t" 38 | "TransportContext" = "quic_transport_context_t" 39 | "PacketSendHandler" = "quic_packet_send_handler_t" 40 | "PacketSendMethods" = "quic_packet_send_methods_t" 41 | "PacketSendContext" = "quic_packet_send_context_t" 42 | "TlsConfigSelectMethods" = "quic_tls_config_select_methods_t" 43 | "TlsConfigSelectorContext" = "quic_tls_config_select_context_t" 44 | "CongestionControlAlgorithm" = "quic_congestion_control_algorithm" 45 | "MultipathAlgorithm" = "quic_multipath_algorithm" 46 | "LevelFilter" = "quic_log_level" 47 | "Http3Connection" = "http3_conn_t" 48 | "Http3Config" = "http3_config_t" 49 | "Http3Context" = "http3_context_t" 50 | "Http3Handler" = "http3_handler_t" 51 | "Http3Headers" = "http3_headers_t" 52 | "Http3Methods" = "http3_methods_t" 53 | "Http3Priority" = "http3_priority_t" 54 | "Header" = "http3_header_t" 55 | "iovec" = "struct iovec" 56 | "sockaddr" = "struct sockaddr" 57 | "sockaddr_storage" = "struct sockaddr_storage" 58 | 59 | [enum] 60 | rename_variants = "QualifiedScreamingSnakeCase" 61 | 62 | [parse] 63 | parse_deps = true 64 | include = ["libc"] 65 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | artifacts 3 | coverage 4 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tquic-fuzz" 3 | version = "0.0.2" 4 | publish = false 5 | edition = "2021" 6 | 7 | [package.metadata] 8 | cargo-fuzz = true 9 | 10 | [dependencies] 11 | libfuzzer-sys = "0.4" 12 | lazy_static = "1.4.0" 13 | 14 | [dependencies.tquic] 15 | path = ".." 16 | 17 | # Prevent this from interfering with workspaces 18 | [workspace] 19 | members = ["."] 20 | 21 | [profile.release] 22 | debug = true 23 | debug-assertions = true 24 | overflow-checks = true 25 | 26 | [[bin]] 27 | name = "client_conn" 28 | path = "fuzz_targets/client_conn.rs" 29 | test = false 30 | doc = false 31 | 32 | [[bin]] 33 | name = "server_conn" 34 | path = "fuzz_targets/server_conn.rs" 35 | test = false 36 | doc = false 37 | -------------------------------------------------------------------------------- /fuzz/conf/cert.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDEjCCAfqgAwIBAgIJAIAdu56fLE7OMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV 3 | BAMMC2V4YW1wbGUub3JnMCAXDTIwMDYxMDEwMzM0NFoYDzIxMjAwNTE3MTAzMzQ0 4 | WjAWMRQwEgYDVQQDDAtleGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP 5 | ADCCAQoCggEBALv+LV1aWIlcK9rI7IuRS8SCusqBnoyJec/ErKiA2gbfgZ/YS73L 6 | Zud84yp45AIqauzcI5q+hrkmsRZ7CKqDzrG+jHavW7jF+0laetJwRt26AcQcOtQD 7 | 2ik2O+Dl1WHAFn4vUAQxb+Xz6WfSaQN0QfM74z06XUDDsr7g7+NYtMzhf98SJSoK 8 | ne/dVKJ3Bc6e6tvhnCRwPtix4ektEodK6WeNHYxwJ6wSZ8cRLzdxgjdD/4OGfFuj 9 | dn8zbOi3SQt5ZqVbcDHUTzp5t0G8EoxnzotHhhzjSAmsypySqZXaxl3oX8aYUkFn 10 | fCdg+WBXo5pOiNfoWh/D5bnIXWGp52yoy+kCAwEAAaNhMF8wHQYDVR0lBBYwFAYI 11 | KwYBBQUHAwIGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFIH+0G3eCswQHbN06kvI80M3 12 | tNH9MB0GA1UdDgQWBBSxLHQE7gOEyfeSNc5uIO/G/rgjpzANBgkqhkiG9w0BAQsF 13 | AAOCAQEAlGm5RwQ79xmLh3rj+5UViCSgsIuMcuhgIT4zogpo9S4uwXMqinrJhzRk 14 | Oc2tb3y06XTAq1lMH2+58tqndAu8ni/UBz3OSghk2CTnZ1vxxXOd3CtQu4ypMq+k 15 | qW0Umdrkk5TeAODNbrCy4c6vpICkQOljnRFWnDYu3aQ3JvaWZ/nObN7C72Lgpjfb 16 | RfLXGmLsBCEIr028f9hpoeRCXoetUY2CiC2boAHR+cO6Jpvex4Jv5yYDpNKac52n 17 | LLC8Cq5ozhOZNOSV6X9FpEca3rdhVUb0VgoNDCPZDdpO1PDJYCN9fUC8KUs+dsOh 18 | SYGpliBaNsztoiJs1q5SkMQrMwmMmg== 19 | -----END CERTIFICATE----- 20 | 21 | -------------------------------------------------------------------------------- /fuzz/conf/cert.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAu/4tXVpYiVwr2sjsi5FLxIK6yoGejIl5z8SsqIDaBt+Bn9hL 3 | vctm53zjKnjkAipq7Nwjmr6GuSaxFnsIqoPOsb6Mdq9buMX7SVp60nBG3boBxBw6 4 | 1APaKTY74OXVYcAWfi9QBDFv5fPpZ9JpA3RB8zvjPTpdQMOyvuDv41i0zOF/3xIl 5 | Kgqd791UoncFzp7q2+GcJHA+2LHh6S0Sh0rpZ40djHAnrBJnxxEvN3GCN0P/g4Z8 6 | W6N2fzNs6LdJC3lmpVtwMdRPOnm3QbwSjGfOi0eGHONICazKnJKpldrGXehfxphS 7 | QWd8J2D5YFejmk6I1+haH8PluchdYannbKjL6QIDAQABAoIBAEUBL7WsjAMfihls 8 | 1ycD1kPzmIzstz3u2H+jOZ1AbsdHE1WRF3w7RTKDbP8SEN+aolT/GTKb7OfZg/c0 9 | giHU7/Hed8C47XoNcgei5qKIA/svY6aQlidsoo+uEJykwIZ488itpTlkzCYkOfCa 10 | E2HpMqwNt4OqAMDdFKdr+aIB1Zu+KPBxW23WD9wEWAbe5LA4YnRF9kT4YZ6y9mce 11 | dGaIf39VtBlrGMmvoU0LE9B79nyuebGi0svW6QDarBqaDrnM/N3fXgL1kk/gVfan 12 | /xs6EA4qPxA5G4h+enYrIlZL0CbSj60nYElo+Z5nRdBaRdCF/bpXOLyK/kXWLUM0 13 | f2HTK+ECgYEA5cVcpJtczxEaxoaEUbppsW1LCrTjJDGTKJ63G7/lwqCxJeCHN185 14 | nnckHOW2287e19bu9aUmKJgRq5s1rXnT+MnCl/hQnfaMrKOKtzE+t7/zsc9+LuAr 15 | pwJrtZ9Dcnwrk8NOE0fPjW5XpDCSoEOo7JWZmGTVlpabOgNkjocfp+sCgYEA0XPt 16 | ZPt3F0wyzgLYRhgnvp5CV8SzQmulsW+ytnL5eiAcNSXqni3wQHN3PGxLInEyQwBQ 17 | /M8TQpUbqGMmahCK4ZxMAwXMrpF0mVB8jfoYMou1FSYPlUV+CvLjWcTkZB1Nirez 18 | VFXdtfHP0mx4PbYK2qjB03u4pPHAN8kuayIf2nsCgYAbv/FHZAgabfto3Jggcr4P 19 | Ep8MhPolxeL69egxbsSl89hRNcO+2T5ROBxhbRDfjSV2tduYSUDJiEwiCJW8BMmn 20 | 814QEopR+ZPVyc6X/1eOw5z/7YpUyPgcrHsrrTdtHTf6GY1VYMfdUeU9zCv5NRKy 21 | uAKb2Bm/nSLUJ9K+L+2PzwKBgQDRQgT3UtTUjehkMitpPFDY/LxDe92simfsMjBW 22 | X+Anx1TnNI6GolbZzYJe98LJElao4fQH38raRqZvQT/rz8MxTDoU+wJXljLryaHn 23 | Jupt9W5hRrli5R7cSXYjBbc43p3N7WJY68CqOoDrNjubS/jkJJ4hcAY1pOHp2jFq 24 | D5nLaQKBgAysU6O5kJ8yKxhbZflb42MqKCFBGrbRnbYx14PAEZOaRhzxehpppQmx 25 | RLbn/z1Uh5Ms28ipxA+vnhyM3FcU5lKboaFyWJeuNslw0FxEcIai6hL6UkDznS4G 26 | aqyzUjpG5Chg0x18xWYCbiGJwjZ9BWhtH+jojm856QHGzeQJWVoo 27 | -----END RSA PRIVATE KEY----- 28 | 29 | -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/06c7e51da071b51b0bd9f05c467766814d7840ff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/06c7e51da071b51b0bd9f05c467766814d7840ff -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/0c0115f3f2f2822441ccc9580598600025877957: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/0c0115f3f2f2822441ccc9580598600025877957 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/0c0d089bbc4a38d9c94b2c23bb7ba5ae7d259d95: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/0c0d089bbc4a38d9c94b2c23bb7ba5ae7d259d95 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/0dbba87ef4f636de7768c54b23e37075a66d0481: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/0dbba87ef4f636de7768c54b23e37075a66d0481 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/13b4139b69898ee60debc69261488eb3a8dc655f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/13b4139b69898ee60debc69261488eb3a8dc655f -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/159b1e8215cd92085c0343086871d160b10bf63f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/159b1e8215cd92085c0343086871d160b10bf63f -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/21db02d1f26881c7a25287ce5dd706cc67c9f1c6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/21db02d1f26881c7a25287ce5dd706cc67c9f1c6 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/27521b988366cfdeeb4465be1f8f7f35e1de653d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/27521b988366cfdeeb4465be1f8f7f35e1de653d -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/2c3e5db89134587979040c5f14bce40457cdda39: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/2c3e5db89134587979040c5f14bce40457cdda39 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/2dbb0e7c3a9b4f87efb23ad375e7ad3b84aec283: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/2dbb0e7c3a9b4f87efb23ad375e7ad3b84aec283 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/2e8d6f4b15ed4cd7518b285f609351b594adf48a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/2e8d6f4b15ed4cd7518b285f609351b594adf48a -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/3107abb7e994e78d5c8fd11143379755c75a1251: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/3107abb7e994e78d5c8fd11143379755c75a1251 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/320355ced694aa69924f6bb82e7b74f420303fd9: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/383e66fb4eea8b5831c1424d9ac2694d10c00b45: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/383e66fb4eea8b5831c1424d9ac2694d10c00b45 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/3b44037df6f169aeebe7dfba6325a25d4d06e89b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/3b44037df6f169aeebe7dfba6325a25d4d06e89b -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/3d87c9a28cef2784c95327423fd2ff8ec5ee5b8e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/3d87c9a28cef2784c95327423fd2ff8ec5ee5b8e -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/42f1412408e1048cde002d1d0b2d34a0f8128599: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/42f1412408e1048cde002d1d0b2d34a0f8128599 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/4b5270c3472b06ffdc3cbb22fc6d1b09e75d6eb9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/4b5270c3472b06ffdc3cbb22fc6d1b09e75d6eb9 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/50551d8c0efc6cc8802bc144a80b0bfdea2323b0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/50551d8c0efc6cc8802bc144a80b0bfdea2323b0 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/52ecf9828429e0e026af45e9baace5802dfb1a58: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/52ecf9828429e0e026af45e9baace5802dfb1a58 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/538f7f3abf0082d1ae2975384482d423db8f5ac2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/538f7f3abf0082d1ae2975384482d423db8f5ac2 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/558880ed1bb9c3cee71fab46adde8dab25a7d9ab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/558880ed1bb9c3cee71fab46adde8dab25a7d9ab -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/56d6b6519797cf028b002162b67a7b91dbbcf72c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/56d6b6519797cf028b002162b67a7b91dbbcf72c -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/59940caa4c4f6c325dcd0070efe150143e245ce3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/59940caa4c4f6c325dcd0070efe150143e245ce3 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/698d4af2ade4c0f4072f4eb9c8510b8068a9484f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/698d4af2ade4c0f4072f4eb9c8510b8068a9484f -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/6f3fcdf109818d22bda7b5f2840fc8dc1973fbd8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/6f3fcdf109818d22bda7b5f2840fc8dc1973fbd8 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/75d364c7d29deb69bea633c56888358c2d6feee2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/75d364c7d29deb69bea633c56888358c2d6feee2 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/78869c048e17acc249acd453dd0a5a63cf30d7c4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/78869c048e17acc249acd453dd0a5a63cf30d7c4 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/7c338ed2840d2bf55f9f5e4eed04f66c80840eb3: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/8b1b3fa0ba0c9a5ee75430a0ce38bfdc8ff9bcb8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/8b1b3fa0ba0c9a5ee75430a0ce38bfdc8ff9bcb8 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/8d883f1577ca8c334b7c6d75ccb71209d71ced13: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/8fcd5cb4a24ad84d54edd3ee673af39ff6ba39aa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/8fcd5cb4a24ad84d54edd3ee673af39ff6ba39aa -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/9cd3a828018a63403e31c7b8ce1290a30880f1d0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/9cd3a828018a63403e31c7b8ce1290a30880f1d0 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/a59973e52b215fc0e5832aeb1454c3dcfb2f1824: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/a59973e52b215fc0e5832aeb1454c3dcfb2f1824 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/a6e25fe7a05836fb1299f5c0424e7eaea37893c2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/a6e25fe7a05836fb1299f5c0424e7eaea37893c2 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/a9229d7b0d783054221257c7b3ef7a6bd8461d8f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/a9229d7b0d783054221257c7b3ef7a6bd8461d8f -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/b1db8fcc3a1b49c6accfe9f2e8460cfd53dedf2f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/b1db8fcc3a1b49c6accfe9f2e8460cfd53dedf2f -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/b41b5c840ad818c050ceff997e268a75cbc251ae: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/b41b5c840ad818c050ceff997e268a75cbc251ae -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/b4f99baa2d5f90f3235627ad00041e7061928bc8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/b4f99baa2d5f90f3235627ad00041e7061928bc8 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/b51ba23be45260059d9d136d97ba6bdd8067404c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/b51ba23be45260059d9d136d97ba6bdd8067404c -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/baefee12fa5ab8d50dc94b29bec7ae29c9ed1fa2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/baefee12fa5ab8d50dc94b29bec7ae29c9ed1fa2 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/bd274f83d6def3036774be2e8530cf711088b2ba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/bd274f83d6def3036774be2e8530cf711088b2ba -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/c121507916e1bc850cfa9741548a0d2c2e65c576: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/c121507916e1bc850cfa9741548a0d2c2e65c576 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/c44265b18c6bfd540afed08edff805a2a6352f11: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/c44265b18c6bfd540afed08edff805a2a6352f11 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/c4488af0c158e8c2832cb927cfb3ce534104cd1e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/c4488af0c158e8c2832cb927cfb3ce534104cd1e -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/c4ea21bb365bbeeaf5f2c654883e56d11e43c44e: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/c5c376a52033aa37edc5c058fb2f27880a930a33: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/c5c376a52033aa37edc5c058fb2f27880a930a33 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/c6296108bb04cc8431be6399a58c75ae93c2e31f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/c6296108bb04cc8431be6399a58c75ae93c2e31f -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/d5a1c62d3dda615c891a38c2774086bbdade08c2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/d5a1c62d3dda615c891a38c2774086bbdade08c2 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/e594b5cc4a21c43f4ad265ba100647a2a17adfcc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/e594b5cc4a21c43f4ad265ba100647a2a17adfcc -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/e727f9853e252169c167dfdcde6590ec31b9b979: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/e727f9853e252169c167dfdcde6590ec31b9b979 -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/ec371eb5a99f3f17e8b11a7d236d048e141f93ca: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/ec371eb5a99f3f17e8b11a7d236d048e141f93ca -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/efd973b00000df29da84cfa144fbc787a58c800a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/efd973b00000df29da84cfa144fbc787a58c800a -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/f5f6c1e300e30ea1d0c7b01b260898a775f8fe1a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/f5f6c1e300e30ea1d0c7b01b260898a775f8fe1a -------------------------------------------------------------------------------- /fuzz/corpus/client_conn/f6804467c8235fb6e92d19c3e15ccaacb4c8d978: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/client_conn/f6804467c8235fb6e92d19c3e15ccaacb4c8d978 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/0aae5fe5dd50c78303fa5f19d4e1bad3ca722127: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/0aae5fe5dd50c78303fa5f19d4e1bad3ca722127 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/0db97c523238b85abddbd148a826fe4be4f5be4f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/0db97c523238b85abddbd148a826fe4be4f5be4f -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/189ebf93be3966e53e508d694226af884595c91e: -------------------------------------------------------------------------------- 1 | � -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/235f358d52e6afcb7334bb034961a3818396d55a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/235f358d52e6afcb7334bb034961a3818396d55a -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/2436421711303dc563eb6a553a99f454c247a46f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/2436421711303dc563eb6a553a99f454c247a46f -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/25b2ccccb6178fab66b3b90646d18147f53656b4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/25b2ccccb6178fab66b3b90646d18147f53656b4 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/286ac037c78f26d0fd6cc951a5b4cc9a7b1e1ab8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/286ac037c78f26d0fd6cc951a5b4cc9a7b1e1ab8 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/2c05f87e9ff39c6c2b89037072ba7e0c5fd2bed2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/2c05f87e9ff39c6c2b89037072ba7e0c5fd2bed2 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/2ed787b25adae9312140e3f6d5160698d4496e81: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/2ed787b25adae9312140e3f6d5160698d4496e81 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/30305e2cfa75d8b8faa1ea3307672300b59f72bd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/30305e2cfa75d8b8faa1ea3307672300b59f72bd -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/3032c6afcf48ee056a8694e5f82e0a33428e411e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/3032c6afcf48ee056a8694e5f82e0a33428e411e -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/3088280ece3e8bdbf06026fb2c5d3c467719c8bf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/3088280ece3e8bdbf06026fb2c5d3c467719c8bf -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/35f596229085838104079f4eae97171221cc58f9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/35f596229085838104079f4eae97171221cc58f9 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/37bf5e8a0fffd5843f1b6f9a62db451d18a99326: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/37bf5e8a0fffd5843f1b6f9a62db451d18a99326 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/39092634e7e5ce7f0fda5515d8b24534fb6231f7: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/39092634e7e5ce7f0fda5515d8b24534fb6231f7 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/3c5502403e8913e1078c07aa5cf629255c6bad9a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/3c5502403e8913e1078c07aa5cf629255c6bad9a -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/3d065fe052576fd3b071c1f005ad97f4783be460: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/3d065fe052576fd3b071c1f005ad97f4783be460 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/43840b579b9ec553a87f100e415fcc88459be79c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/43840b579b9ec553a87f100e415fcc88459be79c -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/447d244508c689e8be77c40b98f5d8f76f68deb9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/447d244508c689e8be77c40b98f5d8f76f68deb9 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/4534b4102502b3c52e0706afd270da266cf2ce1e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/4534b4102502b3c52e0706afd270da266cf2ce1e -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/4bbe1db89d7f007b334f6096609aad9bc4ab873f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/4bbe1db89d7f007b334f6096609aad9bc4ab873f -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/520ac5451fcc3d6780170c312c75da34bd923a54: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/520ac5451fcc3d6780170c312c75da34bd923a54 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/57737e5a20e4adc7a2592ce326e7bcbdf56ca033: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/57737e5a20e4adc7a2592ce326e7bcbdf56ca033 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/5b177e6336f955a4f30ccab6146fbc8d67e087f2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/5b177e6336f955a4f30ccab6146fbc8d67e087f2 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/61a54ef870dda545eadc0e264f91c6f08dc3fc29: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/61a54ef870dda545eadc0e264f91c6f08dc3fc29 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/68466d46121341d45797a5b425d86bfd9021e881: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/68466d46121341d45797a5b425d86bfd9021e881 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/6eacfd79fd62de7f5b0d0d26f644941ac112941b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/6eacfd79fd62de7f5b0d0d26f644941ac112941b -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/79534b64d01967c9b30895dc272b651913e81b2d: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/79534b64d01967c9b30895dc272b651913e81b2d -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/7c176de6d502d7584d2f44b99083694f2c2a7e82: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/7c176de6d502d7584d2f44b99083694f2c2a7e82 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/7c4d33785daa5c2370201ffa236b427aa37c9996: -------------------------------------------------------------------------------- 1 | & -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/7efe94e87547f33eb9c28df1a0d97900d144396e: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/7efe94e87547f33eb9c28df1a0d97900d144396e -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/814710547121f82d710c0a9785ebfe33e220250c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/814710547121f82d710c0a9785ebfe33e220250c -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/8503132b75717cb65921651c678f8fd896a5e812: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/8503132b75717cb65921651c678f8fd896a5e812 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/8bf403b9d53b00fbe0b58397405c87f45316f588: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/8bf403b9d53b00fbe0b58397405c87f45316f588 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/8f799ae02983c136ae268e1fc4ab87e3a671d0c6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/8f799ae02983c136ae268e1fc4ab87e3a671d0c6 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/94a66a555fe7ffb6109b57fe18b4f5b74612728b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/94a66a555fe7ffb6109b57fe18b4f5b74612728b -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/9a328460aaccbfe4736174b5bf928f04ec544755: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/9a328460aaccbfe4736174b5bf928f04ec544755 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/9a3f49c6608954ca470e14126868be14a4bbe1bf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/9a3f49c6608954ca470e14126868be14a4bbe1bf -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/a91c84f158d1cf2e9e7f095be94b4347ad8dc234: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/a91c84f158d1cf2e9e7f095be94b4347ad8dc234 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4: -------------------------------------------------------------------------------- 1 | 5 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/b07fb17867a2dceff5c0da8cb11db54309712d30: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/b07fb17867a2dceff5c0da8cb11db54309712d30 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/b230c817815cf64a53b53577b158e0044bc67c91: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/b230c817815cf64a53b53577b158e0044bc67c91 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/b68542373c05c0ed25231d09955b2c699d37c45b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/b68542373c05c0ed25231d09955b2c699d37c45b -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/b8cd821899f42e9036271f730be064ae124fc6b6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/b8cd821899f42e9036271f730be064ae124fc6b6 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/c2586e32c4c2badbc19cebac3e4f919cfc4f55b4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/c2586e32c4c2badbc19cebac3e4f919cfc4f55b4 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/c36c0706fda85a4852fc3d9fe1d9be0aa22e88c9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/c36c0706fda85a4852fc3d9fe1d9be0aa22e88c9 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/c4e037b3827601c42ebde93c4a80372eb80bc52f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/c4e037b3827601c42ebde93c4a80372eb80bc52f -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/cac7c88b76341fc980146dce9f7cc1ddf8208ea9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/cac7c88b76341fc980146dce9f7cc1ddf8208ea9 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/cb4c0995126a188ab6ce182b04921b22358409f6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/cb4c0995126a188ab6ce182b04921b22358409f6 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/d0f971251d6b6cb1fcb3a27bf208df4ebf46c715: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/d0f971251d6b6cb1fcb3a27bf208df4ebf46c715 -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/f0173820ad43f6a413eee70c69a82dca39904a0f: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/f0173820ad43f6a413eee70c69a82dca39904a0f -------------------------------------------------------------------------------- /fuzz/corpus/server_conn/fb4b1879ee1cbc70aefa25646d18f6b0b4fdafcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tencent/tquic/2a57740960a8c25e10e4c4625331e75468e566b3/fuzz/corpus/server_conn/fb4b1879ee1cbc70aefa25646d18f6b0b4fdafcf -------------------------------------------------------------------------------- /fuzz/fuzz_targets/client_conn.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_main] 16 | 17 | use std::net::SocketAddr; 18 | use std::sync::Mutex; 19 | use std::time::Instant; 20 | 21 | use lazy_static::lazy_static; 22 | use libfuzzer_sys::fuzz_target; 23 | 24 | use tquic::Config; 25 | use tquic::ConnectionId; 26 | use tquic::TlsConfig; 27 | 28 | lazy_static! { 29 | static ref CONFIG: Mutex = { 30 | let mut conf = Config::new().unwrap(); 31 | let tls_conf = TlsConfig::new_client_config(vec![b"h3".to_vec()], false).unwrap(); 32 | conf.set_tls_config(tls_conf); 33 | Mutex::new(conf) 34 | }; 35 | } 36 | 37 | fuzz_target!(|data: &[u8]| { 38 | let mut buf = data.to_vec(); 39 | let local: SocketAddr = "127.0.0.1:9999".parse().unwrap(); 40 | let remote: SocketAddr = "127.0.0.1:443".parse().unwrap(); 41 | let info = tquic::PacketInfo { 42 | src: remote, 43 | dst: local, 44 | time: Instant::now(), 45 | }; 46 | 47 | let mut conn = tquic::Connection::new_client( 48 | &ConnectionId::random(), 49 | local, 50 | remote, 51 | None, 52 | &mut CONFIG.lock().unwrap(), 53 | ) 54 | .unwrap(); 55 | conn.recv(&mut buf, &info).ok(); 56 | }); 57 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/server_conn.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![no_main] 16 | 17 | use std::net::SocketAddr; 18 | use std::sync::Mutex; 19 | use std::time::Instant; 20 | 21 | use lazy_static::lazy_static; 22 | use libfuzzer_sys::fuzz_target; 23 | 24 | use tquic::Config; 25 | use tquic::ConnectionId; 26 | use tquic::TlsConfig; 27 | 28 | lazy_static! { 29 | static ref CONFIG: Mutex = { 30 | let mut conf = Config::new().unwrap(); 31 | let crt_file = "fuzz/conf/cert.crt"; 32 | let key_file = "fuzz/conf/cert.key"; 33 | let protos = vec![b"h3".to_vec()]; 34 | let tls_conf = TlsConfig::new_server_config(crt_file, key_file, protos, false).unwrap(); 35 | conf.set_tls_config(tls_conf); 36 | Mutex::new(conf) 37 | }; 38 | } 39 | 40 | fuzz_target!(|data: &[u8]| { 41 | let mut buf = data.to_vec(); 42 | let local: SocketAddr = "127.0.0.1:9999".parse().unwrap(); 43 | let remote: SocketAddr = "127.0.0.1:443".parse().unwrap(); 44 | let info = tquic::PacketInfo { 45 | src: remote, 46 | dst: local, 47 | time: Instant::now(), 48 | }; 49 | 50 | let mut conn = tquic::Connection::new_server( 51 | &ConnectionId::random(), 52 | local, 53 | remote, 54 | None, 55 | &mut CONFIG.lock().unwrap(), 56 | ) 57 | .unwrap(); 58 | conn.recv(&mut buf, &info).ok(); 59 | }); 60 | -------------------------------------------------------------------------------- /include/tquic_def.h: -------------------------------------------------------------------------------- 1 | #ifndef _TQUIC_DEF_H_ 2 | #define _TQUIC_DEF_H_ 3 | 4 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) 5 | #include 6 | #include 7 | typedef SSIZE_T ssize_t; 8 | struct iovec { 9 | void *iov_base; // starting address 10 | size_t iov_len; // number of bytes to transfer 11 | }; 12 | #else 13 | #include 14 | #include 15 | #endif 16 | 17 | typedef enum http3_error { 18 | HTTP3_NO_ERROR = 0, 19 | 20 | // There is no error or no work to do 21 | HTTP3_ERR_DONE = -1, 22 | 23 | // The endpoint detected an error in the protocol 24 | HTTP3_ERR_GENERAL_PROTOCOL_ERROR = -2, 25 | 26 | // The endpoint encountered an internal error and cannot continue with the 27 | // connection 28 | HTTP3_ERR_INTERNAL_ERROR = -3, 29 | 30 | // The endpoint detected that its peer created a stream that it will not 31 | // accept 32 | HTTP3_ERR_STREAM_CREATION_ERROR = -4, 33 | 34 | // A stream required by the connection was closed or reset 35 | HTTP3_ERR_CLOSED_CRITICAL_STREAM = -5, 36 | 37 | // A frame was received which is not permitted in the current state or on 38 | // the current stream 39 | HTTP3_ERR_FRAME_UNEXPECTED = -6, 40 | 41 | // A frame that fails to satisfy layout requirements or with an invalid 42 | // size was received 43 | HTTP3_ERR_FRAME_ERROR = -7, 44 | 45 | // The endpoint detected that its peer is exhibiting a behavior that might 46 | // be generating excessive load 47 | HTTP3_ERR_EXCESSIVE_LOAD = -8, 48 | 49 | // A stream ID or push ID was used incorrectly, such as exceeding a limit, 50 | // reducing a limit, or being reused 51 | HTTP3_ERR_ID_ERROR = -9, 52 | 53 | // An endpoint detected an error in the payload of a SETTINGS frame 54 | HTTP3_ERR_SETTINGS_ERROR = -10, 55 | 56 | // No SETTINGS frame was received at the beginning of the control stream 57 | HTTP3_ERR_MISSING_SETTINGS = -11, 58 | 59 | // -12 reserved 60 | 61 | // The stream is blocked 62 | HTTP3_ERR_STREAM_BLOCKED = -13, 63 | 64 | // The server rejected the request without performing any application 65 | // processing 66 | HTTP3_ERR_REQUEST_REJECTED = -14, 67 | 68 | // The request or its response (including pushed response) is cancelled 69 | HTTP3_ERR_REQUEST_CANCELLED = -15, 70 | 71 | // The client's stream terminated without containing a fully-formed request 72 | HTTP3_ERR_REQUEST_INCOMPLETE = -16, 73 | 74 | // An HTTP message was malformed and cannot be processed 75 | HTTP3_ERR_MESSAGE_ERROR = -17, 76 | 77 | // The TCP connection established in response to a CONNECT request was 78 | // reset or abnormally closed 79 | HTTP3_ERR_CONNECT_ERROR = -18, 80 | 81 | // The requested operation cannot be served over HTTP/3. The peer should 82 | // retry over HTTP/1.1 83 | HTTP3_ERR_VERSION_FALLBACK = -19, 84 | 85 | // The decoder failed to interpret an encoded field section and is not 86 | // able to continue decoding that field section 87 | HTTP3_ERR_QPACK_DECOMPRESSION_FAILED = -20, 88 | 89 | // The decoder failed to interpret an encoder instruction received on the 90 | // encoder stream 91 | HTTP3_ERR_QPACK_ENCODER_STREAM_ERROR = -21, 92 | 93 | // The encoder failed to interpret a decoder instruction received on the 94 | // decoder stream 95 | HTTP3_ERR_QPACK_DECODER_STREAM_ERROR = -22, 96 | } http3_error; 97 | 98 | #endif /* _TQUIC_DEF_H_ */ 99 | -------------------------------------------------------------------------------- /interop/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 The TQUIC Authors. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # QUIC docker image for interoperability testing. 16 | # See: https://github.com/marten-seemann/quic-interop-runner 17 | 18 | FROM rust:1.70 as build 19 | 20 | WORKDIR /build 21 | 22 | COPY . ./ 23 | 24 | RUN apt-get update && apt-get install -y cmake && rm -rf /var/lib/apt/lists/* 25 | 26 | RUN cargo build --release --manifest-path tools/Cargo.toml 27 | 28 | 29 | FROM martenseemann/quic-network-simulator-endpoint:latest as tquic-interop 30 | 31 | WORKDIR /tquic 32 | 33 | COPY --from=build \ 34 | /build/target/release/tquic_client \ 35 | /build/target/release/tquic_server \ 36 | /build/interop/run_endpoint.sh \ 37 | ./ 38 | 39 | RUN chmod u+x ./run_endpoint.sh 40 | 41 | ENTRYPOINT [ "./run_endpoint.sh" ] 42 | -------------------------------------------------------------------------------- /interop/README.md: -------------------------------------------------------------------------------- 1 | # Running the interop runner for TQUIC on local host 2 | 3 | ## Build the docker image for TQUIC 4 | 5 | ``` 6 | docker build -t tquic_interop:v1 -f interop/Dockerfile . 7 | ``` 8 | 9 | 10 | ## Running the Interop Runner 11 | 12 | * Requirements 13 | See: https://github.com/marten-seemann/quic-interop-runner#requirements 14 | 15 | 16 | * Running a test case 17 | 18 | ``` 19 | # Run test case http3 20 | python3 run.py -s tquic -c tquic -t http3 -d -r tquic=tquic_interop:v1 21 | 22 | # Show usage 23 | python3 run.py -h 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /interop/run_endpoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2023 The TQUIC Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -e 18 | set -x 19 | 20 | # Set up the routing needed for the simulation. 21 | /setup.sh 22 | 23 | case "$TESTCASE" in 24 | handshake|http3|multiconnect|resumption|retry|transfer|zerortt) 25 | ;; 26 | chacha20|keyupdate) 27 | if [ "$ROLE" == "client" ]; then 28 | exit 127 29 | fi 30 | ;; 31 | ecn|v2) 32 | exit 127 33 | ;; 34 | *) 35 | exit 127 36 | ;; 37 | esac 38 | 39 | TQUIC_DIR="/tquic" 40 | TQUIC_CLIENT="tquic_client" 41 | TQUIC_SERVER="tquic_server" 42 | ROOT_DIR="/www" 43 | DOWNLOAD_DIR="/downloads" 44 | LOG_DIR="/logs" 45 | QLOG_DIR="/logs/qlog" 46 | 47 | CC_ALGOR="CUBIC" 48 | case ${CONGESTION^^} in 49 | BBR) 50 | CC_ALGOR="BBR" 51 | ;; 52 | BBR3) 53 | CC_ALGOR="BBR3" 54 | ;; 55 | COPA) 56 | CC_ALGOR="COPA" 57 | ;; 58 | DUMMY) 59 | CC_ALGOR="DUMMY" 60 | ;; 61 | *) 62 | ;; 63 | esac 64 | 65 | # Note: You can add extra command-line options to tquic_client/tquic_sever by 66 | # using the `EXTRA_ARGS` environment variable. 67 | COMMON_ARGS="--keylog-file $SSLKEYLOGFILE --log-level DEBUG --log-file $LOG_DIR/$ROLE.log --idle-timeout 30000 --handshake-timeout 30000 --initial-rtt 100 --congestion-control-algor $CC_ALGOR $EXTRA_ARGS" 68 | 69 | if [ "$TESTCASE" != "transfer" ]; then 70 | COMMON_ARGS="$COMMON_ARGS --qlog-dir $QLOG_DIR" 71 | fi 72 | 73 | if [ "$ROLE" == "client" ]; then 74 | # Wait for the simulator to start up. 75 | /wait-for-it.sh sim:57832 -s -t 30 76 | 77 | REQS=($REQUESTS) 78 | 79 | CLIENT_ARGS="$COMMON_ARGS --dump-dir ${DOWNLOAD_DIR} --max-concurrent-requests ${#REQS[@]}" 80 | CLIENT_ALPN="--alpn hq-interop" 81 | case $TESTCASE in 82 | resumption) 83 | CLIENT_ARGS="$CLIENT_ARGS --session-file=session.bin" 84 | ;; 85 | zerortt) 86 | CLIENT_ARGS="$CLIENT_ARGS --session-file=session.bin --enable-early-data" 87 | ;; 88 | http3) 89 | CLIENT_ALPN="--alpn h3" 90 | ;; 91 | *) 92 | ;; 93 | esac 94 | CLIENT_ARGS="$CLIENT_ARGS $CLIENT_ALPN" 95 | 96 | case $TESTCASE in 97 | multiconnect|resumption) 98 | for REQ in $REQUESTS 99 | do 100 | $TQUIC_DIR/$TQUIC_CLIENT $CLIENT_ARGS $REQ 101 | done 102 | ;; 103 | zerortt) 104 | $TQUIC_DIR/$TQUIC_CLIENT $CLIENT_ARGS ${REQS[0]} 105 | $TQUIC_DIR/$TQUIC_CLIENT $CLIENT_ARGS ${REQS[@]:1} 106 | ;; 107 | *) 108 | $TQUIC_DIR/$TQUIC_CLIENT $CLIENT_ARGS $REQUESTS 109 | ;; 110 | esac 111 | elif [ "$ROLE" == "server" ]; then 112 | SERVER_ARGS="$COMMON_ARGS -c /certs/cert.pem -k /certs/priv.key --listen [::]:443 --root $ROOT_DIR" 113 | case $TESTCASE in 114 | retry) 115 | SERVER_ARGS="$SERVER_ARGS --enable-retry" 116 | ;; 117 | *) 118 | ;; 119 | esac 120 | $TQUIC_DIR/$TQUIC_SERVER $SERVER_ARGS 121 | fi 122 | -------------------------------------------------------------------------------- /src/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Additional parameters for Android 16 | const CMAKE_PARAMS_ANDROID_NDK: &[(&str, &[(&str, &str)])] = &[ 17 | ("aarch64", &[("ANDROID_ABI", "arm64-v8a")]), 18 | ("arm", &[("ANDROID_ABI", "armeabi-v7a")]), 19 | ("x86", &[("ANDROID_ABI", "x86")]), 20 | ("x86_64", &[("ANDROID_ABI", "x86_64")]), 21 | ]; 22 | 23 | /// Additional parameters for iOS 24 | const CMAKE_PARAMS_IOS: &[(&str, &[(&str, &str)])] = &[ 25 | ( 26 | "aarch64-apple-ios", 27 | &[ 28 | ("CMAKE_OSX_ARCHITECTURES", "arm64"), 29 | ("CMAKE_OSX_SYSROOT", "iphoneos"), 30 | ("CMAKE_ASM_FLAGS", "-fembed-bitcode -target arm64-apple-ios"), 31 | ], 32 | ), 33 | ( 34 | "aarch64-apple-ios-sim", 35 | &[ 36 | ("CMAKE_OSX_ARCHITECTURES", "arm64"), 37 | ("CMAKE_OSX_SYSROOT", "iphonesimulator"), 38 | ( 39 | "CMAKE_ASM_FLAGS", 40 | "-fembed-bitcode -target arm64-apple-ios-simulator", 41 | ), 42 | ("CMAKE_THREAD_LIBS_INIT", "-lpthread"), 43 | ("CMAKE_HAVE_THREADS_LIBRARY", "1"), 44 | ("THREADS_PREFER_PTHREAD_FLAG", "ON"), 45 | ], 46 | ), 47 | ( 48 | "x86_64-apple-ios", 49 | &[ 50 | ("CMAKE_OSX_ARCHITECTURES", "x86_64"), 51 | ("CMAKE_OSX_SYSROOT", "iphonesimulator"), 52 | ( 53 | "CMAKE_ASM_FLAGS", 54 | "-fembed-bitcode -target x86_64-apple-ios-simulator", 55 | ), 56 | ], 57 | ), 58 | ]; 59 | 60 | /// Additional parameters for Ohos 61 | const CMAKE_PARAMS_OHOS_NDK: &[(&str, &[(&str, &str)])] = &[ 62 | ("aarch64", &[("OHOS_ARCH", "arm64-v8a")]), 63 | ("arm", &[("OHOS_ARCH", "armeabi-v7a")]), 64 | ("x86_64", &[("OHOS_ARCH", "x86_64")]), 65 | ]; 66 | 67 | /// Create a cmake::Config for building BoringSSL. 68 | fn new_boringssl_cmake_config() -> cmake::Config { 69 | let target = std::env::var("TARGET").unwrap(); 70 | let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap(); 71 | let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); 72 | 73 | let mut boringssl_cmake = cmake::Config::new("deps/boringssl"); 74 | 75 | match os.as_ref() { 76 | "android" => { 77 | for (android_arch, params) in CMAKE_PARAMS_ANDROID_NDK { 78 | if *android_arch == arch { 79 | for (name, value) in *params { 80 | boringssl_cmake.define(name, value); 81 | } 82 | break; 83 | } 84 | } 85 | 86 | let android_ndk_home = std::env::var("ANDROID_NDK_HOME") 87 | .expect("Please set ANDROID_NDK_HOME for Android build"); 88 | let android_ndk_home = std::path::Path::new(&android_ndk_home); 89 | let toolchain_file = android_ndk_home.join("build/cmake/android.toolchain.cmake"); 90 | let toolchain_file = toolchain_file.to_str().unwrap(); 91 | boringssl_cmake.define("CMAKE_TOOLCHAIN_FILE", toolchain_file); 92 | 93 | boringssl_cmake.define("ANDROID_NATIVE_API_LEVEL", "21"); 94 | boringssl_cmake.define("ANDROID_STL", "c++_shared"); 95 | } 96 | 97 | "ios" => { 98 | for (ios_target, params) in CMAKE_PARAMS_IOS { 99 | if *ios_target == target { 100 | for (name, value) in *params { 101 | boringssl_cmake.define(name, value); 102 | if *name == "CMAKE_ASM_FLAGS" { 103 | boringssl_cmake.cflag(value); 104 | } 105 | } 106 | break; 107 | } 108 | } 109 | } 110 | 111 | "linux" => { 112 | if target.ends_with("ohos") { 113 | for (ohos_arch, params) in CMAKE_PARAMS_OHOS_NDK { 114 | if *ohos_arch == arch { 115 | for (name, value) in *params { 116 | boringssl_cmake.define(name, value); 117 | } 118 | break; 119 | } 120 | } 121 | 122 | let ohos_ndk_home = std::env::var("OHOS_NDK_HOME") 123 | .expect("Please set OHOS_NDK_HOME for Harmony build"); 124 | let ohos_ndk_home = std::path::Path::new(&ohos_ndk_home); 125 | let toolchain_file = ohos_ndk_home.join("native/build/cmake/ohos.toolchain.cmake"); 126 | let toolchain_file = toolchain_file.to_str().unwrap(); 127 | boringssl_cmake.define("CMAKE_TOOLCHAIN_FILE", toolchain_file); 128 | // common arguments for ohos help us to ignore some error 129 | boringssl_cmake.define("CMAKE_C_FLAGS", "-Wno-unused-command-line-argument"); 130 | boringssl_cmake.define("CMAKE_CXX_FLAGS", "-Wno-unused-command-line-argument"); 131 | } 132 | } 133 | 134 | _ => (), 135 | }; 136 | 137 | boringssl_cmake 138 | } 139 | 140 | /// Return the build sub-dir for boringssl. 141 | fn get_boringssl_build_sub_dir() -> &'static str { 142 | if !cfg!(target_env = "msvc") { 143 | return ""; 144 | } 145 | 146 | // Note: MSVC outputs static libs in a sub-directory. 147 | let debug = std::env::var("DEBUG").expect("DEBUG not set"); 148 | let opt_level = std::env::var("OPT_LEVEL").expect("OPT_LEVEL not set"); 149 | 150 | match &opt_level[..] { 151 | "1" | "2" | "3" => { 152 | if &debug[..] == "true" { 153 | "RelWithDebInfo" 154 | } else { 155 | "Release" 156 | } 157 | } 158 | "s" | "z" => "MinSizeRel", 159 | _ => "Debug", 160 | } 161 | } 162 | 163 | fn main() { 164 | if let Ok(boringssl_lib_dir) = std::env::var("BORINGSSL_LIB_DIR") { 165 | // Build with static boringssl lib. 166 | // Boringssl lib should turn on CMAKE_POSITION_INDEPENDENT_CODE 167 | // option when compile to build dynamic tquic lib. 168 | println!("cargo:rustc-link-search=native={boringssl_lib_dir}"); 169 | } else { 170 | // Build with boringssl code. 171 | let boringssl_dir = { 172 | let mut cfg = new_boringssl_cmake_config(); 173 | 174 | cfg.build_target("ssl").build(); 175 | cfg.build_target("crypto").build().display().to_string() 176 | }; 177 | let sub_dir = get_boringssl_build_sub_dir(); 178 | let build_dir = format!("{boringssl_dir}/build/{sub_dir}"); 179 | println!("cargo:rustc-link-search=native={build_dir}"); 180 | } 181 | 182 | println!("cargo:rustc-link-lib=static=ssl"); 183 | println!("cargo:rustc-link-lib=static=crypto"); 184 | } 185 | -------------------------------------------------------------------------------- /src/congestion_control/dummy.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![allow(unused_variables)] 16 | 17 | use std::time::Instant; 18 | 19 | use super::CongestionController; 20 | use super::CongestionStats; 21 | use crate::connection::rtt::RttEstimator; 22 | use crate::connection::space::SentPacket; 23 | use crate::RecoveryConfig; 24 | 25 | /// Dummpy Configuration. 26 | #[derive(Debug)] 27 | pub struct DummyConfig { 28 | /// Initial congestion window in bytes. 29 | initial_congestion_window: u64, 30 | } 31 | 32 | impl DummyConfig { 33 | pub fn from(conf: &RecoveryConfig) -> Self { 34 | let max_datagram_size = conf.max_datagram_size as u64; 35 | let initial_congestion_window = conf 36 | .initial_congestion_window 37 | .saturating_mul(max_datagram_size); 38 | 39 | Self { 40 | initial_congestion_window, 41 | } 42 | } 43 | } 44 | 45 | impl Default for DummyConfig { 46 | fn default() -> Self { 47 | Self { 48 | initial_congestion_window: 10 * crate::DEFAULT_SEND_UDP_PAYLOAD_SIZE as u64, 49 | } 50 | } 51 | } 52 | 53 | /// Dummy is a simple congestion controller with a static congestion window. 54 | /// It is intended to be used for testing and experiments. 55 | #[derive(Debug)] 56 | pub struct Dummy { 57 | /// Congestion window in bytes. 58 | cwnd: u64, 59 | 60 | /// Congestion statistics. 61 | stats: CongestionStats, 62 | } 63 | 64 | impl Dummy { 65 | pub fn new(conf: DummyConfig) -> Self { 66 | Self { 67 | cwnd: conf.initial_congestion_window, 68 | stats: Default::default(), 69 | } 70 | } 71 | } 72 | 73 | impl CongestionController for Dummy { 74 | fn name(&self) -> &str { 75 | "DUMMY" 76 | } 77 | 78 | fn on_sent(&mut self, now: Instant, packet: &mut SentPacket, bytes_in_flight: u64) { 79 | let sent_bytes = packet.sent_size as u64; 80 | self.stats.bytes_in_flight = bytes_in_flight; 81 | self.stats.bytes_sent_in_total = self.stats.bytes_sent_in_total.saturating_add(sent_bytes); 82 | } 83 | 84 | fn begin_ack(&mut self, now: Instant, bytes_in_flight: u64) { 85 | // Do nothing. 86 | } 87 | 88 | fn on_ack( 89 | &mut self, 90 | packet: &mut SentPacket, 91 | now: Instant, 92 | app_limited: bool, 93 | rtt: &RttEstimator, 94 | bytes_in_flight: u64, 95 | ) { 96 | let acked_bytes = packet.sent_size as u64; 97 | self.stats.bytes_in_flight = bytes_in_flight; 98 | self.stats.bytes_acked_in_total = 99 | self.stats.bytes_acked_in_total.saturating_add(acked_bytes); 100 | } 101 | 102 | fn end_ack(&mut self) { 103 | // Do nothing. 104 | } 105 | 106 | fn on_congestion_event( 107 | &mut self, 108 | now: Instant, 109 | packet: &SentPacket, 110 | is_persistent_congestion: bool, 111 | lost_bytes: u64, 112 | bytes_in_flight: u64, 113 | ) { 114 | self.stats.bytes_lost_in_total = self.stats.bytes_lost_in_total.saturating_add(lost_bytes); 115 | self.stats.bytes_in_flight = bytes_in_flight; 116 | } 117 | 118 | fn in_slow_start(&self) -> bool { 119 | false 120 | } 121 | 122 | fn in_recovery(&self, sent_time: Instant) -> bool { 123 | false 124 | } 125 | 126 | fn congestion_window(&self) -> u64 { 127 | self.cwnd 128 | } 129 | 130 | fn initial_window(&self) -> u64 { 131 | self.cwnd 132 | } 133 | 134 | fn minimal_window(&self) -> u64 { 135 | self.cwnd 136 | } 137 | 138 | fn stats(&self) -> &CongestionStats { 139 | &self.stats 140 | } 141 | 142 | fn pacing_rate(&self) -> Option { 143 | None 144 | } 145 | } 146 | 147 | #[cfg(test)] 148 | mod tests { 149 | use super::*; 150 | use std::time::Duration; 151 | 152 | #[test] 153 | fn dummy_init() { 154 | let conf = RecoveryConfig { 155 | initial_congestion_window: 10, 156 | max_datagram_size: 1200, 157 | ..RecoveryConfig::default() 158 | }; 159 | let d = Dummy::new(DummyConfig::from(&conf)); 160 | 161 | assert_eq!(d.name(), "DUMMY"); 162 | assert_eq!(d.congestion_window(), 1200 * 10); 163 | assert_eq!(d.initial_window(), 1200 * 10); 164 | assert_eq!(d.minimal_window(), 1200 * 10); 165 | 166 | assert_eq!(d.in_slow_start(), false); 167 | assert_eq!(d.in_recovery(Instant::now()), false); 168 | assert_eq!(d.stats().bytes_in_flight, 0); 169 | assert_eq!(d.pacing_rate(), None); 170 | } 171 | 172 | #[test] 173 | fn dummy_stats() { 174 | let conf = RecoveryConfig { 175 | initial_congestion_window: 10, 176 | max_datagram_size: 1200, 177 | ..RecoveryConfig::default() 178 | }; 179 | let mut d = Dummy::new(DummyConfig::from(&conf)); 180 | 181 | let rtt = Duration::from_millis(100); 182 | let rtt_estimator = RttEstimator::new(rtt); 183 | let now = Instant::now(); 184 | 185 | // Sent and acked a packet 186 | let mut pkt = SentPacket { 187 | pkt_num: 0, 188 | ack_eliciting: true, 189 | in_flight: true, 190 | sent_size: 1200, 191 | ..SentPacket::default() 192 | }; 193 | d.on_sent(now, &mut pkt, 1200); 194 | assert_eq!(d.stats().bytes_in_flight, 1200); 195 | assert_eq!(d.stats().bytes_sent_in_total, 1200); 196 | 197 | let now = now + rtt; 198 | d.begin_ack(now, 1200); 199 | d.on_ack(&mut pkt, now, true, &rtt_estimator, 0); 200 | d.end_ack(); 201 | assert_eq!(d.stats().bytes_in_flight, 0); 202 | assert_eq!(d.stats().bytes_acked_in_total, 1200); 203 | 204 | // Sent and lost a packet 205 | let mut pkt = SentPacket { 206 | pkt_num: 0, 207 | ack_eliciting: true, 208 | in_flight: true, 209 | sent_size: 1400, 210 | ..SentPacket::default() 211 | }; 212 | d.on_sent(now, &mut pkt, 1400); 213 | assert_eq!(d.stats().bytes_in_flight, 1400); 214 | assert_eq!(d.stats().bytes_sent_in_total, 2600); 215 | 216 | d.on_congestion_event(now, &pkt, false, 1400, 0); 217 | assert_eq!(d.stats().bytes_in_flight, 0); 218 | assert_eq!(d.stats().bytes_lost_in_total, 1400); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/connection/timer.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::time::Instant; 16 | 17 | use strum::EnumCount; 18 | use strum_macros::EnumCount; 19 | use strum_macros::EnumIter; 20 | 21 | #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, EnumIter, EnumCount)] 22 | pub(crate) enum Timer { 23 | /// When to declare unacked packets lost or send ack-eliciting probe packets 24 | LossDetection, 25 | 26 | /// When to send acknowledgements 27 | Ack, 28 | 29 | /// When the pacer will allow us to send a packet 30 | Pacer, 31 | 32 | /// When to close the connection after no activity 33 | Idle, 34 | 35 | /// When to determine the handshake is failed if it is not completed in time. 36 | Handshake, 37 | 38 | /// When the timer expires, the connection has been gracefully terminated. 39 | Draining, 40 | 41 | /// When keys are discarded because they should not be needed anymore 42 | KeyDiscard, 43 | 44 | /// When to send a `PING` frame to keep the connection alive 45 | KeepAlive, 46 | 47 | /// When to declare PATH_CHALLENGE probing packet lost 48 | PathChallenge, 49 | } 50 | 51 | /// Associated timeout values with each `Timer` 52 | #[derive(Debug, Copy, Clone, Default)] 53 | pub(crate) struct TimerTable { 54 | expires: [Option; Timer::COUNT], 55 | } 56 | 57 | impl TimerTable { 58 | /// Set expiration time for the giver timer 59 | pub fn set(&mut self, timer: Timer, time: Instant) { 60 | self.expires[timer as usize] = Some(time); 61 | } 62 | 63 | /// Get expiration time for the giver timer 64 | pub fn get(&self, timer: Timer) -> Option { 65 | self.expires[timer as usize] 66 | } 67 | 68 | /// Cancel the giver timer 69 | pub fn stop(&mut self, timer: Timer) { 70 | self.expires[timer as usize] = None; 71 | } 72 | 73 | /// Get the minmium expiration time of all timers 74 | pub fn next_timeout(&self) -> Option { 75 | self.expires.iter().filter_map(|&x| x).min() 76 | } 77 | 78 | /// Check whether the given timer is expired 79 | pub fn is_expired(&self, timer: Timer, after: Instant) -> bool { 80 | self.expires[timer as usize].is_some_and(|x| x <= after) 81 | } 82 | } 83 | 84 | #[cfg(test)] 85 | mod tests { 86 | use super::*; 87 | use std::ops::Add; 88 | use std::time::Duration; 89 | use std::time::Instant; 90 | 91 | #[test] 92 | fn timer_operation() { 93 | let mut timers = TimerTable::default(); 94 | assert_eq!(timers.next_timeout(), None); 95 | 96 | // Set timers 97 | let now = Instant::now(); 98 | let loss_time = now.add(Duration::from_millis(200)); 99 | let idle_time = now.add(Duration::from_millis(3000)); 100 | timers.set(Timer::LossDetection, loss_time); 101 | timers.set(Timer::Idle, idle_time); 102 | 103 | assert_eq!(timers.get(Timer::LossDetection), Some(loss_time)); 104 | assert_eq!(timers.get(Timer::Idle), Some(idle_time)); 105 | assert_eq!(timers.get(Timer::Draining), None); 106 | assert_eq!(timers.get(Timer::KeyDiscard), None); 107 | assert_eq!(timers.next_timeout(), Some(loss_time)); 108 | 109 | // Stop timer 110 | timers.stop(Timer::LossDetection); 111 | assert_eq!(timers.get(Timer::LossDetection), None); 112 | assert_eq!(timers.get(Timer::Idle), Some(idle_time)); 113 | assert_eq!(timers.next_timeout(), Some(idle_time)); 114 | } 115 | 116 | #[test] 117 | fn timer_expiration() { 118 | let mut timers = TimerTable::default(); 119 | let now = Instant::now(); 120 | let loss_time = now.add(Duration::from_millis(200)); 121 | let idle_time = now.add(Duration::from_millis(3000)); 122 | timers.set(Timer::LossDetection, loss_time); 123 | timers.set(Timer::Idle, idle_time); 124 | 125 | assert_eq!(timers.is_expired(Timer::LossDetection, now), false); 126 | assert_eq!(timers.is_expired(Timer::Idle, now), false); 127 | 128 | // Advance ticks 129 | let now = loss_time; 130 | assert_eq!(timers.is_expired(Timer::LossDetection, now), true); 131 | assert_eq!(timers.is_expired(Timer::Idle, now), false); 132 | 133 | // Advance ticks 134 | let now = idle_time; 135 | assert_eq!(timers.is_expired(Timer::LossDetection, now), true); 136 | assert_eq!(timers.is_expired(Timer::Idle, now), true); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/h3/h3.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Implementation of HTTP/3 and QPACK. 16 | 17 | use std::fmt; 18 | use std::fmt::Write; 19 | 20 | /// Result type for HTTP/3 operations. 21 | pub type Result = std::result::Result; 22 | 23 | /// An HTTP/3 configuration. 24 | #[derive(Default)] 25 | pub struct Http3Config { 26 | /// A limit on the maximum size of the message header an endpoint will 27 | /// accept on an individual HTTP message. 28 | max_field_section_size: Option, 29 | 30 | /// The decoder limits the maximum value the encoder is permitted to set 31 | /// for the dynamic table capacity. 32 | qpack_max_table_capacity: Option, 33 | 34 | /// The decoder specifies an upper bound on the number of streams that 35 | /// can be blocked using the SETTINGS_QPACK_BLOCKED_STREAMS setting. 36 | qpack_blocked_streams: Option, 37 | } 38 | 39 | impl Http3Config { 40 | /// Create default HTTP/3 configuration. 41 | pub const fn new() -> Result { 42 | Ok(Http3Config { 43 | max_field_section_size: None, 44 | qpack_max_table_capacity: None, 45 | qpack_blocked_streams: None, 46 | }) 47 | } 48 | 49 | /// Set the `SETTINGS_MAX_FIELD_SECTION_SIZE` setting. 50 | /// The default value is unlimited. 51 | pub fn set_max_field_section_size(&mut self, v: u64) { 52 | self.max_field_section_size = Some(v); 53 | } 54 | 55 | /// Set the `SETTINGS_QPACK_MAX_TABLE_CAPACITY` setting. 56 | /// The default value is `0`. 57 | pub fn set_qpack_max_table_capacity(&mut self, v: u64) { 58 | self.qpack_max_table_capacity = Some(v); 59 | } 60 | 61 | /// Set the `SETTINGS_QPACK_BLOCKED_STREAMS` setting. 62 | /// The default value is `0`. 63 | pub fn set_qpack_blocked_streams(&mut self, v: u64) { 64 | self.qpack_blocked_streams = Some(v); 65 | } 66 | } 67 | 68 | /// An HTTP/3 connection event. 69 | #[derive(Clone, Debug, PartialEq, Eq)] 70 | pub enum Http3Event { 71 | /// HTTP/3 headers were received on request stream. 72 | Headers { 73 | /// HTTP/3 header fields are represented as a list of name-value pairs. 74 | /// Note that the application is responsible for validating the headers. 75 | headers: Vec
, 76 | 77 | /// Whether the stream consists of only headers and no data. 78 | fin: bool, 79 | }, 80 | 81 | /// Data was received on a request or push stream. 82 | /// 83 | /// Note that `Data` event was edge-triggered, so the application must try to 84 | /// read all data from the stream until `Done` value is returned. 85 | Data, 86 | 87 | /// Stream's read side is finished. 88 | /// 89 | /// Note that the stream's write side may still be open. 90 | Finished, 91 | 92 | /// RESET_STREAM was received from the peer. 93 | /// 94 | /// The error code received from the peer is provided as a associated data. 95 | Reset(u64), 96 | 97 | /// GOAWAY was received from the peer. 98 | GoAway, 99 | 100 | /// PRIORITY_UPDATE was received from the peer. 101 | /// 102 | /// Note that `PriorityUpdate` event was edge-triggered, it will not be triggered 103 | /// again until the last PRIORITY_UPDATE has been read. 104 | PriorityUpdate, 105 | } 106 | 107 | /// An HTTP/3 header list. 108 | pub struct Http3Headers<'a> { 109 | pub(crate) headers: &'a Vec
, 110 | } 111 | 112 | /// A trait for types with name and value. 113 | pub trait NameValue { 114 | /// Return the name. 115 | fn name(&self) -> &[u8]; 116 | 117 | /// Return the value. 118 | fn value(&self) -> &[u8]; 119 | } 120 | 121 | impl NameValue for (&[u8], &[u8]) { 122 | fn name(&self) -> &[u8] { 123 | self.0 124 | } 125 | 126 | fn value(&self) -> &[u8] { 127 | self.1 128 | } 129 | } 130 | 131 | /// A raw HTTP header with owned name-value pair. 132 | #[derive(Clone, PartialEq, Eq)] 133 | pub struct Header(Vec, Vec); 134 | 135 | impl Header { 136 | pub fn new(name: &[u8], value: &[u8]) -> Self { 137 | Self(name.to_vec(), value.to_vec()) 138 | } 139 | } 140 | 141 | impl NameValue for Header { 142 | fn name(&self) -> &[u8] { 143 | &self.0 144 | } 145 | 146 | fn value(&self) -> &[u8] { 147 | &self.1 148 | } 149 | } 150 | 151 | impl fmt::Debug for Header { 152 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 153 | f.write_char('"')?; 154 | fmt_readable(&self.0, f)?; 155 | f.write_str(": ")?; 156 | fmt_readable(&self.1, f)?; 157 | f.write_char('"') 158 | } 159 | } 160 | 161 | fn fmt_readable(hdr: &[u8], f: &mut fmt::Formatter) -> fmt::Result { 162 | match std::str::from_utf8(hdr) { 163 | Ok(s) => f.write_str(&s.escape_default().to_string()), 164 | Err(_) => write!(f, "{hdr:?}"), 165 | } 166 | } 167 | 168 | /// A raw HTTP header with non-owned name-value pair. 169 | #[derive(Clone, Debug, PartialEq, Eq)] 170 | pub struct HeaderRef<'a>(&'a [u8], &'a [u8]); 171 | 172 | impl<'a> HeaderRef<'a> { 173 | pub const fn new(name: &'a [u8], value: &'a [u8]) -> Self { 174 | Self(name, value) 175 | } 176 | } 177 | 178 | impl NameValue for HeaderRef<'_> { 179 | fn name(&self) -> &[u8] { 180 | self.0 181 | } 182 | 183 | fn value(&self) -> &[u8] { 184 | self.1 185 | } 186 | } 187 | 188 | /// The Http3Handler lists the callbacks used by the Http3Connection to 189 | /// communicate with the user application code. 190 | pub trait Http3Handler { 191 | /// Called when the stream got headers. 192 | fn on_stream_headers(&self, stream_id: u64, event: &mut Http3Event); 193 | 194 | /// Called when the stream has buffered data to read. 195 | fn on_stream_data(&self, stream_id: u64); 196 | 197 | /// Called when the stream is finished. 198 | fn on_stream_finished(&self, stream_id: u64); 199 | 200 | /// Called when the stream receives a RESET_STREAM frame from the peer. 201 | fn on_stream_reset(&self, stream_id: u64, error_code: u64); 202 | 203 | /// Called when the stream priority is updated. 204 | fn on_stream_priority_update(&self, stream_id: u64); 205 | 206 | /// Called when the connection receives a GOAWAY frame from the peer. 207 | fn on_conn_goaway(&self, stream_id: u64); 208 | } 209 | 210 | #[cfg(test)] 211 | mod tests { 212 | use super::*; 213 | 214 | #[test] 215 | fn fmt_readable() { 216 | let h = Header::new(b"proto", b"h3"); 217 | assert_eq!(format!("{:?}", h), "\"proto: h3\""); 218 | 219 | let h = Header::new(b"proto", &vec![0x97, 0x61, 0x6c]); 220 | assert_eq!(format!("{:?}", h), "\"proto: [151, 97, 108]\""); 221 | } 222 | 223 | #[test] 224 | fn header_ref() { 225 | let name = b"x-powered-by"; 226 | let value = b"tquic"; 227 | let h = HeaderRef::new(name, value); 228 | assert_eq!(h.name(), name); 229 | assert_eq!(h.value(), value); 230 | } 231 | } 232 | 233 | pub use error::Http3Error; 234 | 235 | #[path = "qpack/qpack.rs"] 236 | mod qpack; 237 | 238 | pub mod connection; 239 | mod error; 240 | mod frame; 241 | mod stream; 242 | -------------------------------------------------------------------------------- /src/h3/qpack/prefix_int.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::codec::Decoder; 16 | use crate::codec::Encoder; 17 | use crate::h3::Http3Error; 18 | use crate::h3::Result; 19 | 20 | /// Encode an integer using QPACK integer representation. 21 | /// 22 | /// An integer is represented in two parts: a prefix that fills the current 23 | /// octet and an optional list of octets that are used if the integer value 24 | /// does not fit within the prefix. The number of bits of the prefix (called N) 25 | /// is a parameter of the integer representation. 26 | /// See RFC 7541 Section 5.1 27 | /// 28 | /// The `i` is the integer to be encoded. 29 | /// The `n` is the number of bits of the prefix. 30 | /// The most significant bits of the `first` carrys the placeholder bits. Its 31 | /// number of bits is `8-n`. 32 | pub fn encode_int(mut i: u64, first: u8, n: usize, mut buf: &mut [u8]) -> Result { 33 | let buf_len = buf.len(); 34 | 35 | // If the integer value is small enough, i.e., strictly less than 2^N-1, 36 | // it is encoded within the N-bit prefix. 37 | let mask = 2u64.pow(n as u32) - 1; 38 | if i < mask { 39 | buf.write_u8(first | i as u8)?; 40 | return Ok(buf_len - buf.len()); 41 | } 42 | 43 | // Otherwise, all the bits of the prefix are set to 1, and the value, 44 | // decreased by 2^N-1, is encoded using a list of one or more octets. 45 | buf.write_u8(first | mask as u8)?; 46 | i -= mask; 47 | while i >= 128 { 48 | // The most significant bit of each octet is used as a continuation 49 | // flag: its value is set to 1 except for the last octet in the list. 50 | buf.write_u8((i % 128 + 128) as u8)?; 51 | i >>= 7; 52 | } 53 | buf.write_u8(i as u8)?; 54 | 55 | Ok(buf_len - buf.len()) 56 | } 57 | 58 | /// Decode an integer using Qpack integer representation. 59 | /// 60 | /// The `n` is the number of bits of the prefix. 61 | pub fn decode_int(mut buf: &[u8], n: usize) -> Result<(u64, usize)> { 62 | let buf_len = buf.len(); 63 | 64 | let mask = 2u64.pow(n as u32) - 1; 65 | let mut val = u64::from(buf.read_u8()?); 66 | val &= mask; 67 | if val < mask { 68 | return Ok((val, 1)); 69 | } 70 | 71 | let mut shift = 0; 72 | while !buf.is_empty() { 73 | let byte = buf.read_u8()?; 74 | let inc = u64::from(byte & 0x7f) 75 | .checked_shl(shift) 76 | .ok_or(Http3Error::QpackDecompressionFailed)?; 77 | val = val 78 | .checked_add(inc) 79 | .ok_or(Http3Error::QpackDecompressionFailed)?; 80 | shift += 7; 81 | 82 | if byte & 0x80 == 0 { 83 | return Ok((val, buf_len - buf.len())); 84 | } 85 | } 86 | 87 | Err(Http3Error::QpackDecompressionFailed) 88 | } 89 | 90 | #[cfg(test)] 91 | mod test { 92 | use super::*; 93 | 94 | /// Unit test for RFC 7541 Appendix C.1 95 | #[test] 96 | fn prefix_int_normal() { 97 | let cases = [ 98 | (10, 5, vec![0b01010]), 99 | (1337, 5, vec![0b11111, 0b10011010, 0b00001010]), 100 | (42, 8, vec![0b101010]), 101 | ]; 102 | 103 | for c in cases { 104 | let (value, prefix, mut buf) = c; 105 | let buf_len = buf.len(); 106 | assert_eq!(decode_int(&mut buf, prefix), Ok((value, buf_len))); 107 | 108 | let mut out = [0; 4]; 109 | let enc_len = encode_int(value, 0, prefix, &mut out).unwrap(); 110 | assert_eq!(&buf, &out[..enc_len]); 111 | } 112 | } 113 | 114 | #[test] 115 | fn decode_int_without_end_flag() { 116 | let mut buf = vec![0b11111, 0b10011010, 0b10001010]; 117 | assert!(decode_int(&mut buf, 5).is_err()); 118 | } 119 | 120 | #[test] 121 | fn decode_int_empty_buf() { 122 | let mut buf = vec![]; 123 | assert!(decode_int(&mut buf, 5).is_err()); 124 | } 125 | 126 | #[test] 127 | fn decode_int_too_big() { 128 | let mut buf = vec![ 129 | 0b11111, 0b10011010, 0b10001010, 0b10001010, 0b10001010, 0b10001010, 0b10001010, 130 | 0b10001010, 0b10001010, 131 | ]; 132 | assert!(decode_int(&mut buf, 5).is_err()); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/multipath_scheduler/multipath_scheduler.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #![allow(unused_variables)] 16 | 17 | use core::str::FromStr; 18 | use std::time::Instant; 19 | 20 | use self::scheduler_minrtt::*; 21 | use self::scheduler_redundant::*; 22 | use self::scheduler_rr::*; 23 | use crate::connection::path::PathMap; 24 | use crate::connection::space::PacketNumSpaceMap; 25 | use crate::connection::space::SentPacket; 26 | use crate::connection::stream::StreamMap; 27 | use crate::Error; 28 | use crate::MultipathConfig; 29 | use crate::PathEvent; 30 | use crate::Result; 31 | 32 | /// MultipathScheduler is a packet scheduler that decides the path over which 33 | /// the next QUIC packet will be sent. 34 | /// Note: The API of MultipathScheduler is not stable and may change in future 35 | /// versions. 36 | pub(crate) trait MultipathScheduler { 37 | /// Select a validated path with sufficient congestion window for sending 38 | /// non-probing packets. 39 | fn on_select( 40 | &mut self, 41 | paths: &mut PathMap, 42 | spaces: &mut PacketNumSpaceMap, 43 | streams: &mut StreamMap, 44 | ) -> Result; 45 | 46 | /// Process a packet sent event. 47 | fn on_sent( 48 | &mut self, 49 | packet: &SentPacket, 50 | now: Instant, 51 | path_id: usize, 52 | paths: &mut PathMap, 53 | spaces: &mut PacketNumSpaceMap, 54 | streams: &mut StreamMap, 55 | ) { 56 | } 57 | 58 | /// Process a path event. 59 | fn on_path_updated(&mut self, paths: &mut PathMap, event: PathEvent) {} 60 | } 61 | 62 | /// Available multipath scheduling algorithms. 63 | #[repr(C)] 64 | #[derive(Debug, Clone, Copy, PartialEq)] 65 | pub enum MultipathAlgorithm { 66 | /// The scheduler sends packets over the path with the lowest smoothed RTT 67 | /// among all available paths. It aims to optimize throughput and achieve 68 | /// load balancing, making it particularly advantageous for bulk transfer 69 | /// applications in heterogeneous networks. 70 | MinRtt, 71 | 72 | /// The scheduler sends all packets redundantly on all available paths. It 73 | /// utilizes additional bandwidth to minimize latency, thereby reducing the 74 | /// overall flow completion time for applications with bounded bandwidth 75 | /// requirements that can be met by a single path. 76 | /// In scenarios where two paths with varying available bandwidths are 77 | /// present, it ensures a goodput at least equivalent to the best single 78 | /// path. 79 | Redundant, 80 | 81 | /// The scheduler sends packets over available paths in a round robin 82 | /// manner. It aims to fully utilize the capacity of each path as the 83 | /// distribution across all path is equal. It is only used for testing 84 | /// purposes. 85 | RoundRobin, 86 | } 87 | 88 | impl FromStr for MultipathAlgorithm { 89 | type Err = Error; 90 | 91 | fn from_str(algor: &str) -> Result { 92 | if algor.eq_ignore_ascii_case("minrtt") { 93 | Ok(MultipathAlgorithm::MinRtt) 94 | } else if algor.eq_ignore_ascii_case("redundant") { 95 | Ok(MultipathAlgorithm::Redundant) 96 | } else if algor.eq_ignore_ascii_case("roundrobin") { 97 | Ok(MultipathAlgorithm::RoundRobin) 98 | } else { 99 | Err(Error::InvalidConfig("unknown".into())) 100 | } 101 | } 102 | } 103 | 104 | /// Build a multipath scheduler 105 | pub(crate) fn build_multipath_scheduler(conf: &MultipathConfig) -> Box { 106 | match conf.multipath_algorithm { 107 | MultipathAlgorithm::MinRtt => Box::new(MinRttScheduler::new(conf)), 108 | MultipathAlgorithm::Redundant => Box::new(RedundantScheduler::new(conf)), 109 | MultipathAlgorithm::RoundRobin => Box::new(RoundRobinScheduler::new(conf)), 110 | } 111 | } 112 | 113 | pub(crate) fn buffer_required(algor: MultipathAlgorithm) -> bool { 114 | match algor { 115 | MultipathAlgorithm::MinRtt => false, 116 | MultipathAlgorithm::Redundant => true, 117 | MultipathAlgorithm::RoundRobin => false, 118 | } 119 | } 120 | 121 | #[cfg(test)] 122 | pub(crate) mod tests { 123 | use super::*; 124 | use crate::connection::stream; 125 | use crate::Config; 126 | use crate::Path; 127 | use crate::TransportParams; 128 | use std::time::Duration; 129 | 130 | pub(crate) struct MultipathTester { 131 | pub(crate) paths: PathMap, 132 | pub(crate) spaces: PacketNumSpaceMap, 133 | pub(crate) streams: StreamMap, 134 | } 135 | 136 | impl MultipathTester { 137 | /// Create context for multipath scheduler. 138 | pub(crate) fn new() -> Result { 139 | let path = new_test_path("127.0.0.1:443", "127.0.0.1:8443", true, 200); 140 | let mut paths = PathMap::new(path, 8, crate::ANTI_AMPLIFICATION_FACTOR, true); 141 | paths.enable_multipath(); 142 | 143 | let spaces = PacketNumSpaceMap::new(); 144 | 145 | let params = stream::StreamTransportParams::from(&TransportParams::default()); 146 | let streams = StreamMap::new(true, 1024 * 1024, 1024 * 1024, params); 147 | 148 | Ok(MultipathTester { 149 | paths, 150 | spaces, 151 | streams, 152 | }) 153 | } 154 | 155 | /// Add a test path. 156 | pub(crate) fn add_path( 157 | &mut self, 158 | local: &str, 159 | remote: &str, 160 | initial_rtt: u64, 161 | ) -> Result { 162 | let mut path = new_test_path(local, remote, false, initial_rtt); 163 | path.set_active(true); 164 | path.dcid_seq = Some(self.paths.len() as u64); 165 | self.paths.insert_path(path) 166 | } 167 | 168 | /// Mark the given path as active or inactive. 169 | pub(crate) fn set_path_active(&mut self, path_id: usize, active: bool) -> Result<()> { 170 | let path = self.paths.get_mut(path_id)?; 171 | path.set_active(active); 172 | Ok(()) 173 | } 174 | } 175 | 176 | fn new_test_path(local: &str, remote: &str, is_initial: bool, initial_rtt: u64) -> Path { 177 | let local = local.parse().unwrap(); 178 | let remote = remote.parse().unwrap(); 179 | let mut conf = Config::new().unwrap(); 180 | conf.recovery.initial_rtt = Duration::from_millis(initial_rtt); 181 | 182 | Path::new(local, remote, is_initial, &conf.recovery, "") 183 | } 184 | 185 | #[test] 186 | fn scheduler_name() { 187 | let cases = [ 188 | ("minrtt", Ok(MultipathAlgorithm::MinRtt)), 189 | ("Minrtt", Ok(MultipathAlgorithm::MinRtt)), 190 | ("MinRtt", Ok(MultipathAlgorithm::MinRtt)), 191 | ("MINRTT", Ok(MultipathAlgorithm::MinRtt)), 192 | ("redundant", Ok(MultipathAlgorithm::Redundant)), 193 | ("Redundant", Ok(MultipathAlgorithm::Redundant)), 194 | ("REDUNDANT", Ok(MultipathAlgorithm::Redundant)), 195 | ("roundrobin", Ok(MultipathAlgorithm::RoundRobin)), 196 | ("Roundrobin", Ok(MultipathAlgorithm::RoundRobin)), 197 | ("RoundRobin", Ok(MultipathAlgorithm::RoundRobin)), 198 | ("ROUNDROBIN", Ok(MultipathAlgorithm::RoundRobin)), 199 | ("redun", Err(Error::InvalidConfig("unknown".into()))), 200 | ]; 201 | 202 | for (name, algor) in cases { 203 | assert_eq!(MultipathAlgorithm::from_str(name), algor); 204 | } 205 | } 206 | } 207 | 208 | mod scheduler_minrtt; 209 | mod scheduler_redundant; 210 | mod scheduler_rr; 211 | -------------------------------------------------------------------------------- /src/multipath_scheduler/scheduler_minrtt.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::connection::path::PathMap; 16 | use crate::connection::space::PacketNumSpaceMap; 17 | use crate::connection::stream::StreamMap; 18 | use crate::multipath_scheduler::MultipathScheduler; 19 | use crate::Error; 20 | use crate::MultipathConfig; 21 | use crate::Result; 22 | 23 | /// MinRttScheduler prioritizes sending data on the path with the lowest RTT 24 | /// until its cwnd is fully utilized, and then proceeds to send data on the path 25 | /// with the next highest RTT. 26 | /// 27 | /// The scheduler aims to optimize throughput and achieve load balancing, making 28 | /// it particularly advantageous for bulk transfer applications in heterogeneous 29 | /// networks. 30 | pub struct MinRttScheduler {} 31 | 32 | impl MinRttScheduler { 33 | pub fn new(_conf: &MultipathConfig) -> MinRttScheduler { 34 | MinRttScheduler {} 35 | } 36 | } 37 | 38 | impl MultipathScheduler for MinRttScheduler { 39 | /// Select the path with the minimum RTT and sufficient congestion window. 40 | fn on_select( 41 | &mut self, 42 | paths: &mut PathMap, 43 | spaces: &mut PacketNumSpaceMap, 44 | streams: &mut StreamMap, 45 | ) -> Result { 46 | let mut best = None; 47 | 48 | for (pid, path) in paths.iter_mut() { 49 | // Skip the path that is not ready for sending non-probing packets. 50 | if !path.active() || !path.recovery.can_send() { 51 | continue; 52 | } 53 | 54 | // Select the path with the minimum srtt 55 | let srtt = path.recovery.rtt.smoothed_rtt(); 56 | match best { 57 | None => best = Some((pid, srtt)), 58 | Some((_, rtt)) => { 59 | if srtt < rtt { 60 | best = Some((pid, srtt)); 61 | } 62 | } 63 | } 64 | } 65 | 66 | match best { 67 | Some((i, _)) => Ok(i), 68 | None => Err(Error::Done), 69 | } 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | mod tests { 75 | use super::*; 76 | use crate::multipath_scheduler::tests::*; 77 | 78 | #[test] 79 | fn minrtt_single_available_path() -> Result<()> { 80 | let mut t = MultipathTester::new()?; 81 | 82 | let mut s = MinRttScheduler {}; 83 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 0); 84 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 0); 85 | Ok(()) 86 | } 87 | 88 | #[test] 89 | fn minrtt_multi_available_path() -> Result<()> { 90 | let mut t = MultipathTester::new()?; 91 | t.add_path("127.0.0.1:443", "127.0.0.2:8443", 50)?; 92 | t.add_path("127.0.0.1:443", "127.0.0.3:8443", 150)?; 93 | t.add_path("127.0.0.1:443", "127.0.0.4:8443", 100)?; 94 | 95 | let mut s = MinRttScheduler {}; 96 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 1); 97 | 98 | t.set_path_active(1, false)?; 99 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 3); 100 | 101 | Ok(()) 102 | } 103 | 104 | #[test] 105 | fn minrtt_no_available_path() -> Result<()> { 106 | let mut t = MultipathTester::new()?; 107 | t.set_path_active(0, false)?; 108 | 109 | let mut s = MinRttScheduler {}; 110 | assert_eq!( 111 | s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams), 112 | Err(Error::Done) 113 | ); 114 | Ok(()) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/multipath_scheduler/scheduler_redundant.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use log::*; 16 | use std::time::Instant; 17 | 18 | use crate::connection::path::PathMap; 19 | use crate::connection::space::BufferType; 20 | use crate::connection::space::PacketNumSpaceMap; 21 | use crate::connection::space::SentPacket; 22 | use crate::connection::stream::StreamMap; 23 | use crate::frame::Frame; 24 | use crate::multipath_scheduler::MultipathScheduler; 25 | use crate::Error; 26 | use crate::MultipathConfig; 27 | use crate::Result; 28 | 29 | /// RedundantScheduler sends all packets redundantly on all available paths. 30 | /// 31 | /// The scheduler utilizes additional bandwidth to minimize latency, thereby 32 | /// reducing the overall flow completion time for applications with bounded 33 | /// bandwidth requirements that can be met by a single path. 34 | /// In scenarios where two paths with varying available bandwidths are present, 35 | /// it ensures a goodput at least equivalent to the best single path. 36 | pub struct RedundantScheduler {} 37 | 38 | impl RedundantScheduler { 39 | pub fn new(_conf: &MultipathConfig) -> RedundantScheduler { 40 | RedundantScheduler {} 41 | } 42 | } 43 | 44 | impl MultipathScheduler for RedundantScheduler { 45 | /// Select a path with sufficient congestion window. 46 | fn on_select( 47 | &mut self, 48 | paths: &mut PathMap, 49 | spaces: &mut PacketNumSpaceMap, 50 | streams: &mut StreamMap, 51 | ) -> Result { 52 | for (pid, path) in paths.iter_mut() { 53 | // Skip the path that is not ready for sending non-probing packets. 54 | if !path.active() || !path.recovery.can_send() { 55 | continue; 56 | } 57 | return Ok(pid); 58 | } 59 | Err(Error::Done) 60 | } 61 | 62 | /// Try to reinject the sent packet to other available paths. 63 | fn on_sent( 64 | &mut self, 65 | packet: &SentPacket, 66 | now: Instant, 67 | path_id: usize, 68 | paths: &mut PathMap, 69 | spaces: &mut PacketNumSpaceMap, 70 | streams: &mut StreamMap, 71 | ) { 72 | if packet.buffer_flags.has_buffered() { 73 | return; 74 | } 75 | 76 | // Reinject the frames to other active paths. 77 | for (pid, path) in paths.iter() { 78 | if pid == path_id || !path.active() { 79 | continue; 80 | } 81 | let space = match spaces.get_mut(path.space_id) { 82 | Some(space) => space, 83 | None => return, 84 | }; 85 | for frame in &packet.frames { 86 | if let Frame::Stream { .. } = frame { 87 | debug!("RedundantScheduler: inject {:?} on path {:?}", frame, pid); 88 | space.buffered.push_back(frame.clone(), BufferType::High); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | 95 | #[cfg(test)] 96 | mod tests { 97 | use super::*; 98 | use crate::multipath_scheduler::tests::MultipathTester; 99 | 100 | #[test] 101 | fn redundant_select() -> Result<()> { 102 | let mut t = MultipathTester::new()?; 103 | t.add_path("127.0.0.1:443", "127.0.0.2:8443", 50)?; 104 | 105 | let mut s = RedundantScheduler {}; 106 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 0); 107 | 108 | t.set_path_active(0, false)?; 109 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 1); 110 | 111 | t.set_path_active(1, false)?; 112 | assert_eq!( 113 | s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams), 114 | Err(Error::Done) 115 | ); 116 | Ok(()) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/multipath_scheduler/scheduler_rr.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::connection::path::PathMap; 16 | use crate::connection::space::PacketNumSpaceMap; 17 | use crate::connection::stream::StreamMap; 18 | use crate::multipath_scheduler::MultipathScheduler; 19 | use crate::Error; 20 | use crate::MultipathConfig; 21 | use crate::Path; 22 | use crate::Result; 23 | 24 | /// RoundRobinScheduler iterates over the available paths and select the next 25 | /// one whose congestion window is open. 26 | /// 27 | /// The simple scheduler aims to guarantee that the capacity of each path is 28 | /// fully utilized as the distribution across all path is equal. It is for 29 | /// testing purposes only. 30 | pub struct RoundRobinScheduler { 31 | last: Option, 32 | } 33 | 34 | impl RoundRobinScheduler { 35 | pub fn new(_conf: &MultipathConfig) -> RoundRobinScheduler { 36 | RoundRobinScheduler { last: None } 37 | } 38 | } 39 | 40 | impl RoundRobinScheduler { 41 | /// Iterate and find the last used path 42 | fn find_last(&self, iter: &mut slab::IterMut, last: usize) -> bool { 43 | for (pid, _) in iter.by_ref() { 44 | if pid != last { 45 | continue; 46 | } 47 | return true; 48 | } 49 | false 50 | } 51 | 52 | /// Try to select an available path 53 | fn select(&mut self, iter: &mut slab::IterMut) -> Option { 54 | for (pid, path) in iter.by_ref() { 55 | // Skip the path that is not ready for sending non-probing packets. 56 | if !path.active() || !path.recovery.can_send() { 57 | continue; 58 | } 59 | 60 | self.last = Some(pid); 61 | return Some(pid); 62 | } 63 | None 64 | } 65 | } 66 | 67 | impl MultipathScheduler for RoundRobinScheduler { 68 | /// Select the next path with sufficient congestion window. 69 | fn on_select( 70 | &mut self, 71 | paths: &mut PathMap, 72 | spaces: &mut PacketNumSpaceMap, 73 | streams: &mut StreamMap, 74 | ) -> Result { 75 | let mut iter = paths.iter_mut(); 76 | let mut exist_last = false; 77 | 78 | // Iterate and find the last used path 79 | if let Some(last) = self.last { 80 | if self.find_last(&mut iter, last) { 81 | exist_last = true; 82 | } else { 83 | // The last path has been abandoned 84 | iter = paths.iter_mut(); 85 | } 86 | } 87 | 88 | // Find the next available path 89 | if let Some(pid) = self.select(&mut iter) { 90 | return Ok(pid); 91 | } 92 | if !exist_last { 93 | return Err(Error::Done); 94 | } 95 | 96 | let mut iter = paths.iter_mut(); 97 | if let Some(pid) = self.select(&mut iter) { 98 | return Ok(pid); 99 | } 100 | Err(Error::Done) 101 | } 102 | } 103 | 104 | #[cfg(test)] 105 | mod tests { 106 | use super::*; 107 | use crate::multipath_scheduler::tests::*; 108 | 109 | #[test] 110 | fn round_robin_single_available_path() -> Result<()> { 111 | let mut t = MultipathTester::new()?; 112 | 113 | let mut s = RoundRobinScheduler::new(&MultipathConfig::default()); 114 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 0); 115 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 0); 116 | Ok(()) 117 | } 118 | 119 | #[test] 120 | fn round_robin_multi_available_path() -> Result<()> { 121 | let mut t = MultipathTester::new()?; 122 | t.add_path("127.0.0.1:443", "127.0.0.2:8443", 50)?; 123 | t.add_path("127.0.0.1:443", "127.0.0.3:8443", 150)?; 124 | t.add_path("127.0.0.1:443", "127.0.0.4:8443", 100)?; 125 | 126 | let mut s = RoundRobinScheduler::new(&MultipathConfig::default()); 127 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 0); 128 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 1); 129 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 2); 130 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 3); 131 | 132 | t.set_path_active(1, false)?; 133 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 0); 134 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 2); 135 | 136 | t.set_path_active(3, false)?; 137 | assert_eq!(s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams)?, 0); 138 | Ok(()) 139 | } 140 | 141 | #[test] 142 | fn round_robin_no_available_path() -> Result<()> { 143 | let mut t = MultipathTester::new()?; 144 | t.set_path_active(0, false)?; 145 | 146 | let mut s = RoundRobinScheduler::new(&MultipathConfig::default()); 147 | assert_eq!( 148 | s.on_select(&mut t.paths, &mut t.spaces, &mut t.streams), 149 | Err(Error::Done) 150 | ); 151 | Ok(()) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/timer_queue.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::time::Duration; 16 | use std::time::Instant; 17 | 18 | use priority_queue::double_priority_queue::DoublePriorityQueue; 19 | 20 | type Index = u64; 21 | 22 | /// Store timers in a binary queue. Keep them sorted by which timer is going to expire first. 23 | pub struct TimerQueue { 24 | timers: DoublePriorityQueue, 25 | } 26 | 27 | impl TimerQueue { 28 | /// Create a new TimerQueue. 29 | pub fn new() -> Self { 30 | Self { 31 | timers: DoublePriorityQueue::new(), 32 | } 33 | } 34 | 35 | /// Creates an empty timer queue with a specific capacity. 36 | pub fn with_capacity(capacity: usize) -> Self { 37 | Self { 38 | timers: DoublePriorityQueue::with_capacity(capacity), 39 | } 40 | } 41 | 42 | /// Return the number of timers in the queue. 43 | pub fn len(&self) -> usize { 44 | self.timers.len() 45 | } 46 | 47 | /// Return if the timer queue is empty. 48 | pub fn is_empty(&self) -> bool { 49 | self.timers.is_empty() 50 | } 51 | 52 | /// Add a timer into the queue, replacing any existing timer if one exists. 53 | pub fn add(&mut self, idx: u64, duration: Duration, now: Instant) { 54 | _ = self.timers.push(idx, now + duration); 55 | } 56 | 57 | /// Delete a timer by id. 58 | pub fn del(&mut self, idx: &u64) { 59 | _ = self.timers.remove(idx); 60 | } 61 | 62 | /// Return the amount of time remaining for the earliest expiring timer. 63 | pub fn time_remaining(&self, now: Instant) -> Option { 64 | self.timers.peek_min().map(|(_, expires_at)| { 65 | if now > *expires_at { 66 | return Duration::new(0, 0); 67 | } 68 | *expires_at - now 69 | }) 70 | } 71 | 72 | /// Return the next expired timer if any. 73 | pub fn next_expire(&mut self, now: Instant) -> Option { 74 | if let Some((_, expires_at)) = self.timers.peek_min() { 75 | if *expires_at <= now { 76 | let idx = self.timers.pop_min().map(|(idx, _)| idx).unwrap(); 77 | return Some(idx); 78 | } 79 | } 80 | None 81 | } 82 | 83 | /// Clear all the timers 84 | pub fn clear(&mut self) { 85 | self.timers.clear(); 86 | } 87 | } 88 | 89 | impl Default for TimerQueue { 90 | fn default() -> Self { 91 | Self::new() 92 | } 93 | } 94 | 95 | #[cfg(test)] 96 | mod tests { 97 | use super::*; 98 | 99 | #[test] 100 | fn add() { 101 | let mut tq = TimerQueue::with_capacity(10); 102 | assert!(tq.is_empty()); 103 | 104 | let now = Instant::now(); 105 | // Add a new timer. 106 | tq.add(0, Duration::from_millis(100), now); 107 | assert_eq!(tq.len(), 1); 108 | 109 | // Add another new timer. 110 | tq.add(1, Duration::from_millis(200), now); 111 | assert_eq!(tq.len(), 2); 112 | 113 | // Add an existing new timer. 114 | tq.add(1, Duration::from_millis(300), now); 115 | assert_eq!(tq.len(), 2); 116 | } 117 | 118 | #[test] 119 | fn del() { 120 | let mut tq = TimerQueue::default(); 121 | 122 | let now = Instant::now(); 123 | // Add a new timer. 124 | tq.add(0, Duration::from_millis(100), now); 125 | assert_eq!(tq.len(), 1); 126 | 127 | // Delete a non-existing timer. 128 | tq.del(&1); 129 | assert_eq!(tq.len(), 1); 130 | 131 | // Delete an existing new timer. 132 | tq.del(&0); 133 | assert!(tq.is_empty()); 134 | } 135 | 136 | #[test] 137 | fn expired() { 138 | let mut tq = TimerQueue::default(); 139 | 140 | let now = Instant::now(); 141 | tq.add(0, Duration::from_millis(100), now); 142 | tq.add(1, Duration::from_millis(200), now); 143 | tq.add(2, Duration::from_millis(300), now); 144 | assert!(tq.next_expire(now).is_none()); 145 | assert_eq!(tq.len(), 3); 146 | 147 | let t = now + Duration::from_millis(100); 148 | let idx = tq.next_expire(t); 149 | assert!(idx.is_some()); 150 | assert_eq!(idx.unwrap(), 0); 151 | assert_eq!(tq.len(), 2); 152 | 153 | tq.del(&2); 154 | tq.add(3, Duration::from_millis(1000), now); 155 | tq.add(4, Duration::from_millis(1000), now); 156 | tq.add(5, Duration::from_millis(1500), now); 157 | let t = now + Duration::from_millis(1000); 158 | assert_eq!(tq.next_expire(t), Some(1)); 159 | assert_eq!(tq.next_expire(t), Some(3)); 160 | assert_eq!(tq.next_expire(t), Some(4)); 161 | assert_eq!(tq.len(), 1); 162 | } 163 | 164 | #[test] 165 | fn time_remaining() { 166 | let mut tq = TimerQueue::default(); 167 | 168 | let now = Instant::now(); 169 | assert_eq!(tq.time_remaining(now), None); 170 | 171 | tq.add(0, Duration::from_millis(100), now); 172 | tq.add(1, Duration::from_millis(200), now); 173 | tq.add(2, Duration::from_millis(300), now); 174 | assert_eq!(tq.len(), 3); 175 | assert_eq!(tq.time_remaining(now), Some(Duration::from_millis(100))); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/tls/boringssl/boringssl.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | pub(crate) mod crypto; 16 | pub(crate) mod tls; 17 | -------------------------------------------------------------------------------- /src/tls/testdata/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC7zCCAdegAwIBAgIJAK+PzwLWVBGQMA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNV 3 | BAMMAkNBMCAXDTIzMTEzMDAyMDkzOVoYDzIxMjMxMTA2MDIwOTM5WjANMQswCQYD 4 | VQQDDAJDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOXUeamr4YG5 5 | D2wCek385yr2La4zFoSSMFmn2wZLq+pzxWiKtunTt+fPZawQokHpwIloRl79+PBC 6 | obx+FVA7tDhtJx7apvk5/ejjtZCevI/R2WpLh4FdNwkXkaBLn1DZJeACOm1TInYw 7 | +64zoSrcCXomPBx0HDvCcIw65D2Icu1z9cFgUsyfZv/nni2c74j0vX6DaFo4PGw/ 8 | DLIlhtTjgByGzsol2oZmCVNDsRK4HZv3xUOIkj/QvsNu68X6y03rhPiQNbcgdwgC 9 | 8n3kEL8SGQFe+NR9dWks6MQhYJ1SQAjayJTcknxqLb+H3a9jOMdQ2e62neVM6/cf 10 | ZjeOzBtOPiUCAwEAAaNQME4wHQYDVR0OBBYEFApU7WWKaNWsNfrXeye8S70Czqy+ 11 | MB8GA1UdIwQYMBaAFApU7WWKaNWsNfrXeye8S70Czqy+MAwGA1UdEwQFMAMBAf8w 12 | DQYJKoZIhvcNAQELBQADggEBAGQ4UDsLxPZnciMDGnoY7tmT02aIMF8tvo96Okjr 13 | dn24AcNxCaINa1qiYujGvYgXI63FZEC+LM4M0q96pN/wEHWvJ7CcPB4cEFveuRTQ 14 | GOISDyPo5nTHIoXijozQM3SpJEP+fCdBuANfsrYLsXfeH++n4EiTIGnst4iODLPC 15 | RI65l/DfLngzQk1RZxYR3pDQDxCC7y4KC3dqso2NVe8GuxP8IY6m6LHAE416pKIk 16 | Cspl1tfOA28uYaku+KnQovy6QKALFQHnXSclPVH+IwHwvt7f2FAB/cEI+4TC/vXm 17 | 05qZYx3czd8w2j8CxRru+L7NpSFRDd86AyvazX5IiZct1iY= 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /src/tls/testdata/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDl1Hmpq+GBuQ9s 3 | AnpN/Ocq9i2uMxaEkjBZp9sGS6vqc8Voirbp07fnz2WsEKJB6cCJaEZe/fjwQqG8 4 | fhVQO7Q4bSce2qb5Of3o47WQnryP0dlqS4eBXTcJF5GgS59Q2SXgAjptUyJ2MPuu 5 | M6Eq3Al6JjwcdBw7wnCMOuQ9iHLtc/XBYFLMn2b/554tnO+I9L1+g2haODxsPwyy 6 | JYbU44Achs7KJdqGZglTQ7ESuB2b98VDiJI/0L7DbuvF+stN64T4kDW3IHcIAvJ9 7 | 5BC/EhkBXvjUfXVpLOjEIWCdUkAI2siU3JJ8ai2/h92vYzjHUNnutp3lTOv3H2Y3 8 | jswbTj4lAgMBAAECggEAQa4kCrolGvZxEtPuceUphxyNV3AgKkjA6o5OyJDNk5X8 9 | P3EM7uvjbarWioLCbAXKUTpsie8YukjAgeaiXqiBS3LeMDIab7UD/A8/LxI+HvfA 10 | o2DsW6rw9Hc41c9muysjaM4aNm4AtjgvZMw6bc4uIytUGGmSO/3IPjsCSRd6iKMF 11 | Plvw4/7z9kDA/B1F3lJfh/tTPUPATX09d4J7bkWaHTDCXr2MmIOuBJempe7Tsd74 12 | AUrEoq3lphVOjrsGwmdAgWveRxRo/EEhw1mwLJn9PUAMh6ZOmnz3Osri1qm22ihR 13 | w6p1FeH3jn2c/rSZLje41Dl1Ih80ttl37ZJdArZkLQKBgQD9TfWBwww+FxcNCGsO 14 | eD/CT872XmQw2wJ1dZhY1U+42eU/16+wjEM2UrTA0ppLwRfdmD8vUjjJiLFDsPVT 15 | kMf/5TOmamQdE/x8qERjcozAROJ/3glVxyVzrZXmN3fHUhCk5MopKM74DfZPT8Yw 16 | InjF9mZAIYP7/QMoE50+o8y92wKBgQDoRpFi0/fz9EzdyYQHJtHL89KDY7XJ6d7T 17 | EU36CJWeTFZqCPKVxGMdPxxPWVChlNrSaSkvIanw/hD8S0qjVf65n1LG8gxvYGb8 18 | 7cGi6CLaQc5kRcLkelPecwL5dD0qb3pbSsHFFFxa9jXOOqGyQrfcQd/o0lZefLmx 19 | 4st6O+ez/wKBgFypNhQf68yS2cXUvITwjDJiy4zofrI4i6+4MuDcN2uhmfE+z3A/ 20 | /x5SJvkakYMAweA4H3UKGOfwbxgaFcRaBC+iAsDvzmussgJP86SFdOOoz0qDSNru 21 | HM8yQtGSnOh3JsuLPkDMQtoPEXl170QQeCnCtyjimcuGhK8WbcFKXk1dAoGBANZ0 22 | mTVTGkLek73Omhz0RY32T65J+IU1bisYdhNdq66dMsfx/PA0g4J0S5Ijl6OoSSUu 23 | PP0sKri8Czi113LZKjd+F7JhtwN6UXxDlJ3xkIRNb9763gePvjHLpLpnDTZJ+QGj 24 | EycANokxe9ouHi71FMbC95YB5gaVR+oqyM3R04tHAoGBAKwgnA8wIhZMCmMIhso/ 25 | GskOg4SyCCDndjbrRRqW64XwG6jvQvzJLCRaUhuhlJoTkW+KqrYGk4Nq4QN3aJZn 26 | rDEBbSkAm59Kz81LBJ5mx2t3P4aStQivWYiO/Xi4dX5XRT2H9Nfwh5Hcc5qEqtJ2 27 | O3WdvMG77cqB6ve4UdOACbQs 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /src/tls/testdata/cas/56c899cd.0: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC7zCCAdegAwIBAgIJAK+PzwLWVBGQMA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNV 3 | BAMMAkNBMCAXDTIzMTEzMDAyMDkzOVoYDzIxMjMxMTA2MDIwOTM5WjANMQswCQYD 4 | VQQDDAJDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOXUeamr4YG5 5 | D2wCek385yr2La4zFoSSMFmn2wZLq+pzxWiKtunTt+fPZawQokHpwIloRl79+PBC 6 | obx+FVA7tDhtJx7apvk5/ejjtZCevI/R2WpLh4FdNwkXkaBLn1DZJeACOm1TInYw 7 | +64zoSrcCXomPBx0HDvCcIw65D2Icu1z9cFgUsyfZv/nni2c74j0vX6DaFo4PGw/ 8 | DLIlhtTjgByGzsol2oZmCVNDsRK4HZv3xUOIkj/QvsNu68X6y03rhPiQNbcgdwgC 9 | 8n3kEL8SGQFe+NR9dWks6MQhYJ1SQAjayJTcknxqLb+H3a9jOMdQ2e62neVM6/cf 10 | ZjeOzBtOPiUCAwEAAaNQME4wHQYDVR0OBBYEFApU7WWKaNWsNfrXeye8S70Czqy+ 11 | MB8GA1UdIwQYMBaAFApU7WWKaNWsNfrXeye8S70Czqy+MAwGA1UdEwQFMAMBAf8w 12 | DQYJKoZIhvcNAQELBQADggEBAGQ4UDsLxPZnciMDGnoY7tmT02aIMF8tvo96Okjr 13 | dn24AcNxCaINa1qiYujGvYgXI63FZEC+LM4M0q96pN/wEHWvJ7CcPB4cEFveuRTQ 14 | GOISDyPo5nTHIoXijozQM3SpJEP+fCdBuANfsrYLsXfeH++n4EiTIGnst4iODLPC 15 | RI65l/DfLngzQk1RZxYR3pDQDxCC7y4KC3dqso2NVe8GuxP8IY6m6LHAE416pKIk 16 | Cspl1tfOA28uYaku+KnQovy6QKALFQHnXSclPVH+IwHwvt7f2FAB/cEI+4TC/vXm 17 | 05qZYx3czd8w2j8CxRru+L7NpSFRDd86AyvazX5IiZct1iY= 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /src/tls/testdata/cert.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDEjCCAfqgAwIBAgIJAIAdu56fLE7OMA0GCSqGSIb3DQEBCwUAMBYxFDASBgNV 3 | BAMMC2V4YW1wbGUub3JnMCAXDTIwMDYxMDEwMzM0NFoYDzIxMjAwNTE3MTAzMzQ0 4 | WjAWMRQwEgYDVQQDDAtleGFtcGxlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP 5 | ADCCAQoCggEBALv+LV1aWIlcK9rI7IuRS8SCusqBnoyJec/ErKiA2gbfgZ/YS73L 6 | Zud84yp45AIqauzcI5q+hrkmsRZ7CKqDzrG+jHavW7jF+0laetJwRt26AcQcOtQD 7 | 2ik2O+Dl1WHAFn4vUAQxb+Xz6WfSaQN0QfM74z06XUDDsr7g7+NYtMzhf98SJSoK 8 | ne/dVKJ3Bc6e6tvhnCRwPtix4ektEodK6WeNHYxwJ6wSZ8cRLzdxgjdD/4OGfFuj 9 | dn8zbOi3SQt5ZqVbcDHUTzp5t0G8EoxnzotHhhzjSAmsypySqZXaxl3oX8aYUkFn 10 | fCdg+WBXo5pOiNfoWh/D5bnIXWGp52yoy+kCAwEAAaNhMF8wHQYDVR0lBBYwFAYI 11 | KwYBBQUHAwIGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFIH+0G3eCswQHbN06kvI80M3 12 | tNH9MB0GA1UdDgQWBBSxLHQE7gOEyfeSNc5uIO/G/rgjpzANBgkqhkiG9w0BAQsF 13 | AAOCAQEAlGm5RwQ79xmLh3rj+5UViCSgsIuMcuhgIT4zogpo9S4uwXMqinrJhzRk 14 | Oc2tb3y06XTAq1lMH2+58tqndAu8ni/UBz3OSghk2CTnZ1vxxXOd3CtQu4ypMq+k 15 | qW0Umdrkk5TeAODNbrCy4c6vpICkQOljnRFWnDYu3aQ3JvaWZ/nObN7C72Lgpjfb 16 | RfLXGmLsBCEIr028f9hpoeRCXoetUY2CiC2boAHR+cO6Jpvex4Jv5yYDpNKac52n 17 | LLC8Cq5ozhOZNOSV6X9FpEca3rdhVUb0VgoNDCPZDdpO1PDJYCN9fUC8KUs+dsOh 18 | SYGpliBaNsztoiJs1q5SkMQrMwmMmg== 19 | -----END CERTIFICATE----- 20 | 21 | -------------------------------------------------------------------------------- /src/tls/testdata/cert.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAu/4tXVpYiVwr2sjsi5FLxIK6yoGejIl5z8SsqIDaBt+Bn9hL 3 | vctm53zjKnjkAipq7Nwjmr6GuSaxFnsIqoPOsb6Mdq9buMX7SVp60nBG3boBxBw6 4 | 1APaKTY74OXVYcAWfi9QBDFv5fPpZ9JpA3RB8zvjPTpdQMOyvuDv41i0zOF/3xIl 5 | Kgqd791UoncFzp7q2+GcJHA+2LHh6S0Sh0rpZ40djHAnrBJnxxEvN3GCN0P/g4Z8 6 | W6N2fzNs6LdJC3lmpVtwMdRPOnm3QbwSjGfOi0eGHONICazKnJKpldrGXehfxphS 7 | QWd8J2D5YFejmk6I1+haH8PluchdYannbKjL6QIDAQABAoIBAEUBL7WsjAMfihls 8 | 1ycD1kPzmIzstz3u2H+jOZ1AbsdHE1WRF3w7RTKDbP8SEN+aolT/GTKb7OfZg/c0 9 | giHU7/Hed8C47XoNcgei5qKIA/svY6aQlidsoo+uEJykwIZ488itpTlkzCYkOfCa 10 | E2HpMqwNt4OqAMDdFKdr+aIB1Zu+KPBxW23WD9wEWAbe5LA4YnRF9kT4YZ6y9mce 11 | dGaIf39VtBlrGMmvoU0LE9B79nyuebGi0svW6QDarBqaDrnM/N3fXgL1kk/gVfan 12 | /xs6EA4qPxA5G4h+enYrIlZL0CbSj60nYElo+Z5nRdBaRdCF/bpXOLyK/kXWLUM0 13 | f2HTK+ECgYEA5cVcpJtczxEaxoaEUbppsW1LCrTjJDGTKJ63G7/lwqCxJeCHN185 14 | nnckHOW2287e19bu9aUmKJgRq5s1rXnT+MnCl/hQnfaMrKOKtzE+t7/zsc9+LuAr 15 | pwJrtZ9Dcnwrk8NOE0fPjW5XpDCSoEOo7JWZmGTVlpabOgNkjocfp+sCgYEA0XPt 16 | ZPt3F0wyzgLYRhgnvp5CV8SzQmulsW+ytnL5eiAcNSXqni3wQHN3PGxLInEyQwBQ 17 | /M8TQpUbqGMmahCK4ZxMAwXMrpF0mVB8jfoYMou1FSYPlUV+CvLjWcTkZB1Nirez 18 | VFXdtfHP0mx4PbYK2qjB03u4pPHAN8kuayIf2nsCgYAbv/FHZAgabfto3Jggcr4P 19 | Ep8MhPolxeL69egxbsSl89hRNcO+2T5ROBxhbRDfjSV2tduYSUDJiEwiCJW8BMmn 20 | 814QEopR+ZPVyc6X/1eOw5z/7YpUyPgcrHsrrTdtHTf6GY1VYMfdUeU9zCv5NRKy 21 | uAKb2Bm/nSLUJ9K+L+2PzwKBgQDRQgT3UtTUjehkMitpPFDY/LxDe92simfsMjBW 22 | X+Anx1TnNI6GolbZzYJe98LJElao4fQH38raRqZvQT/rz8MxTDoU+wJXljLryaHn 23 | Jupt9W5hRrli5R7cSXYjBbc43p3N7WJY68CqOoDrNjubS/jkJJ4hcAY1pOHp2jFq 24 | D5nLaQKBgAysU6O5kJ8yKxhbZflb42MqKCFBGrbRnbYx14PAEZOaRhzxehpppQmx 25 | RLbn/z1Uh5Ms28ipxA+vnhyM3FcU5lKboaFyWJeuNslw0FxEcIai6hL6UkDznS4G 26 | aqyzUjpG5Chg0x18xWYCbiGJwjZ9BWhtH+jojm856QHGzeQJWVoo 27 | -----END RSA PRIVATE KEY----- 28 | 29 | -------------------------------------------------------------------------------- /src/tls/testdata/cert2.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDrjCCAZYCCQDXv+byN+9g3DANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9J 3 | bnRlcm1lZGlhdGUgQ0EwIBcNMjMwNjAyMDkyMzM1WhgPMjEyMzA1MDkwOTIzMzVa 4 | MBYxFDASBgNVBAMMC2V4YW1wbGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 5 | MIIBCgKCAQEArtGECooltEozkvttTBHNCyIYIlbyZFr3h+XS/27KcR0K9mXtab62 6 | nefgRrTo6emIyPixVy/WG63S2DjGcn4sCFxsu7yqadUdsWOch3bO8+1e0k6hyQAb 7 | 4dZDjdOhGruLA76sLcNyFObgks9yMobAKCU6NeOz9bc3OSmTWCVTH4wc85p1V5Bz 8 | TtAT3p59O0nmcB4R2Ks2+SXNMwdoHLSqggLuNOwhkfwCmzOwwQrbvRbTBe1r/R+E 9 | +BKluoP1LpGV0+aVHA8lzHNsfOr1+XniXEB4pmyuWia9raD/jsBAaKgYlM4QaDQJ 10 | fO4y4ttWN+iqrxJRiqIOnBFK5CCgyCGSqQIDAQABMA0GCSqGSIb3DQEBCwUAA4IC 11 | AQCH6JMxpqTzEhW/CHtKoNzX33g6296kiyPFJNvOiBEmsuiNTGoSYPfmQBYIvHOk 12 | TDI4YGi5jsM8o0m0v9sI+8Sa3+2cXCREbzNuzok2gaGgJ0zOknBAr0ZoXX5huFVM 13 | cgjvuHiInXR9HnbmYmAc+dFsZntvgUgQlKrwGRSjuPtN5vvOY2UmwbNqmpZkRaFj 14 | /VFnj8jCZeEmjIIajSHrzBttKKH4kh71NzT5CS+OUFEMKLomLEAtYhgymBjLO3ZI 15 | vwGPVSV6vkkLqfL19hyiy/q+dvCNy4iEqowMm61ZN8d3poYKyL4F7I6+APEMuyf+ 16 | VRf6y8vC7snX8jI+IA/p88n/q/q0tuBAtlq+Fa7dC5UxjLgl6jFZOXH6OQH27ka7 17 | jKawJLW9TB2//wYxEzvOGjbsyC8J7mjJJyR3nGZulxRP/2js6pJcmq8HLNlQVSVn 18 | AQYE2mLRq5jYYOYfLEhihJk3/JzE6Ypxry1iKgJAPIS8pgSAbO3fSlus9q6/PGL+ 19 | Iz4/ENfINC2KpJ7+Ai5GR3slGl9+hXj/1/UcBvwW3xuwy0algQTPaTGJ7M60/Dgi 20 | /51tS5/Rce1LrH5QH2zhCbBJUhO2vpgjhJPEpWD/MyQv65pveNOLyN8DCcaTlYEW 21 | eLt9z3Ssw6rEfghvKhx1RqingS2HluRlvC71ygAfkPQDMg== 22 | -----END CERTIFICATE----- 23 | -----BEGIN CERTIFICATE----- 24 | MIIEqjCCApICCQDlNStYO4vXcTANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdS 25 | b290IENBMCAXDTIzMDYwMjA5MTYyNloYDzIxMjMwNTA5MDkxNjI2WjAaMRgwFgYD 26 | VQQDDA9JbnRlcm1lZGlhdGUgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK 27 | AoICAQDT7E+BwJkQabrScfB28ng07fWPZnYNf5OihK7uh0Rc32r4xWYp+GaCJ8yQ 28 | oVghdJf8CHmoXYwC/PbL3hHjFFteTc5UnwACZxY42lQqgQJVy5RRD0hAy4qp3gz+ 29 | dqodOe2XGXeiWSnGU+JtWijB8lAf6qeCOo0f96xqECqiLIJDwrFlOUcUgeg7W+Ya 30 | jymDESi+l3mqN7zGE6x/4vvEOHqmW9kKxIS57WS35rvZoTPhf0rAb6rY0Y+LwkPK 31 | mwwtWOFwXTo0sZkrfPo+g/uszEIIpg0SVDEnxcNNIzM4VNbiP8N/wLtFQuOf25KZ 32 | LicDuT8gRQ8WDLFLnPkxpenyAwraYyF0+Yon5DGnw3S05o0Gt0f6WdtA/AW9qWYQ 33 | vMyF04MAM4IHobalzjVwyZgRO1d7c2lr6VTOcPeIazAnTAasRuP7zDrE+eXIREYN 34 | M+Bg8gRXtrVH3xDkGmRCSR5C+S4MumJQE5pe3/NXJTt+fVKI6W0P2VbtY6kESfPb 35 | X1536DzJWLiT/qd2lZEpavhcM+IKQv1fHmGbVExQsvreMXRtbeeiVB47dpMXgf/T 36 | Qs4TqORgsjfWoxQVd5rRaJye3WPRrY/g8jnbWSRF3oFLw0lxrwWdvwm08OsRcCT0 37 | cqqu0azAMSHBnCUacTZjvPXMEGhGrMZCeyGqnCHykrmMUZG2WQIDAQABMA0GCSqG 38 | SIb3DQEBCwUAA4ICAQDfqhiIyuYWzWqNYy0by0oB++bizGySBxslGhWGEc6kF80f 39 | QSQNND94WQrJHal8tgKhyuInde+UpHkC5WWgPtJf65dQ4j6mlRJCCpr3WdbOVdvM 40 | PTP3DhRLjet4t1UNX3obMGQdnfGIRysjCvorVMR1jtBaEHf40rDPZX7lsGGFRe3k 41 | hCJIRl9mKZtaeXUo7WQMx4oCDBdk9Lzyw9hV91ETJWL+HwNkjgM9alqE5tzkDaeU 42 | vphJsQqCX73sk57a6VJoSNDLvt9gn/pl977Pt6ZlWr0WtTFuGejA/KUSfQCDU2WA 43 | v+HuOjPrg9ofTIoTj+4jyGSyx6QdtVF1H6Qb3ouRDwj8jkvxfIU7FSosRWGSw8PK 44 | 7fK6sVNwIVptxGMtUAuOcTdghNHgTAVYGwYv4GjIVRmVNRAHn/5HWdhJxfRZ/HHD 45 | XeOoZHkXMnkakeKnvOVhW70WfbSLza3brA3cBHDtiJCXpniQv8U2YCUWSRtYqoCa 46 | Cg62j9yd2F7NwaFZs6MzUuyeqEHYtJeBS7EMOppsf4cPdtEIIfeJHGEcxvD425x4 47 | n+gYlSooHbmSEH7gfwpezvoDwR9XS9IZI/eo+QbfJDq2dMUY+oo7cZ78DQXpaU6j 48 | DwLzmYAujfVYuFOx4FhvqSwmi2waCt5vtQT2vtKWr8FQNjLKpLRgZgwoj7iTBQ== 49 | -----END CERTIFICATE----- 50 | -------------------------------------------------------------------------------- /src/tls/testdata/cert2.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEArtGECooltEozkvttTBHNCyIYIlbyZFr3h+XS/27KcR0K9mXt 3 | ab62nefgRrTo6emIyPixVy/WG63S2DjGcn4sCFxsu7yqadUdsWOch3bO8+1e0k6h 4 | yQAb4dZDjdOhGruLA76sLcNyFObgks9yMobAKCU6NeOz9bc3OSmTWCVTH4wc85p1 5 | V5BzTtAT3p59O0nmcB4R2Ks2+SXNMwdoHLSqggLuNOwhkfwCmzOwwQrbvRbTBe1r 6 | /R+E+BKluoP1LpGV0+aVHA8lzHNsfOr1+XniXEB4pmyuWia9raD/jsBAaKgYlM4Q 7 | aDQJfO4y4ttWN+iqrxJRiqIOnBFK5CCgyCGSqQIDAQABAoIBAAw9Bq1mNnWErE+h 8 | 4mjs5hndbSsaQyXbaHDPGhJfwzRgACB3ykMY4tfzH7RTferCOp03K9MaujZ92MDi 9 | WKIzSwnazpHmB1gZX1bHznF9U8en2neGobTcmRcaVOhc5G5mqri8ZHlfsWDdOOOO 10 | OkRnQpSM5qJXKoYNKYSK8JG/Ky6OFtGc7goOOFIShHhPigy45llOsgBlKltGvyUS 11 | jvZUOweE7Nvzo0x8N026NxKSq/C+SWsDPN6q3A1OxjW6FswYtYmNG/qYyCBXGBt1 12 | HL90qUyyZdNGqHEo9Nv/JEp9Us1NFZtzHjL2oNmTdAvsshsqoX3HCq/LGB2MPRA3 13 | LoFqj7ECgYEA5TgkubDfbXCSBgGm5Mcb3AwyBwqW9i/Fl3/u6r6qtcxjGsQOgD+U 14 | fsj5ZbsxErs8RHUIc4deb4GfCE23js7xULdHh7UQzl4nW4eUJC/EL4YR0i6Vge5Y 15 | Px26tgGJYJIj8n8mg42HaYotkL5JuDh/QmXmMZeqFguvX39zVTrh/4sCgYEAwz5D 16 | iNH536+pC0AQPO5S6z5eIZ8IRTnNcC+KidEG+obButdIW3TNhmQcHh4UymEIMMDY 17 | CDNCKN74cMJDjfrFc6HxGerbXy4Px5v+pcmhkbqGABj1GMpcjei6v8B1S7dGm+ZM 18 | LFHP5j3zbiyP8QhMWxhDiLDOBCJUYeHflZ4mvRsCgYAIseaCvLjc9+YyICHqj7r4 19 | z+Wd6sLrN8ixBINVmEH1f3lK7KD4MHE7lfgP6dfktAGHP0FA0KO+axZTRE9wNV1m 20 | CG3IMhAfI+cuS6hM7xrsCpPmeynksdvbGQ+aE9jYNvLyIeyeFOJMGJe949n8CPia 21 | 1i6zNuOWzn6qirUfSIqGHQKBgHSLJiT1sgIkCPP+ObMlT7m8cNQA6h7ocgkVV80M 22 | SO4iomG1QCvXfRdLir0vl5l2cj0n38yFTGi5L/OfAlQt7M8pS5H87xXRmPm91DOC 23 | OD+U54gHXcZ+hODwmwj7CQk5TZyhy7Zo34DAWh7Z2tI18d4ujaF6aH9vPo9vFNdq 24 | oBUhAoGBAMen9ueh57iIL5G/l+3SViBBREzHq3nrFAngXZ2RhpQUEf6IOc0sTLrP 25 | B/ETPd5A8W/6LMMCaWk1BxuAnS2BGbAazB2/UilJlsMdpX1I20LvzsMrx/2HG1+z 26 | YAXzdMDOB55jO81BMIIKVQyp5v2Jo7XGeiIS/RDKD8iJE3OLIc46 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /src/tls/testdata/cert3.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICoTCCAYkCCQDAAmTovbds7DANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDDAJD 3 | QTAgFw0yMzExMzAwODQ2MThaGA8yMTIzMTEwNjA4NDYxOFowFjEUMBIGA1UEAwwL 4 | ZXhhbXBsZS5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5tk2H 5 | TIYiJxtcSga8ugnNlRNfwVGfUYdXd4EGls4UTzGQuB7Ov79snaz62lrNVE8lqlUY 6 | kwJMqyrSKjQoObQA3MIHznF7ohsraA8kvuhZMauetztRcxk5uRCWkL5/k6CJ3Xnx 7 | EdV8g5AjUZIDwMZa09xzMqM98BiXjKLsQZjOxJf6cxlilyS8ZSXoG0LKRi0z9qdO 8 | jw8Suv0SqDjPJ/JYJg89dcXBRvrHhziejoDdmUS9urLI1oFIfK+deE0qF7ZtQ9rU 9 | +MT+qzqlFYOSSSiBgwLaEczSZUuv/9Vam92zkbvc8oqGa17+1pI/18JqV47P4o6D 10 | XXZuiuA0y5HI++CFAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAIh6oup75XOVgki3 11 | P1X8YwXtLYn0q37oLRO8MP5sVMmTIIZ5rGXQVwFrgO7Eg1Q0PDPB3MGnDr54TjEH 12 | 3GbI+rNfmrik3XukfuIeD0dzMmgonrYYA3iBdEcuZ+Wv90u7fmWhZ+FTYmTlJhrQ 13 | TmiXMlsnd0JcR9tcHc9tXIrGvROyoZx57KwNJc/GgACwtEqac2BcU/0Ull1rAxZv 14 | CrS3YbcrTSu7v8oBlSLl+376yYvGLLDXms6ymtSi0xUnV19o+Sts7rum0OKYh38G 15 | UPRq/eHESs1ApYyutzgfxfmhaCw9BcFyFoGduG+2/eRB+oo/Mt9/7DNoII+QyoIA 16 | 36pmFOA= 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /src/tls/testdata/cert3.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5tk2HTIYiJxtc 3 | Sga8ugnNlRNfwVGfUYdXd4EGls4UTzGQuB7Ov79snaz62lrNVE8lqlUYkwJMqyrS 4 | KjQoObQA3MIHznF7ohsraA8kvuhZMauetztRcxk5uRCWkL5/k6CJ3XnxEdV8g5Aj 5 | UZIDwMZa09xzMqM98BiXjKLsQZjOxJf6cxlilyS8ZSXoG0LKRi0z9qdOjw8Suv0S 6 | qDjPJ/JYJg89dcXBRvrHhziejoDdmUS9urLI1oFIfK+deE0qF7ZtQ9rU+MT+qzql 7 | FYOSSSiBgwLaEczSZUuv/9Vam92zkbvc8oqGa17+1pI/18JqV47P4o6DXXZuiuA0 8 | y5HI++CFAgMBAAECggEAXAQP9fHcpB9sVxX2sJwdL1el+dP3dVEYFiaIL5OZGEcE 9 | 8AfKe/ZWCqM1ijWWuBYGAUucTkjI4uQMm4V9wlCU97hqCyQm5nMzsxYkKrjMtyT9 10 | O+7d3UWCQi+IIJInUV78NFMgkGmdtcwaJuXLy0BBB8/hctKsP4wvIyfzscvuwsOd 11 | ciYvh/OHGbMAGtNS46mjNzaUCgIHGTjdREhYFM/XZBq2xU/RGchsz5Vfy9/haMPi 12 | X8ipAOi0sGCSVOfs4Ca4Wh39eCS4grwQLX50DgFoOWPBXSgaQptgPdyTyJBVOfwO 13 | Q7xNu6rQZL1UfhJtnZgH4amP7B7gnvitAkvYVsxDIQKBgQDnox6gqenDw0IDd5Np 14 | +rFspnH7wKzcZBUn2CVVhUqLYpKEsKC3hw+/NhuB8swpvfb/X3HSCWJOO7k1UtK5 15 | vyh2GlcLkOwincVUTb5noSjIXdP22ZKYfErq7RfxeJU2vC4Q4w7OR46wzilyOhnq 16 | 0utqp5H+O8QQeP5ZwccgykhWEwKBgQDNPqQdiYpBpj8/2OJOke0fJpMGPm5Uhmjb 17 | PiAF76z+nGEQEYrFnx2C5lvDBdNDsWjKKgn9v2Caiepe0CXIVSWtLPhaym8ElRVu 18 | bvBrX+WbyAMY3GjRvKxMgZc2BbDlEl+A2OJF7U7+axjGf+qHjku+SVoTGsQfRmTi 19 | 0q/4YoEiBwKBgED7GK93mxWI5C72z4rugfF57AX08klNZkXsM+T+5H+fonzI0/1I 20 | TVmVwze8NhwLp4vSaarDaSmLRZS47FahUqd3Ei4zdJrtaZsRyDVoF3nGIJ1iQea3 21 | D4lCA/KAolNdjQq9t7yUhGoDG8tokza1/sbHxYPtnj4Ew03It6TyOkKzAoGBAKGL 22 | X2M8QYzgjEH1tFJBO1DcbLZqZy6ySE/gOxZRCmmwrhkWpZJcZb8FD06WBC3wslkE 23 | 8648do3MZaNQ05w1oJ0Im0XitbT2ntK4ERWCele9+uoTeZEznq3tBhZoLk5uwqrI 24 | inidiyJnKy35uqANWQKh+3q0A4/WOUvqpsSqy5b7AoGBAIY30tUUzptNbKJ6T1Gl 25 | KCR14DBplZGAfiYXp9XORK3JPCjTNsB2uP8m9JdVszkiX59n/wB4LriNsVTCPytj 26 | 5iLI5vJBPS3teyWHi0LMMgfjsjWb/GR45NRfjKT8OFnTFPEpF1pyf45qWB6vwAx0 27 | vhtim2vekWXIdXY4pVfRfsoW 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /src/tls/testdata/error.crt: -------------------------------------------------------------------------------- 1 | ERROR FORMAT -------------------------------------------------------------------------------- /src/tls/testdata/error.key: -------------------------------------------------------------------------------- 1 | ERROR FORMAT -------------------------------------------------------------------------------- /src/window.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// Maximum count of sequence number in the sliding window. 16 | const SEQ_NUM_WINDOW_SIZE: u64 = 128; 17 | 18 | /// A sliding window packet number for deduplication detection. 19 | /// See RFC 4303 Section 3.4.3 for a similar algorithm. 20 | #[derive(Clone, Copy, Default)] 21 | pub struct SeqNumWindow { 22 | /// The lowest sequence number 23 | lower: u64, 24 | 25 | /// A contiguous bitfield, where each bit corresponds to a sequence number 26 | window: u128, 27 | } 28 | 29 | impl SeqNumWindow { 30 | /// Insert an sequence number 31 | pub fn insert(&mut self, seq: u64) { 32 | // Sequence number is on the left end of the window. 33 | if seq < self.lower { 34 | return; 35 | } 36 | 37 | // Sequence number is on the right end of the window. 38 | if seq > self.upper() { 39 | let diff = seq - self.upper(); 40 | self.lower += diff; 41 | self.window = self.window.checked_shl(diff as u32).unwrap_or(0); 42 | } 43 | 44 | let mask = 1_u128 << (self.upper() - seq); 45 | self.window |= mask; 46 | } 47 | 48 | /// Check whether the packet number exist or not 49 | pub fn contains(&mut self, seq: u64) -> bool { 50 | // Sequence number is on the right end of the window. 51 | if seq > self.upper() { 52 | return false; 53 | } 54 | 55 | // Sequence number is on the left end of the window. 56 | if seq < self.lower { 57 | return true; 58 | } 59 | 60 | let mask = 1_u128 << (self.upper() - seq); 61 | self.window & mask != 0 62 | } 63 | 64 | /// Return the largest sequence number 65 | fn upper(&self) -> u64 { 66 | self.lower.saturating_add(SEQ_NUM_WINDOW_SIZE) - 1 67 | } 68 | } 69 | 70 | #[cfg(test)] 71 | mod tests { 72 | use super::*; 73 | 74 | #[test] 75 | fn seq_num_window_default() { 76 | let mut win = SeqNumWindow::default(); 77 | assert!(!win.contains(0)); 78 | assert!(!win.contains(1)); 79 | } 80 | 81 | #[test] 82 | fn seq_num_window_insert() { 83 | let mut win = SeqNumWindow::default(); 84 | win.insert(0); 85 | assert!(win.contains(0)); 86 | assert!(!win.contains(1)); 87 | 88 | win.insert(1); 89 | assert!(win.contains(0)); 90 | assert!(win.contains(1)); 91 | 92 | win.insert(3); 93 | assert!(win.contains(0)); 94 | assert!(win.contains(1)); 95 | assert!(!win.contains(2)); 96 | assert!(win.contains(3)); 97 | assert!(!win.contains(200)); 98 | } 99 | 100 | #[test] 101 | fn seq_num_window_insert_slide() { 102 | let mut win = SeqNumWindow::default(); 103 | 104 | win.insert(10); 105 | assert!(!win.contains(0)); 106 | assert!(win.contains(10)); 107 | 108 | win.insert(138); 109 | assert!(win.contains(138)); 110 | assert!(!win.contains(137)); 111 | assert!(win.contains(10)); 112 | assert!(win.contains(0)); 113 | 114 | win.insert(8); 115 | assert!(win.contains(8)); 116 | } 117 | 118 | #[test] 119 | fn seq_num_window_insert_max() { 120 | let mut win = SeqNumWindow::default(); 121 | let max_seq = std::u64::MAX - 1; 122 | win.insert(max_seq); 123 | assert!(win.contains(0)); 124 | assert!(win.contains(max_seq)); 125 | assert!(!win.contains(max_seq - 1)); 126 | assert!(win.contains(max_seq - 128)); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /tools/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tquic_tools" 3 | version = "1.6.0" 4 | edition = "2021" 5 | rust-version = "1.70.0" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/tencent/tquic" 8 | homepage = "https://tquic.net" 9 | description = "TQUIC client and server tools" 10 | keywords = ["quic"] 11 | categories = ["network-programming"] 12 | 13 | [dependencies] 14 | bytes = "1" 15 | url = "1" 16 | log = "0.4" 17 | mio = { version = "0.8", features = ["net", "os-poll"] } 18 | env_logger = "0.9" 19 | clap = { version = "=4.2.5", features = ["derive"] } 20 | rustc-hash = "1.1" 21 | slab = "0.4" 22 | rand = "0.8.5" 23 | statrs = "0.16" 24 | signal-hook = "0.3.17" 25 | tquic = { path = "..", version = "1.5.0"} 26 | 27 | [target."cfg(unix)".dependencies] 28 | jemallocator = { version = "0.5", package = "tikv-jemallocator" } 29 | 30 | [lib] 31 | crate-type = ["lib"] 32 | path = "src/common.rs" 33 | 34 | [[bin]] 35 | name="tquic_client" 36 | path="src/bin/tquic_client.rs" 37 | 38 | [[bin]] 39 | name="tquic_server" 40 | path="src/bin/tquic_server.rs" 41 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # TQUIC tools 2 | 3 | [TQUIC](https://github.com/Tencent/tquic) is a high-performance, lightweight, and cross-platform library for the [IETF QUIC](https://datatracker.ietf.org/wg/quic/bout/) protocol. 4 | 5 | The crate contains client and server tools based on TQUIC: 6 | - tquic_client: A QUIC and HTTP/3 client. It's also an HTTP/3 benchmarking tool. 7 | - tquic_server: A QUIC and HTTP/3 static file server. 8 | 9 | 10 | ## Installation 11 | 12 | ``` 13 | cargo install tquic_tools 14 | ``` 15 | 16 | 17 | ## Documentation 18 | 19 | - [English version](https://tquic.net/docs/getting_started/demo/) 20 | - [Chinese version](https://tquic.net/zh/docs/getting_started/demo/) 21 | 22 | 23 | ## License 24 | 25 | The project is under the Apache 2.0 license. 26 | -------------------------------------------------------------------------------- /tools/script/tquic_qvis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This tool is used to convert qlogs generated by tquic tools from JSON-SEQ 4 | # format to JSON format, and make other changes to be compatible with qvis. 5 | # See https://qvis.quictools.info 6 | 7 | # Check whether jq is installed 8 | if ! command -v jq &> /dev/null 9 | then 10 | echo "Please install jq to use this script." 11 | echo "See https://jqlang.github.io/jq/download/" 12 | exit 1 13 | fi 14 | 15 | # Check whether a file name is provided 16 | if [ "$#" -ne 1 ]; then 17 | echo "Usage: $0 " 18 | echo "Description: convert qlog from JSON-SEQ to JSON format compatible with qvis" 19 | exit 1 20 | fi 21 | 22 | # Check whether the file exists 23 | if [ ! -f "$1" ]; then 24 | echo "Error: File '$1' not found." 25 | exit 1 26 | fi 27 | 28 | # Convert to JSON format 29 | OUT="$1.qvis.json" 30 | sed 's/^\x1e//' $1 | jq -s '.[1:] as $events | .[0] | .trace.events=$events | .traces=[.trace] | del(.trace) | .qlog_format="JSON"' > $OUT 31 | 32 | # Change for backward compatibility 33 | # Note qvis reportedly does not plan to implement qlog 0.4 and will continue 34 | # using 0.3 until a 1.0 RC is specced. 35 | sed -i -e 's/name": "quic:/name": "transport:/' -e 's/"qlog_version": "0.4"/"qlog_version": "0.3"/' $OUT 36 | -------------------------------------------------------------------------------- /tools/script/tquic_time_cwnd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This tool is used to analyze TQUIC debug logs and produce a time-cwnd figure 4 | # for the specified QUIC connection. 5 | 6 | import re 7 | 8 | from datetime import datetime 9 | import argparse 10 | import matplotlib.pyplot as plt 11 | 12 | 13 | def parse_log(log_file, id): 14 | with open(log_file, "r") as file: 15 | log_data = file.readlines() 16 | 17 | timestamps = [] 18 | inflights = [] 19 | cwnds = [] 20 | 21 | # Refine the regular expression to match timestamps and cwnds 22 | timestamp_pattern = re.compile(r"\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3})Z") 23 | cwnd_format = r"{} [a-zA-Z]* BEGIN_ACK inflight=(\d+) cwnd=(\d+)" 24 | cwnd_pattern = re.compile(cwnd_format.format(id)) 25 | 26 | for line in log_data: 27 | timestamp_match = timestamp_pattern.search(line) 28 | if not timestamp_match: 29 | continue 30 | 31 | cwnd_match = cwnd_pattern.search(line) 32 | if not cwnd_match: 33 | continue 34 | 35 | timestamp_str = timestamp_match.group(1) 36 | timestamp = datetime.strptime(timestamp_str, "%Y-%m-%dT%H:%M:%S.%f") 37 | inflight = int(cwnd_match.group(1)) 38 | cwnd = int(cwnd_match.group(2)) 39 | timestamps.append(timestamp) 40 | inflights.append(inflight) 41 | cwnds.append(cwnd) 42 | 43 | return timestamps, inflights, cwnds 44 | 45 | 46 | def plot_offsets(timestamps, inflights, cwnds, connection_path_id): 47 | # Set output file name 48 | ids = connection_path_id.split("-") 49 | cid = ids[1] 50 | pid = ids[2] 51 | output_file_name = "tquic_time_cwnd_{}_{}.png".format(cid, pid) 52 | 53 | plt.figure(figsize=(20, 6)) 54 | plt.plot(timestamps, inflights, label="inflight", linestyle="-", linewidth=0.5) 55 | plt.plot(timestamps, cwnds, label="cwnd", linestyle="-", linewidth=0.5) 56 | plt.xlabel("Time") 57 | plt.ylabel("Cwnd/Inflight") 58 | plt.title(f"Congestion window by Time in Connection {cid} Path {pid}") 59 | plt.legend() 60 | plt.gca().xaxis.set_major_formatter( 61 | plt.matplotlib.dates.DateFormatter("%H:%M:%S.%f") 62 | ) 63 | plt.savefig(output_file_name) 64 | print("Found %d items, figure %s" % (len(timestamps), output_file_name)) 65 | 66 | 67 | if __name__ == "__main__": 68 | # Set up the command line argument parser 69 | parser = argparse.ArgumentParser( 70 | description="Analyze TQUIC logs to get the relationship between cwnd/inflight and time." 71 | ) 72 | parser.add_argument( 73 | "-l", 74 | "--log_file", 75 | type=str, 76 | help="path to the TQUIC debug log file", 77 | required=True, 78 | ) 79 | parser.add_argument( 80 | "-c", 81 | "--connection_path_id", 82 | type=str, 83 | help="connection path id, eg. SERVER-c6d45bc005585f42-0", 84 | required=True, 85 | ) 86 | args = parser.parse_args() 87 | 88 | # Calling with command-line arguments 89 | timestamps, inflights, cwnds = parse_log(args.log_file, args.connection_path_id) 90 | plot_offsets(timestamps, inflights, cwnds, args.connection_path_id) 91 | -------------------------------------------------------------------------------- /tools/script/tquic_time_offset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This tool is used to analyze TQUIC debug logs and produce a time-offset figure 4 | # for the specified QUIC stream. 5 | 6 | import re 7 | 8 | from datetime import datetime 9 | import argparse 10 | import matplotlib.pyplot as plt 11 | 12 | STREAM_SEND_FORMAT = ( 13 | r"{} sent packet OneRTT.*?STREAM id={} off=(\d+) len=\d+ fin=(?:true|false)" 14 | ) 15 | STREAM_RECV_FORMAT = r"{} recv frame STREAM id={} off=(\d+) len=\d+ fin=(?:true|false)" 16 | 17 | 18 | def parse_log(log_file, cid, stream_id, recv): 19 | with open(log_file, "r") as file: 20 | log_data = file.readlines() 21 | 22 | timestamps = [] 23 | offsets = [] 24 | 25 | # Refine the regular expression to match timestamps and stream offsets 26 | timestamp_pattern = re.compile(r"\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3})Z") 27 | stream_format = STREAM_RECV_FORMAT if recv else STREAM_SEND_FORMAT 28 | connection_stream_pattern = re.compile(stream_format.format(cid, stream_id)) 29 | 30 | for line in log_data: 31 | timestamp_match = timestamp_pattern.search(line) 32 | if not timestamp_match: 33 | continue 34 | 35 | connection_stream_frame_match = connection_stream_pattern.search(line) 36 | if not connection_stream_frame_match: 37 | continue 38 | 39 | timestamp_str = timestamp_match.group(1) 40 | timestamp = datetime.strptime(timestamp_str, "%Y-%m-%dT%H:%M:%S.%f") 41 | current_offset = int(connection_stream_frame_match.group(1)) 42 | timestamps.append(timestamp) 43 | offsets.append(current_offset) 44 | 45 | return timestamps, offsets 46 | 47 | 48 | def plot_offsets(timestamps, offsets, connection_trace_id, stream_id): 49 | # Get connection id and set output file name 50 | cid = connection_trace_id.split("-")[1] 51 | output_file_name = "tquic_time_offset_{}_{}.png".format(cid, stream_id) 52 | 53 | plt.figure(figsize=(20, 6)) 54 | plt.plot(timestamps, offsets, marker=".", linewidth=0.5) 55 | plt.xlabel("Time") 56 | plt.ylabel("Stream Offset") 57 | plt.title(f"Stream {stream_id} Offset by Time in Connection {cid}") 58 | plt.gca().xaxis.set_major_formatter( 59 | plt.matplotlib.dates.DateFormatter("%H:%M:%S.%f") 60 | ) 61 | plt.savefig(output_file_name) 62 | print("Found %d items, figure %s" % (len(timestamps), output_file_name)) 63 | 64 | 65 | if __name__ == "__main__": 66 | # Set up the command line argument parser 67 | parser = argparse.ArgumentParser( 68 | description="Analyze TQUIC logs to get the relationship between stream offset and time." 69 | ) 70 | parser.add_argument( 71 | "-l", 72 | "--log_file", 73 | type=str, 74 | help="path to the TQUIC debug log file", 75 | required=True, 76 | ) 77 | parser.add_argument( 78 | "-c", 79 | "--connection_trace_id", 80 | type=str, 81 | help="connection trace id, eg. SERVER-c6d45bc005585f42", 82 | required=True, 83 | ) 84 | parser.add_argument( 85 | "-s", 86 | "--stream_id", 87 | type=int, 88 | help="stream id (default 0), eg. 0", 89 | default=0, 90 | ) 91 | parser.add_argument( 92 | "-r", 93 | "--recv", 94 | type=bool, 95 | help="recv side instead of send side (default false)", 96 | default=False, 97 | ) 98 | args = parser.parse_args() 99 | 100 | # Calling with command-line arguments 101 | timestamps, offsets = parse_log( 102 | args.log_file, args.connection_trace_id, args.stream_id, args.recv 103 | ) 104 | plot_offsets(timestamps, offsets, args.connection_trace_id, args.stream_id) 105 | -------------------------------------------------------------------------------- /tools/src/common.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 The TQUIC Authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::io::ErrorKind; 16 | use std::net::SocketAddr; 17 | 18 | use clap::builder::PossibleValue; 19 | use clap::ValueEnum; 20 | use env_logger::Target; 21 | use log::*; 22 | use mio::net::UdpSocket; 23 | use mio::Interest; 24 | use mio::Registry; 25 | use mio::Token; 26 | use rustc_hash::FxHashMap; 27 | use slab::Slab; 28 | 29 | use tquic::PacketInfo; 30 | use tquic::PacketSendHandler; 31 | 32 | pub type Result = std::result::Result>; 33 | 34 | /// Supported application protocols. 35 | #[derive(Clone, Copy, Default, PartialEq, Debug)] 36 | pub enum ApplicationProto { 37 | /// Proto for QUIC interop, see https://github.com/quic-interop/quic-interop-runner 38 | Interop, 39 | 40 | /// HTTP/0.9, see https://http.dev/0.9 41 | Http09, 42 | 43 | /// HTTP/3, see https://www.rfc-editor.org/rfc/rfc9114.html 44 | #[default] 45 | H3, 46 | } 47 | 48 | impl ApplicationProto { 49 | /// Create a new ApplicationProto from byte slice. 50 | pub fn from_slice(proto: &[u8]) -> Self { 51 | match proto { 52 | b"hq-interop" => Self::Interop, 53 | b"http/0.9" => Self::Http09, 54 | b"h3" => Self::H3, 55 | _ => unreachable!(), 56 | } 57 | } 58 | 59 | /// Convert an ApplicationProto into a byte slice. 60 | pub fn to_slice(&self) -> &[u8] { 61 | match self { 62 | Self::Interop => b"hq-interop", 63 | Self::Http09 => b"http/0.9", 64 | Self::H3 => b"h3", 65 | } 66 | } 67 | 68 | /// Convert an ApplicationProto slice to a two-dimension byte vector. 69 | pub fn convert_to_vec(protos: &[Self]) -> Vec> { 70 | protos 71 | .iter() 72 | .map(|proto| proto.to_slice().to_vec()) 73 | .collect() 74 | } 75 | } 76 | 77 | impl ValueEnum for ApplicationProto { 78 | fn to_possible_value(&self) -> Option { 79 | Some(match self { 80 | Self::Interop => PossibleValue::new("hq-interop"), 81 | Self::Http09 => PossibleValue::new("http/0.9"), 82 | Self::H3 => PossibleValue::new("h3"), 83 | }) 84 | } 85 | 86 | fn value_variants<'a>() -> &'a [Self] { 87 | &[Self::Interop, Self::Http09, Self::H3] 88 | } 89 | } 90 | 91 | /// UDP socket wrapper for QUIC 92 | pub struct QuicSocket { 93 | /// The underlying UDP sockets for QUIC Endpoint. 94 | socks: Slab, 95 | 96 | /// The mappings between local address and socket identifier. 97 | addrs: FxHashMap, 98 | 99 | /// Local address of the initial socket. 100 | local_addr: SocketAddr, 101 | } 102 | 103 | impl QuicSocket { 104 | pub fn new(local: &SocketAddr, registry: &Registry) -> Result { 105 | let mut socks = Slab::new(); 106 | let mut addrs = FxHashMap::default(); 107 | 108 | let socket = UdpSocket::bind(*local)?; 109 | let local_addr = socket.local_addr()?; 110 | let sid = socks.insert(socket); 111 | addrs.insert(local_addr, sid); 112 | 113 | let socket = socks.get_mut(sid).unwrap(); 114 | registry.register(socket, Token(sid), Interest::READABLE)?; 115 | 116 | Ok(Self { 117 | socks, 118 | addrs, 119 | local_addr, 120 | }) 121 | } 122 | 123 | /// Return the local address of the initial socket. 124 | pub fn local_addr(&self) -> SocketAddr { 125 | self.local_addr 126 | } 127 | 128 | /// Add additional socket binding with given local address. 129 | pub fn add(&mut self, local: &SocketAddr, registry: &Registry) -> Result { 130 | let socket = UdpSocket::bind(*local)?; 131 | let local_addr = socket.local_addr()?; 132 | let sid = self.socks.insert(socket); 133 | self.addrs.insert(local_addr, sid); 134 | 135 | let socket = self.socks.get_mut(sid).unwrap(); 136 | registry.register(socket, Token(sid), Interest::READABLE)?; 137 | Ok(local_addr) 138 | } 139 | 140 | /// Delete socket binding with given local address. 141 | pub fn del(&mut self, local: &SocketAddr, registry: &Registry) -> Result<()> { 142 | let sid = match self.addrs.get(local) { 143 | Some(sid) => *sid, 144 | None => return Ok(()), 145 | }; 146 | 147 | let socket = match self.socks.get_mut(sid) { 148 | Some(socket) => socket, 149 | None => return Ok(()), 150 | }; 151 | 152 | registry.deregister(socket)?; 153 | self.socks.remove(sid); 154 | Ok(()) 155 | } 156 | 157 | /// Receive data from the socket. 158 | pub fn recv_from( 159 | &self, 160 | buf: &mut [u8], 161 | token: mio::Token, 162 | ) -> std::io::Result<(usize, SocketAddr, SocketAddr)> { 163 | let socket = match self.socks.get(token.0) { 164 | Some(socket) => socket, 165 | None => return Err(std::io::Error::new(ErrorKind::Other, "invalid token")), 166 | }; 167 | 168 | match socket.recv_from(buf) { 169 | Ok((len, remote)) => Ok((len, socket.local_addr()?, remote)), 170 | Err(e) => Err(e), 171 | } 172 | } 173 | 174 | /// Send data on the socket to the given address. 175 | /// Note: packets with unknown src address are dropped. 176 | pub fn send_to(&self, buf: &[u8], src: SocketAddr, dst: SocketAddr) -> std::io::Result { 177 | let sid = match self.addrs.get(&src) { 178 | Some(sid) => sid, 179 | None => { 180 | debug!("send_to drop packet with unknown address {:?}", src); 181 | return Ok(buf.len()); 182 | } 183 | }; 184 | 185 | match self.socks.get(*sid) { 186 | Some(socket) => Ok(socket.send_to(buf, dst)?), 187 | None => { 188 | debug!("send_to drop packet with unknown address {:?}", src); 189 | Ok(buf.len()) 190 | } 191 | } 192 | } 193 | } 194 | 195 | impl PacketSendHandler for QuicSocket { 196 | fn on_packets_send(&self, pkts: &[(Vec, PacketInfo)]) -> tquic::Result { 197 | let mut count = 0; 198 | for (pkt, info) in pkts { 199 | if let Err(e) = self.send_to(pkt, info.src, info.dst) { 200 | if e.kind() == std::io::ErrorKind::WouldBlock { 201 | debug!("socket send would block"); 202 | return Ok(count); 203 | } 204 | return Err(tquic::Error::InvalidOperation(format!( 205 | "socket send_to(): {:?}", 206 | e 207 | ))); 208 | } 209 | debug!("written {} bytes", pkt.len()); 210 | count += 1; 211 | } 212 | Ok(count) 213 | } 214 | } 215 | 216 | /// Get the target for the log output. 217 | pub fn log_target(log_file: &Option) -> Result { 218 | if let Some(log_file) = log_file { 219 | if let Ok(file) = std::fs::OpenOptions::new() 220 | .create(true) 221 | .append(true) 222 | .open(log_file) 223 | { 224 | return Ok(Target::Pipe(Box::new(file))); 225 | } 226 | return Err(format!("create log file {:?} failed", log_file).into()); 227 | } 228 | 229 | Ok(Target::Stderr) 230 | } 231 | -------------------------------------------------------------------------------- /tools/tests/tquic_tools_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) 2024 The TQUIC Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # This simple script contains additional end-to-end test cases for tquic tools. 18 | # When conditions permit, it's plan to implement all of the following test cases 19 | # in the `github.com/tquic-group/quic-interop-runner` repo. 20 | 21 | set -e 22 | 23 | BIN_DIR="./" 24 | TEST_DIR="./test-`date +%Y%m%d%H%M%S`" 25 | TEST_CASES="multipath_minrtt,multipath_roundrobin,multipath_redundant" 26 | TEST_PID="$$" 27 | TEST_FILE="10M" 28 | PATH_NUM=4 29 | LOG_LEVEL="debug" 30 | CLI_OPTIONS="" 31 | SRV_OPTIONS="" 32 | EXIT_CODE=0 33 | 34 | cleanup() { 35 | set +e 36 | pkill -P $TEST_PID 37 | echo "exit with" $EXIT_CODE 38 | exit $EXIT_CODE 39 | } 40 | 41 | show_help() { 42 | echo "Usage: $0 [options]" 43 | echo " -b, Set the directory of tquic_client/tquic_server." 44 | echo " -w, Set the workring directory for testing." 45 | echo " -l, List all supported test cases." 46 | echo " -t, Run the specified test cases." 47 | echo " -f, File size for test cases, eg. 10M" 48 | echo " -p, Path number for test cases, eg. 4" 49 | echo " -g, Log level, eg. debug" 50 | echo " -c, Extra tquic_client options, eg. ~~cid-len 10" 51 | echo " -s, Extra tquic_server options, eg. ~~cid-len 10" 52 | echo " -h, Display this help and exit." 53 | } 54 | 55 | while getopts ":b:w:t:f:p:g:c:s:lh" opt; do 56 | case $opt in 57 | b) 58 | BIN_DIR="$OPTARG" 59 | ;; 60 | w) 61 | TEST_DIR="$OPTARG" 62 | ;; 63 | t) 64 | TEST_CASES="$OPTARG" 65 | ;; 66 | f) 67 | TEST_FILE="$OPTARG" 68 | ;; 69 | p) 70 | PATH_NUM="$OPTARG" 71 | ;; 72 | g) 73 | LOG_LEVEL="$OPTARG" 74 | ;; 75 | c) 76 | CLI_OPTIONS="${OPTARG//\~/-}" 77 | ;; 78 | s) 79 | SRV_OPTIONS="${OPTARG//\~/-}" 80 | ;; 81 | l) 82 | echo $TEST_CASES 83 | exit 0 84 | ;; 85 | h) 86 | show_help 87 | exit 0 88 | ;; 89 | \?) 90 | echo "Invalid option: -$OPTARG" >&2 91 | show_help 92 | exit 1 93 | ;; 94 | :) 95 | echo "Option -$OPTARG requires an argument." >&2 96 | exit 1 97 | ;; 98 | esac 99 | done 100 | 101 | # Ensure that all child processes have exited. 102 | trap 'cleanup' EXIT 103 | 104 | if [[ ! -f "$BIN_DIR/tquic_client" || ! -f "$BIN_DIR/tquic_server" ]]; then 105 | echo "Not found tquic_client/tquic_server. Please specify the directory for them by '-b' option." 106 | show_help 107 | exit 108 | fi 109 | 110 | CID_LIMIT=$(( $PATH_NUM * 2 )) 111 | 112 | generate_cert() { 113 | local cert_dir="$1/cert" 114 | mkdir -p $cert_dir 115 | openssl genpkey -algorithm RSA -out $cert_dir/cert.key -pkeyopt rsa_keygen_bits:2048 -quiet 116 | openssl req -new -key $cert_dir/cert.key -out $cert_dir/cert.csr -subj "/C=CN/ST=beijing/O=tquic/CN=example.org" 117 | openssl x509 -req -in $cert_dir/cert.csr -signkey $cert_dir/cert.key -out $cert_dir/cert.crt 118 | } 119 | 120 | generate_files() { 121 | local data_dir="$1/data" 122 | mkdir -p $data_dir 123 | dd if=/dev/urandom of=$data_dir/$TEST_FILE bs=$TEST_FILE count=1 124 | } 125 | 126 | test_multipath() { 127 | local test_dir=$1 128 | local algor=$2 129 | echo "[-] Running multipath test for $algor" 130 | 131 | # prepare environment 132 | local cert_dir="$test_dir/cert" 133 | local data_dir="$test_dir/data" 134 | local dump_dir="$test_dir/dump" 135 | local qlog_dir="$test_dir/qlog" 136 | 137 | generate_cert $test_dir 138 | generate_files $test_dir 139 | 140 | # start tquic server 141 | RUST_BACKTRACE=1 $BIN_DIR/tquic_server -l 127.0.8.8:8443 --enable-multipath --multipath-algor $algor \ 142 | --cert $cert_dir/cert.crt --key $cert_dir/cert.key --root $data_dir \ 143 | --active-cid-limit $CID_LIMIT --log-file $test_dir/server.log --log-level $LOG_LEVEL \ 144 | $SRV_OPTIONS & 145 | server_pid=$! 146 | 147 | # start tquic client 148 | mkdir -p $dump_dir 149 | local_addresses=`seq -s, -f "127.0.0.%g" 1 $PATH_NUM` 150 | RUST_BACKTRACE=1 $BIN_DIR/tquic_client -c 127.0.8.8:8443 --enable-multipath --multipath-algor $algor \ 151 | --local-addresses $local_addresses --active-cid-limit $CID_LIMIT \ 152 | --qlog-dir $qlog_dir --log-file $test_dir/client.log --log-level $LOG_LEVEL \ 153 | --dump-dir $dump_dir $CLI_OPTIONS \ 154 | https://example.org/$TEST_FILE 155 | 156 | # check files 157 | if ! cmp -s $dump_dir/$TEST_FILE $data_dir/$TEST_FILE; then 158 | echo "Files not same $dump_dir/$TEST_FILE:$data_dir/$TEST_FILE" 159 | EXIT_CODE=100 160 | exit $EXIT_CODE 161 | fi 162 | 163 | # check packets received 164 | pnum=`grep "recv packet OneRTT" $test_dir/client.log | grep "local=.*" -o | sort | uniq -c | tee /dev/stderr | wc -l` 165 | if [ $pnum != $PATH_NUM ]; then 166 | echo "Not all path ($pnum/$PATH_NUM) received packets" 167 | EXIT_CODE=101 168 | exit $EXIT_CODE 169 | fi 170 | 171 | # clean up 172 | kill $server_pid 173 | echo -e "Test $algor OK\n" 174 | } 175 | 176 | echo "$TEST_CASES" | sed 's/,/\n/g' | while read -r TEST_CASE; do 177 | case $TEST_CASE in 178 | multipath_minrtt) 179 | test_multipath "$TEST_DIR/minrtt" minrtt 180 | ;; 181 | multipath_redundant) 182 | test_multipath "$TEST_DIR/redundant" redundant 183 | ;; 184 | multipath_roundrobin) 185 | test_multipath "$TEST_DIR/roundrobin" roundrobin 186 | ;; 187 | *) 188 | echo "[x] Unknown test case $TEST_CASE" 189 | ;; 190 | esac 191 | done 192 | 193 | -------------------------------------------------------------------------------- /typos.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | binary = false 3 | check-filename = true 4 | 5 | [default.extend-identifiers] 6 | referes = "referes" 7 | StatuS = "StatuS" 8 | 9 | [default.extend-words] 10 | # ECN Capable Transport 11 | ect = "ect" 12 | # Packet Number 13 | pn = "pn" 14 | # Retransmission Timeout 15 | RTO = "RTO" 16 | 17 | [files] 18 | extend-exclude = [ 19 | "*.snap", 20 | "*.bin", 21 | "*.der", 22 | "*.pem", 23 | "LICENSE", 24 | "**/target/**/*", 25 | "**/deps/**/*", 26 | "**/corpus/**/*", 27 | ] 28 | --------------------------------------------------------------------------------