Compare commits

..

198 Commits

Author SHA1 Message Date
Tatsuhiro Tsujikawa
af36d716b9 Merge pull request #2657 from nghttp2/nghttpx-tcp-defer-accept
nghttpx: Choose the sensible value for TCP_DEFER_ACCEPT
2026-03-24 23:28:09 +09:00
Tatsuhiro Tsujikawa
6b75efe257 nghttpx: Choose the sensible value for TCP_DEFER_ACCEPT 2026-03-24 22:35:50 +09:00
Tatsuhiro Tsujikawa
7310f0583c Merge pull request #2656 from nghttp2/nghttpx-quic-utils-span
nghttpx: Refactor QUIC utils with std::span
2026-03-24 22:32:37 +09:00
Tatsuhiro Tsujikawa
58ef096c2f nghttpx: Refactor QUIC utils with std::span 2026-03-24 22:00:00 +09:00
Tatsuhiro Tsujikawa
27198cfbd9 Merge pull request #2655 from nghttp2/nghttpx-connection-read-span
Nghttpx connection read span
2026-03-24 21:47:58 +09:00
Tatsuhiro Tsujikawa
b4cccf03d8 nghttpx: Make Connection::read_clear accept std::span 2026-03-24 21:24:13 +09:00
Tatsuhiro Tsujikawa
b4a5bc1830 nghttpx: Make Connection::read_tls accept std::span 2026-03-24 21:24:13 +09:00
Tatsuhiro Tsujikawa
4d0a6e4d00 Merge pull request #2654 from nghttp2/nghttpx-connection-write-span
Nghttpx connection write span
2026-03-24 21:16:41 +09:00
Tatsuhiro Tsujikawa
a74e8d61fa nghttpx: Make Connection::write_clear accept std::span 2026-03-24 20:55:11 +09:00
Tatsuhiro Tsujikawa
31d6973b2f nghttpx: Make Connection::write_tls accept std::span 2026-03-24 20:55:11 +09:00
Tatsuhiro Tsujikawa
e82b41d6b8 Merge pull request #2653 from nghttp2/nghttpx-livecheck-span
nghttpx: Adopt std::span for LiveCheck read path
2026-03-24 19:41:36 +09:00
Tatsuhiro Tsujikawa
f4139aa4cf nghttpx: Adopt std::span for LiveCheck read path 2026-03-24 18:08:26 +09:00
Tatsuhiro Tsujikawa
b17a692a78 Merge pull request #2652 from nghttp2/dependabot/github_actions/microsoft/setup-msbuild-3
build(deps): bump microsoft/setup-msbuild from 2 to 3
2026-03-24 01:33:16 +09:00
Tatsuhiro Tsujikawa
da755b2f05 Merge pull request #2651 from nghttp2/nghttpx-api-dconn-partial-write
nghttpx: Deal with partial write in API downstream connection
2026-03-24 00:02:04 +09:00
dependabot[bot]
6f55c84a13 build(deps): bump microsoft/setup-msbuild from 2 to 3
Bumps [microsoft/setup-msbuild](https://github.com/microsoft/setup-msbuild) from 2 to 3.
- [Release notes](https://github.com/microsoft/setup-msbuild/releases)
- [Commits](https://github.com/microsoft/setup-msbuild/compare/v2...v3)

---
updated-dependencies:
- dependency-name: microsoft/setup-msbuild
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-23 14:33:03 +00:00
Tatsuhiro Tsujikawa
d4b813b862 nghttpx: Deal with partial write in API downstream connection 2026-03-23 23:14:01 +09:00
Tatsuhiro Tsujikawa
99065afe91 Merge pull request #2650 from nghttp2/nghttpx-downstream-conn-span
nghttpx: Modernize downstream connection with std::span
2026-03-23 23:13:19 +09:00
Tatsuhiro Tsujikawa
4f7f90eaa1 nghttpx: Modernize downstream connection with std::span 2026-03-23 22:46:33 +09:00
Tatsuhiro Tsujikawa
d0ad6a2f90 Merge pull request #2649 from nghttp2/nghttpx-upstream-span
nghttpx: Use std::span for upstream interface
2026-03-23 22:04:19 +09:00
Tatsuhiro Tsujikawa
4aff44b6b9 nghttpx: Use std::span for upstream interface 2026-03-23 21:41:06 +09:00
Tatsuhiro Tsujikawa
2d9ce2eab1 Merge pull request #2647 from nghttp2/src-iterator-range-concept
src: Review the use of iterator and range concepts
2026-03-22 23:38:56 +09:00
Tatsuhiro Tsujikawa
dc1ac54552 src: Review the use of iterator and range concepts 2026-03-22 23:17:34 +09:00
Tatsuhiro Tsujikawa
35383a4f0b Merge pull request #2646 from nghttp2/src-as_uint8_span-auto
src: Simplify as_uint8_span with auto
2026-03-22 22:43:28 +09:00
Tatsuhiro Tsujikawa
52f895500b src: Simplify as_uint8_span with auto 2026-03-22 21:48:41 +09:00
Tatsuhiro Tsujikawa
5c947628d5 Merge pull request #2645 from nghttp2/src-override
src: Adopt override keyword
2026-03-19 01:07:06 +09:00
Tatsuhiro Tsujikawa
483ea05120 src: Adopt override keyword 2026-03-18 23:54:25 +09:00
Tatsuhiro Tsujikawa
745b025e07 Merge pull request #2644 from nghttp2/buffer-write
src: Refactor Buffer::write with std::span
2026-03-18 22:35:28 +09:00
Tatsuhiro Tsujikawa
2d2c666df0 src: Refactor Buffer::write with std::span 2026-03-18 21:55:48 +09:00
Tatsuhiro Tsujikawa
864ba96694 Bump library version due to the patch release 2026-03-18 20:37:44 +09:00
Tatsuhiro Tsujikawa
310c239817 Merge pull request #2643 from nghttp2/add-missing-iframe-state-validation
Add missing iframe state validation
2026-03-18 19:04:50 +09:00
Tatsuhiro Tsujikawa
caed460cd7 Add tests for iframe->state validation 2026-03-18 18:40:54 +09:00
Tatsuhiro Tsujikawa
92ad06a703 Fix missing iframe->state validations to avoid assertion failure 2026-03-17 22:42:56 +09:00
Tatsuhiro Tsujikawa
a86693f273 Merge pull request #2642 from nghttp2/nghttp-buf-outside-loop
nghttp: Move span creation out of loop
2026-03-17 21:30:33 +09:00
Tatsuhiro Tsujikawa
41d3be9070 nghttp: Move span creation out of loop 2026-03-17 20:39:57 +09:00
Tatsuhiro Tsujikawa
3469972630 Merge pull request #2641 from nghttp2/h2load-read-buf-span
h2load: Use span for reading
2026-03-17 19:46:45 +09:00
Tatsuhiro Tsujikawa
285cf1c884 h2load: Use span for reading 2026-03-17 19:21:53 +09:00
Tatsuhiro Tsujikawa
68016b9982 Merge pull request #2639 from nghttp2/nghttp-span
nghttp: Refactor with std::span
2026-03-17 18:55:20 +09:00
Tatsuhiro Tsujikawa
9727c13113 nghttp: Refactor with std::span 2026-03-17 18:32:17 +09:00
Tatsuhiro Tsujikawa
19898e103b Merge pull request #2640 from nghttp2/dependabot/go_modules/golang.org/x/net-0.52.0
build(deps): bump golang.org/x/net from 0.51.0 to 0.52.0
2026-03-17 01:01:46 +09:00
dependabot[bot]
461c467ab2 build(deps): bump golang.org/x/net from 0.51.0 to 0.52.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.51.0 to 0.52.0.
- [Commits](https://github.com/golang/net/compare/v0.51.0...v0.52.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.52.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-16 14:33:26 +00:00
Tatsuhiro Tsujikawa
74c131b865 Merge pull request #2638 from nghttp2/nghttpd-span
nghttpd: Refactor with std::span
2026-03-16 22:30:59 +09:00
Tatsuhiro Tsujikawa
f6431d721b nghttpd: Refactor with std::span 2026-03-16 21:50:59 +09:00
Tatsuhiro Tsujikawa
9e2252a146 Merge pull request #2637 from nghttp2/h2load-span
h2load: Refactor with std::span
2026-03-16 21:42:17 +09:00
Tatsuhiro Tsujikawa
a79b59bd85 h2load: Refactor with std::span 2026-03-16 21:12:18 +09:00
Tatsuhiro Tsujikawa
74dafcdc4e Merge pull request #2636 from nghttp2/h2load-template-sdstat
h2load: Print integral samples as integral
2026-03-16 21:01:37 +09:00
Tatsuhiro Tsujikawa
f12a7e7ce3 h2load: Print integral samples as integral 2026-03-16 20:38:39 +09:00
Tatsuhiro Tsujikawa
579d55dae2 Merge pull request #2634 from nghttp2/src-refine-defer
src: Refine Defer
2026-03-16 18:11:26 +09:00
Tatsuhiro Tsujikawa
b367158b7b Merge pull request #2633 from nghttp2/src-pass-by-value
Src pass by value
2026-03-16 17:58:37 +09:00
Tatsuhiro Tsujikawa
2ce0dfaf00 src: Refine Defer 2026-03-15 22:53:05 +09:00
Tatsuhiro Tsujikawa
5604194a3b src: Pass and return std::string_view by value 2026-03-15 21:24:31 +09:00
Tatsuhiro Tsujikawa
cf2d5b93e2 src: Pass std::span without const 2026-03-15 20:53:17 +09:00
Tatsuhiro Tsujikawa
af2f0f5c33 Merge pull request #2629 from nghttp2/h2load-json-output
h2load: Output the measurement results in JSON
2026-03-15 20:51:52 +09:00
Tatsuhiro Tsujikawa
db091f096d Merge pull request #2632 from nghttp2/nghttpx-h1-host-extra-validation
nghttpx: More strict validation for h1 host
2026-03-14 17:15:34 +09:00
Tatsuhiro Tsujikawa
4f3eaff3c4 nghttpx: More strict validation for h1 host 2026-03-14 16:41:39 +09:00
Tatsuhiro Tsujikawa
b92c98e8a5 Merge pull request #2631 from nghttp2/nghttpx-h1-path-extra-validation
nghttpx: Add extra validation for non-regular path for HTTP/1.1
2026-03-14 15:41:18 +09:00
Tatsuhiro Tsujikawa
21b1ae3ed5 nghttpx: Add extra validation for non-regular path for HTTP/1.1 2026-03-14 12:10:45 +09:00
Tatsuhiro Tsujikawa
5907198a7c Merge pull request #2630 from nghttp2/nghttpx-rework-close-wait-pkt-generation
nghttpx: Rework close-wait packet generation for h3
2026-03-14 10:54:27 +09:00
Tatsuhiro Tsujikawa
8b06168dbf nghttpx: Rework close-wait packet generation for h3 2026-03-14 10:25:45 +09:00
Tatsuhiro Tsujikawa
d2a4e4cf89 h2load: Output the measurement results in JSON 2026-03-13 21:52:21 +09:00
Tatsuhiro Tsujikawa
c9971a6f29 Merge pull request #2628 from nghttp2/h2load-compact-output
h2load: Make the names of perf metric short and concise
2026-03-13 20:37:57 +09:00
Tatsuhiro Tsujikawa
577650a30f h2load: Make the names of perf metric short and concise 2026-03-13 19:30:45 +09:00
Tatsuhiro Tsujikawa
71e73659d7 Merge pull request #2626 from nghttp2/nghttpx-accept-pending-conn
nghttpx: Accept pending connections until it returns error
2026-03-13 19:30:12 +09:00
Tatsuhiro Tsujikawa
f7e49bc9dd nghttpx: Accept pending connections until it returns error 2026-03-13 18:53:52 +09:00
Tatsuhiro Tsujikawa
8cb8c3baa8 Merge pull request #2627 from nghttp2/src-no-consteval
src: Avoid consteval for now
2026-03-12 00:47:11 +09:00
Tatsuhiro Tsujikawa
9f309fdae6 Merge pull request #2624 from nghttp2/h2load-tls-resumption
h2load: Support TLS resumption
2026-03-12 00:24:59 +09:00
Tatsuhiro Tsujikawa
fce8e889c9 src: Avoid consteval for now 2026-03-11 23:56:57 +09:00
Tatsuhiro Tsujikawa
478a1e5608 Merge pull request #2625 from nghttp2/src-accept-conn-num
nghttpd, nghttpx: Accept at most 10 connections per loop
2026-03-11 23:54:38 +09:00
Tatsuhiro Tsujikawa
aa1fcda83f h2load: Support TLS resumption 2026-03-11 23:15:00 +09:00
Tatsuhiro Tsujikawa
726dce78cd nghttpd, nghttpx: Accept at most 10 connections per loop 2026-03-11 23:02:56 +09:00
Tatsuhiro Tsujikawa
47e1c42600 Merge pull request #2623 from nghttp2/h2load-cert-group
h2load: Show certificate type and negotiated group
2026-03-11 22:36:07 +09:00
Tatsuhiro Tsujikawa
16ba46cbe1 h2load: Show certificate type and negotiated group 2026-03-11 21:52:40 +09:00
Tatsuhiro Tsujikawa
53c04fdbcb Merge pull request #2618 from nghttp2/dependabot/github_actions/docker/setup-buildx-action-4
build(deps): bump docker/setup-buildx-action from 3 to 4
2026-03-11 08:28:27 +09:00
dependabot[bot]
7b1a2343cb build(deps): bump docker/setup-buildx-action from 3 to 4
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3 to 4.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-version: '4'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 15:09:45 +00:00
Tatsuhiro Tsujikawa
d8e226271d Merge pull request #2617 from nghttp2/dependabot/github_actions/docker/build-push-action-7
build(deps): bump docker/build-push-action from 6 to 7
2026-03-11 00:08:41 +09:00
Tatsuhiro Tsujikawa
ae4ee62134 Merge pull request #2621 from nghttp2/amend-2619
Amend #2619
2026-03-10 22:59:37 +09:00
Tatsuhiro Tsujikawa
bcc0b9cfc5 Amend #2619 2026-03-10 21:57:10 +09:00
Tatsuhiro Tsujikawa
e4f55dc528 Merge pull request #2620 from nghttp2/h2load-histogram
h2load: Plot histogram
2026-03-10 21:30:44 +09:00
Tatsuhiro Tsujikawa
4db13a7d4d h2load: Plot histogram 2026-03-10 20:51:28 +09:00
Tatsuhiro Tsujikawa
24e72ca9d9 Merge pull request #2616 from nghttp2/h2load-quic-metrics
h2load: Add some QUIC metrics
2026-03-10 19:37:14 +09:00
Tatsuhiro Tsujikawa
378ce7c0b0 h2load: Add some QUIC metrics
The following QUIC metrics have been added:

- min RTT
- smoothed RTT
- packets sent
- packets received
- packets lost
- packets per recvmsg (GRO)
2026-03-10 19:07:22 +09:00
Tatsuhiro Tsujikawa
aeeadcad7b Merge pull request #2619 from nghttp2/refactor-h2load-metrics-manual
h2load: Refactor metrics manual
2026-03-10 19:03:54 +09:00
Tatsuhiro Tsujikawa
34409d4e62 h2load: Refactor metrics manual 2026-03-10 18:40:02 +09:00
dependabot[bot]
d1fbcfacde build(deps): bump docker/build-push-action from 6 to 7
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6 to 7.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-09 15:29:37 +00:00
Tatsuhiro Tsujikawa
14d2145299 Merge pull request #2615 from nghttp2/h2load-refactor-sd-stat-output
h2load: Refactor SDStat output
2026-03-08 18:07:32 +09:00
Tatsuhiro Tsujikawa
e7e0fb7b9f h2load: Refactor SDStat output 2026-03-08 16:01:55 +09:00
Tatsuhiro Tsujikawa
fbded45cd1 Merge pull request #2614 from nghttp2/h2load-more-stats
h2load: Add median, p95, and p99 metrics
2026-03-07 19:22:50 +09:00
Tatsuhiro Tsujikawa
95321ef9b3 h2load: Add median, p95, and p99 metrics 2026-03-07 18:58:14 +09:00
Tatsuhiro Tsujikawa
883f9370e4 Merge pull request #2613 from nghttp2/h2load-fix-client-do-not-stop
h2load: Fix bug that h2load does not stop early with -D option
2026-03-07 18:10:49 +09:00
Tatsuhiro Tsujikawa
6813f88cac h2load: Fix bug that h2load does not stop early with -D option 2026-03-07 17:35:30 +09:00
Tatsuhiro Tsujikawa
4f78e96df2 Merge pull request #2612 from nghttp2/dependabot/go_modules/golang.org/x/net-0.51.0
build(deps): bump golang.org/x/net from 0.50.0 to 0.51.0
2026-03-03 18:14:11 +09:00
Tatsuhiro Tsujikawa
6168c8be53 GHA: Bump go version in workflow 2026-03-03 17:42:00 +09:00
Tatsuhiro Tsujikawa
1b3c183521 Merge pull request #2611 from nghttp2/dependabot/github_actions/actions/upload-artifact-7
build(deps): bump actions/upload-artifact from 6 to 7
2026-03-03 00:46:20 +09:00
dependabot[bot]
4eda36bb11 build(deps): bump golang.org/x/net from 0.50.0 to 0.51.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.50.0 to 0.51.0.
- [Commits](https://github.com/golang/net/compare/v0.50.0...v0.51.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.51.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 15:19:47 +00:00
dependabot[bot]
9b4393df30 build(deps): bump actions/upload-artifact from 6 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-02 15:19:39 +00:00
Tatsuhiro Tsujikawa
ad65ce60f3 Merge pull request #2610 from nghttp2/revert-2609-typedefs
Revert "Ensure typedefs use named structs and unions"
2026-03-02 19:09:29 +09:00
Tatsuhiro Tsujikawa
435b6d4505 Revert "Ensure typedefs use named structs and unions" 2026-03-02 18:40:33 +09:00
Tatsuhiro Tsujikawa
2fc46fa64c Merge pull request #2609 from cbarrick/typedefs
Ensure typedefs use named structs and unions
2026-03-01 17:40:10 +09:00
Chris Barrick
4170fd8c31 Ensure typedefs use named structs and unions 2026-02-28 15:05:48 -05:00
Tatsuhiro Tsujikawa
cd3c01267d Merge pull request #2608 from nghttp2/fix-altsvc-null-arithmetic
altsvc: Avoid pointer arithmetic against NULL
2026-02-19 19:28:03 +09:00
Tatsuhiro Tsujikawa
545ee4ff83 altsvc: Avoid pointer arithmetic against NULL 2026-02-19 18:58:51 +09:00
Tatsuhiro Tsujikawa
619e861ddf Merge pull request #2607 from nghttp2/check-fatal-first
Check nghttp2_is_fatal first
2026-02-19 18:55:56 +09:00
Tatsuhiro Tsujikawa
68f77a3475 Check nghttp2_is_fatal first 2026-02-18 22:45:48 +09:00
Tatsuhiro Tsujikawa
c14f38b84f Merge pull request #2605 from nghttp2/dependabot/go_modules/golang.org/x/net-0.50.0
build(deps): bump golang.org/x/net from 0.49.0 to 0.50.0
2026-02-17 08:25:26 +09:00
dependabot[bot]
f5a9a72c2e build(deps): bump golang.org/x/net from 0.49.0 to 0.50.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.49.0 to 0.50.0.
- [Commits](https://github.com/golang/net/compare/v0.49.0...v0.50.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.50.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-16 15:22:35 +00:00
Tatsuhiro Tsujikawa
dcb6842fb6 Merge pull request #2602 from nghttp2/revert-2593-src-avoid-strict-aliasing-violation
Revert "src: Avoid strict aliasing violation"
2026-01-27 18:42:00 +09:00
Tatsuhiro Tsujikawa
e99404a6a6 Revert "src: Avoid strict aliasing violation" 2026-01-27 18:06:20 +09:00
Tatsuhiro Tsujikawa
2a25753088 Merge pull request #2601 from nghttp2/bump-ngtcp2
Bump ngtcp2 and its dependencies
2026-01-21 19:29:23 +09:00
Tatsuhiro Tsujikawa
e93993af5d Bump ngtcp2 and its dependencies 2026-01-21 18:53:11 +09:00
Tatsuhiro Tsujikawa
a0b9f89677 Merge pull request #2598 from nghttp2/remove-ignored-data-glitch
Remove glitch detection for ignored DATA frame
2026-01-14 18:02:40 +09:00
Tatsuhiro Tsujikawa
d45577eeea Merge pull request #2600 from nghttp2/gha-fix-main-branch
GHA: Fix main branch in cancel-in-progress
2026-01-13 21:03:01 +09:00
Tatsuhiro Tsujikawa
34e45c6c66 Merge pull request #2596 from nghttp2/dependabot/go_modules/golang.org/x/net-0.49.0
build(deps): bump golang.org/x/net from 0.48.0 to 0.49.0
2026-01-13 19:39:28 +09:00
Tatsuhiro Tsujikawa
507e6c8f36 GHA: Fix main branch in cancel-in-progress 2026-01-13 19:10:58 +09:00
Tatsuhiro Tsujikawa
b320a57c78 Merge pull request #2599 from nghttp2/increase-glitch-rate-limit
Increase default glitch rate limit to 10x
2026-01-13 19:07:31 +09:00
dependabot[bot]
f8f777b674 build(deps): bump golang.org/x/net from 0.48.0 to 0.49.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.48.0 to 0.49.0.
- [Commits](https://github.com/golang/net/compare/v0.48.0...v0.49.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.49.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-13 09:52:54 +00:00
Tatsuhiro Tsujikawa
b35e136d0b Merge pull request #2595 from nghttp2/dependabot/go_modules/github.com/quic-go/quic-go-0.59.0
build(deps): bump github.com/quic-go/quic-go from 0.58.0 to 0.59.0
2026-01-13 18:50:52 +09:00
Tatsuhiro Tsujikawa
13b25a40ac Increase default glitch rate limit to 10x
Increase default glitch rate limit to 10x to make it less susceptible.
2026-01-13 18:34:25 +09:00
Tatsuhiro Tsujikawa
c4e990bc32 Remove glitch detection for ignored DATA frame
This is problematic if server responds to client RESET_STREAM very
slowly or high latency with high bandwidth connections.
2026-01-13 18:13:09 +09:00
dependabot[bot]
42b7d162e7 build(deps): bump github.com/quic-go/quic-go from 0.58.0 to 0.59.0
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.58.0 to 0.59.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.58.0...v0.59.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.59.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-12 17:50:36 +00:00
Tatsuhiro Tsujikawa
5653439558 Merge pull request #2594 from nghttp2/strlen-lit
Introduce nghttp2_strlen_lit
2026-01-08 18:46:47 +09:00
Tatsuhiro Tsujikawa
05febf8a3a Introduce nghttp2_strlen_lit 2026-01-08 18:24:57 +09:00
Tatsuhiro Tsujikawa
1d6f58461a Merge pull request #2593 from nghttp2/src-avoid-strict-aliasing-violation
src: Avoid strict aliasing violation
2026-01-06 21:32:29 +09:00
Tatsuhiro Tsujikawa
e94a42d22a src: Avoid strict aliasing violation 2026-01-06 20:36:38 +09:00
Tatsuhiro Tsujikawa
85cf340e27 Merge pull request #2592 from nghttp2/rewrite-dockerfile
Rewrite Dockerfile with heredoc syntax
2025-12-24 19:25:56 +09:00
Tatsuhiro Tsujikawa
1595540dc7 Rewrite Dockerfile with heredoc syntax 2025-12-24 19:02:26 +09:00
Tatsuhiro Tsujikawa
7fa2531bd8 Merge pull request #2591 from nghttp2/dependabot/go_modules/github.com/quic-go/quic-go-0.58.0
build(deps): bump github.com/quic-go/quic-go from 0.57.1 to 0.58.0
2025-12-23 08:39:30 +09:00
dependabot[bot]
d50fc14deb build(deps): bump github.com/quic-go/quic-go from 0.57.1 to 0.58.0
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.57.1 to 0.58.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.57.1...v0.58.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.58.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-22 14:05:54 +00:00
Tatsuhiro Tsujikawa
ae9dedbc8b Merge pull request #2589 from nghttp2/dependabot/go_modules/golang.org/x/net-0.48.0
build(deps): bump golang.org/x/net from 0.47.0 to 0.48.0
2025-12-16 21:00:06 +09:00
Tatsuhiro Tsujikawa
f1468b9f7a Merge pull request #2588 from nghttp2/dependabot/github_actions/actions/cache-5
build(deps): bump actions/cache from 4 to 5
2025-12-16 19:35:35 +09:00
Tatsuhiro Tsujikawa
d3d90be75d Merge pull request #2587 from nghttp2/dependabot/github_actions/actions/upload-artifact-6
build(deps): bump actions/upload-artifact from 5 to 6
2025-12-16 00:35:46 +09:00
dependabot[bot]
8e8b057d68 build(deps): bump golang.org/x/net from 0.47.0 to 0.48.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.47.0 to 0.48.0.
- [Commits](https://github.com/golang/net/compare/v0.47.0...v0.48.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.48.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 14:06:19 +00:00
dependabot[bot]
3e78214051 build(deps): bump actions/cache from 4 to 5
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 14:05:59 +00:00
dependabot[bot]
76bbcad48c build(deps): bump actions/upload-artifact from 5 to 6
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 14:05:55 +00:00
Tatsuhiro Tsujikawa
a107cc8c76 Merge pull request #2581 from nghttp2/src-variant-sockaddr
src: Rewrite Address with std::variant
2025-12-07 22:12:43 +09:00
Tatsuhiro Tsujikawa
c51d154977 src: Rewrite Address with std::variant 2025-12-07 21:37:53 +09:00
Tatsuhiro Tsujikawa
f3cc363b59 Merge pull request #2580 from nghttp2/remove-extra-semicolon
Remove extraneous semicolon
2025-12-07 18:25:03 +09:00
Tatsuhiro Tsujikawa
dea60b982c Remove extraneous semicolon 2025-12-07 17:35:43 +09:00
Tatsuhiro Tsujikawa
9d75e0048e Merge pull request #2579 from nghttp2/nghttpx-remove-stream-closed
nghttpx: Remove stream_closed_ from Http2DownstreamConnection
2025-12-07 13:29:02 +09:00
Tatsuhiro Tsujikawa
a19239863d nghttpx: Remove stream_closed_ from Http2DownstreamConnection
Now RST_STREAM handling is improved in libnghttp2, we can just submit
RST_STREAM, and let lbnghttp2 decide whether the frame should be sent
or not.
2025-12-07 12:25:16 +09:00
Tatsuhiro Tsujikawa
73b773710d Merge pull request #2578 from nghttp2/cancel-sending-rst-stream-if-stream-not-found
Cancel sending RST_STREAM if stream is not found
2025-12-07 12:22:07 +09:00
Tatsuhiro Tsujikawa
d3f0a6d9ee Cancel sending RST_STREAM if stream is not found
nghttp2_submit_rst_stream is intended to send RST_STREAM frame to the
existing stream.  Actually, nghttp2_session_add_rst_stream_continue
does not add RST_STREAM if the stream is not found.  There is a
situation that the stream exists when nghttp2_submit_rst_stream is
called, but it may be closed before actually sending RST_STREAM.
Previously, we send the frame in this case hoping that this is noop on
remote endpoint.  This commit checks stream existence just before
sending RST_STREAM, and if the stream is not found, cancel RST_STREAM.
This is the consistent behavior of nghttp2_submit_rst_stream and fixes
race condition.
2025-12-07 11:59:27 +09:00
Tatsuhiro Tsujikawa
a581d84d99 Merge pull request #2577 from nghttp2/remove-union-from-worker-id
Remove union from WorkerID
2025-12-05 20:38:22 +09:00
Tatsuhiro Tsujikawa
8a8c319c72 Remove union from WorkerID 2025-12-05 19:22:48 +09:00
Tatsuhiro Tsujikawa
c186b00b5c Merge pull request #2576 from nghttp2/fix-union-usage-in-dpw
Fix union usage in nghttp2_data_provider_wrap
2025-12-05 19:22:16 +09:00
Tatsuhiro Tsujikawa
c322eec789 Fix union usage in nghttp2_data_provider_wrap 2025-12-05 18:57:41 +09:00
Tatsuhiro Tsujikawa
0c570c823d Merge pull request #2575 from nghttp2/dependabot/go_modules/github.com/quic-go/quic-go-0.57.1
build(deps): bump github.com/quic-go/quic-go from 0.57.0 to 0.57.1
2025-12-02 08:36:26 +09:00
dependabot[bot]
d6f85b11ce build(deps): bump github.com/quic-go/quic-go from 0.57.0 to 0.57.1
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.57.0 to 0.57.1.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.57.0...v0.57.1)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.57.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 16:04:48 +00:00
Tatsuhiro Tsujikawa
ba1747b97c Merge pull request #2574 from nghttp2/bump-ngtcp2
Bump ngtcp2 and its dependencies
2025-11-28 19:39:43 +09:00
Tatsuhiro Tsujikawa
6c79b9afde Bump ngtcp2 and its dependencies 2025-11-28 18:50:24 +09:00
Tatsuhiro Tsujikawa
18f2edf50a Merge pull request #2571 from nghttp2/dependabot/go_modules/github.com/quic-go/quic-go-0.57.0
build(deps): bump github.com/quic-go/quic-go from 0.56.0 to 0.57.0
2025-11-25 20:42:24 +09:00
Tatsuhiro Tsujikawa
1c98cc6673 Merge pull request #2570 from nghttp2/dependabot/github_actions/actions/checkout-6
build(deps): bump actions/checkout from 5 to 6
2025-11-25 19:07:33 +09:00
dependabot[bot]
ba9b332627 build(deps): bump github.com/quic-go/quic-go from 0.56.0 to 0.57.0
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.56.0 to 0.57.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.56.0...v0.57.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.57.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 14:48:56 +00:00
dependabot[bot]
279cee2af7 build(deps): bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 14:43:33 +00:00
Tatsuhiro Tsujikawa
05d77b6d30 Merge pull request #2569 from nghttp2/src-remove-lowcase-redundant-cast
examples: Remove redundant cast in lowcase
2025-11-24 19:14:43 +09:00
Tatsuhiro Tsujikawa
15a13d5ac4 examples: Remove redundant cast in lowcase 2025-11-24 18:44:18 +09:00
Tatsuhiro Tsujikawa
bf3cc82a4b Merge pull request #2568 from nghttp2/src-lowcase
src: Generate lowcase_tbl
2025-11-24 18:39:23 +09:00
Tatsuhiro Tsujikawa
b91f598282 src: Generate lowcase_tbl 2025-11-24 17:54:29 +09:00
Tatsuhiro Tsujikawa
fa4a274a18 Merge pull request #2567 from nghttp2/gha-ubuntu-arm
Gha ubuntu arm
2025-11-24 11:46:51 +09:00
Tatsuhiro Tsujikawa
fc73d69ffb Fix compile errors 2025-11-24 10:46:52 +09:00
Tatsuhiro Tsujikawa
b4d6889fb6 GHA: Add ubuntu-24.04-arm builds 2025-11-24 09:39:08 +09:00
Tatsuhiro Tsujikawa
73ae764211 Merge pull request #2566 from nghttp2/dependabot/go_modules/golang.org/x/crypto-0.45.0
build(deps): bump golang.org/x/crypto from 0.43.0 to 0.45.0
2025-11-22 09:57:08 +09:00
dependabot[bot]
1c6d26488b build(deps): bump golang.org/x/crypto from 0.43.0 to 0.45.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.43.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.43.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-20 02:30:15 +00:00
Tatsuhiro Tsujikawa
b485a87b57 Merge pull request #2564 from nghttp2/nghttpx-ensure-reset-downstream-stream
nghttpx: Ensure resetting downstream h2 stream
2025-11-14 19:29:00 +09:00
Tatsuhiro Tsujikawa
b0f79f18bb nghttpx: Ensure resetting downstream h2 stream
Ensure resetting downstream h2 stream if Http2DownstreamConnection is
not closed via nghttp2_on_stream_close_callback.
2025-11-14 18:59:11 +09:00
Tatsuhiro Tsujikawa
14c02f6bc3 Merge pull request #2563 from nghttp2/gha-cancel-stale-job
GHA: Cancel stale job
2025-11-11 21:40:22 +09:00
Tatsuhiro Tsujikawa
3a79f38503 GHA: Cancel stale job
We have severely limited resources in terms of GitHub Actions.  We
cannot run full 2 build workflows at the same time.  To speed up the
latest build, we need to cancel the previous jobs, but it is too
tedious.  Let's cancel those stale jobs automatically.  No need to
cancel jobs on main because they should finish once committed.
2025-11-11 21:16:39 +09:00
Tatsuhiro Tsujikawa
800023a8e9 Merge pull request #2551 from trukna/cmake-fix-install-path
lib/CMakeLists.txt: Fix NGHTTP2_CONFIG_INSTALL_DIR path
2025-11-11 20:31:53 +09:00
Tatsuhiro Tsujikawa
a957322040 Merge pull request #2562 from nghttp2/src-remove-duplicated-test
src: Remove the duplicated test
2025-11-11 20:30:50 +09:00
Tatsuhiro Tsujikawa
a7fa441f0a src: Remove the duplicated test 2025-11-11 19:35:49 +09:00
Tatsuhiro Tsujikawa
61d3a68415 Merge pull request #2561 from nghttp2/src-simplify-dlist-remove
src: Simplify DList::remove
2025-11-11 19:34:32 +09:00
Tatsuhiro Tsujikawa
476a5f805e src: Simplify DList::remove 2025-11-11 19:09:00 +09:00
Tatsuhiro Tsujikawa
81b74b4e42 Merge pull request #2559 from nghttp2/dependabot/go_modules/github.com/quic-go/quic-go-0.56.0
build(deps): bump github.com/quic-go/quic-go from 0.55.0 to 0.56.0
2025-11-11 18:59:57 +09:00
dependabot[bot]
61cb0095b3 build(deps): bump github.com/quic-go/quic-go from 0.55.0 to 0.56.0
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.55.0 to 0.56.0.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.55.0...v0.56.0)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-version: 0.56.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-11 09:17:05 +00:00
Tatsuhiro Tsujikawa
e4454672f0 Merge pull request #2560 from nghttp2/integration-cope-with-errprocessdone
integration: Cope with os.ErrProcessDone
2025-11-11 18:16:01 +09:00
Tatsuhiro Tsujikawa
e15a5517c7 integration: Cope with os.ErrProcessDone 2025-11-11 17:46:54 +09:00
Tatsuhiro Tsujikawa
9b0044d051 Merge pull request #2557 from nghttp2/src-workaround-ossl3-perf-regression
src: Workaround performance regression since OpenSSL 3.0
2025-11-09 20:49:27 +09:00
Tatsuhiro Tsujikawa
e9e5e15bbf src: Workaround performance regression since OpenSSL 3.0 2025-11-09 19:12:00 +09:00
Tatsuhiro Tsujikawa
2c7ef6442d Merge pull request #2556 from nghttp2/nghttpx-save-quic-tx-buf-allocation
nghttpx: Avoid separate allocation for QUIC tx buffer
2025-11-09 18:16:07 +09:00
Tatsuhiro Tsujikawa
d3ecf78031 nghttpx: Avoid separate allocation for QUIC tx buffer 2025-11-09 17:47:06 +09:00
Tatsuhiro Tsujikawa
d01db47215 Merge pull request #2555 from nghttp2/src-adopt-get0-ec-key
src: Adopt EVP_PKEY_get0_EC_KEY
2025-11-09 17:32:49 +09:00
Tatsuhiro Tsujikawa
8a760d0726 src: Adopt EVP_PKEY_get0_EC_KEY 2025-11-09 17:00:48 +09:00
Tatsuhiro Tsujikawa
73bfe4bf21 Merge pull request #2554 from nghttp2/src-remove-defer-dtor-noexcept
src: Remove noexcept from ~Defer
2025-11-09 17:00:09 +09:00
Tatsuhiro Tsujikawa
6e5e9bceca src: Remove noexcept from ~Defer
Remove noexcept from ~Defer because it is noexcept by default.
2025-11-09 16:33:46 +09:00
Tatsuhiro Tsujikawa
0476f0efbc Merge pull request #2553 from nghttp2/src-remove-lambda-emplty-param-list
src: Remove empty parameter list from lambda
2025-11-09 16:30:15 +09:00
Tatsuhiro Tsujikawa
ca23a490c3 src: Remove empty parameter list from lambda 2025-11-09 14:45:12 +09:00
Tatsuhiro Tsujikawa
ee2a4b625b Merge pull request #2552 from nghttp2/src-rewrite-defer
src: Rewrite defer
2025-11-09 14:07:00 +09:00
Tatsuhiro Tsujikawa
cec4bf08a2 src: Rewrite defer 2025-11-09 13:45:18 +09:00
Ankur Tyagi
0b67049243 lib/CMakeLists.txt: Fix NGHTTP2_CONFIG_INSTALL_DIR path
Remove hard coded path to fix installation on 64-bit arch.

Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
2025-11-09 13:38:26 +13:00
Tatsuhiro Tsujikawa
ebf4b7eaee Merge pull request #2550 from nghttp2/remove-unused-macros-and-enums
Remove unused macros and enums
2025-11-03 23:46:09 +09:00
Tatsuhiro Tsujikawa
0bf5b764fa Remove unused macros and enums 2025-11-03 22:45:18 +09:00
Tatsuhiro Tsujikawa
081eb29e9f Merge pull request #2549 from nghttp2/update-map
Port ngtcp2_map changes
2025-11-03 22:44:50 +09:00
Tatsuhiro Tsujikawa
ca81d89fe1 Port ngtcp2_map changes 2025-11-03 21:44:05 +09:00
Tatsuhiro Tsujikawa
450ed6afce Merge pull request #2548 from nghttp2/optimize-hpack-huffman
hpack: Optimize huffman decoding a bit
2025-11-03 19:20:20 +09:00
Tatsuhiro Tsujikawa
e72f4af5de hpack: Optimize huffman decoding a bit 2025-11-03 18:32:37 +09:00
Tatsuhiro Tsujikawa
3fa6a6349c Merge pull request #2546 from nghttp2/dependabot/github_actions/actions/upload-artifact-5
build(deps): bump actions/upload-artifact from 4 to 5
2025-10-28 08:33:37 +09:00
dependabot[bot]
6c0fd9400d build(deps): bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 15:21:33 +00:00
Tatsuhiro Tsujikawa
de81da7621 Merge pull request #2545 from nghttp2/simplify-format-hex
src: Simplify format_hex and format_upper_hex
2025-10-27 18:53:39 +09:00
Tatsuhiro Tsujikawa
8593b1f46c src: Simplify format_hex and format_upper_hex
Ignore -Wsign-conversion warning to avoid an issue that is very hard
to workaround.
2025-10-27 18:20:03 +09:00
Tatsuhiro Tsujikawa
0e9d325dee Bump package version 2025-10-25 17:34:56 +09:00
154 changed files with 8180 additions and 6381 deletions

View File

@@ -15,10 +15,10 @@ jobs:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
file: Dockerfile.android

View File

@@ -4,49 +4,53 @@ on: [push, pull_request]
permissions: read-all
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
env:
LIBBPF_VERSION: v1.6.2
OPENSSL1_VERSION: 1_1_1w+quic
OPENSSL3_VERSION: 3.6.0
BORINGSSL_VERSION: db1a8456167249f95b854a1cd24c6b553d0f1567
AWSLC_VERSION: v1.62.0
NGHTTP3_VERSION: v1.12.0
NGTCP2_VERSION: v1.17.0
WOLFSSL_VERSION: v5.8.2-stable
BORINGSSL_VERSION: 52975ff6ea9fb076e53025b82f2e80a23b027a5c
AWSLC_VERSION: v1.66.2
NGHTTP3_VERSION: v1.15.0
NGTCP2_VERSION: v1.20.0
WOLFSSL_VERSION: v5.8.4-stable
jobs:
build-cache:
strategy:
matrix:
os: [ubuntu-24.04, macos-14, macos-15]
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-14, macos-15]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Restore libbpf cache
id: cache-libbpf
uses: actions/cache@v4
uses: actions/cache@v5
if: runner.os == 'Linux'
with:
path: libbpf/build
key: ${{ matrix.os }}-libbpf-${{ env.LIBBPF_VERSION }}
- name: Restore OpenSSL v1.1.1 cache
id: cache-openssl1
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: openssl1/build
key: ${{ matrix.os }}-openssl-${{ env.OPENSSL1_VERSION }}
- name: Restore OpenSSL v3.x cache
id: cache-openssl3
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: openssl3/build
key: ${{ matrix.os }}-openssl-${{ env.OPENSSL3_VERSION }}
- name: Restore BoringSSL cache
id: cache-boringssl
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
boringssl/build/libcrypto.a
@@ -55,7 +59,7 @@ jobs:
key: ${{ matrix.os }}-boringssl-${{ env.BORINGSSL_VERSION }}
- name: Restore aws-lc cache
id: cache-awslc
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
aws-lc/build/crypto/libcrypto.a
@@ -64,25 +68,25 @@ jobs:
key: ${{ matrix.os }}-awslc-${{ env.AWSLC_VERSION }}
- name: Restore wolfSSL cache
id: cache-wolfssl
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: wolfssl/build
key: ${{ matrix.os }}-wolfssl-${{ env.WOLFSSL_VERSION }}
- name: Restore nghttp3 cache
id: cache-nghttp3
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: nghttp3/build
key: ${{ matrix.os }}-nghttp3-${{ env.NGHTTP3_VERSION }}
- name: Restore ngtcp2 + quictls/openssl v1.1.1 cache
id: cache-ngtcp2-openssl1
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ngtcp2-openssl1/build
key: ${{ matrix.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL1_VERSION }}
- name: Restore ngtcp2 + quictls/openssl v3.x cache
id: cache-ngtcp2-openssl3
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ngtcp2-openssl3/build
key: ${{ matrix.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL3_VERSION }}
@@ -257,12 +261,22 @@ jobs:
buildtool: distcheck
http3: http3
openssl: awslc
- os: ubuntu-24.04-arm
compiler: clang
buildtool: autotools
http3: http3
openssl: wolfssl
- os: ubuntu-24.04-arm
compiler: gcc
buildtool: autotools
http3: http3
openssl: wolfssl
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
submodules: recursive
- name: Linux setup
@@ -330,7 +344,7 @@ jobs:
echo 'CC=gcc' >> $GITHUB_ENV
echo 'CXX=g++' >> $GITHUB_ENV
- name: Restore libbpf cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
if: matrix.http3 == 'http3' && matrix.compiler == 'clang' && runner.os == 'Linux'
with:
path: libbpf/build
@@ -355,21 +369,21 @@ jobs:
echo 'LIBEV_CFLAGS='"$LIBEV_CFLAGS" >> $GITHUB_ENV
echo 'LIBEV_LIBS='"$LIBEV_LIBS" >> $GITHUB_ENV
- name: Restore quictls/openssl v1.1.1 cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
if: matrix.openssl == 'openssl1'
with:
path: openssl1/build
key: ${{ matrix.os }}-openssl-${{ env.OPENSSL1_VERSION }}
fail-on-cache-miss: true
- name: Restore openssl/openssl v3.x cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
if: matrix.openssl == 'openssl3'
with:
path: openssl3/build
key: ${{ matrix.os }}-openssl-${{ env.OPENSSL3_VERSION }}
fail-on-cache-miss: true
- name: Restore BoringSSL cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
if: matrix.openssl == 'boringssl'
with:
path: |
@@ -379,7 +393,7 @@ jobs:
key: ${{ matrix.os }}-boringssl-${{ env.BORINGSSL_VERSION }}
fail-on-cache-miss: true
- name: Restore aws-lc cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
if: matrix.openssl == 'awslc'
with:
path: |
@@ -417,7 +431,7 @@ jobs:
echo 'BORINGSSL_LIBS='"$OPENSSL_LIBS" >> $GITHUB_ENV
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
- name: Restore wolfSSL cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
if: matrix.openssl == 'wolfssl'
with:
path: wolfssl/build
@@ -432,21 +446,21 @@ jobs:
echo 'EXTRA_AUTOTOOLS_OPTS='"$EXTRA_AUTOTOOLS_OPTS" >> $GITHUB_ENV
echo 'EXTRA_CMAKE_OPTS='"$EXTRA_CMAKE_OPTS" >> $GITHUB_ENV
- name: Restore nghttp3 cache
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
if: matrix.http3 == 'http3'
with:
path: nghttp3/build
key: ${{ matrix.os }}-nghttp3-${{ env.NGHTTP3_VERSION }}
fail-on-cache-miss: true
- name: Restore ngtcp2 + quictls/openssl v1.1.1 cache + BoringSSL
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
if: matrix.http3 == 'http3' && (matrix.openssl == 'openssl1' || matrix.openssl == 'boringssl' || matrix.openssl == 'wolfssl')
with:
path: ngtcp2-openssl1/build
key: ${{ matrix.os }}-ngtcp2-${{ env.NGTCP2_VERSION }}-openssl-${{ env.OPENSSL1_VERSION }}
fail-on-cache-miss: true
- name: Restore ngtcp2 + quictls/openssl v3.x cache + aws-lc
uses: actions/cache/restore@v4
uses: actions/cache/restore@v5
if: matrix.http3 == 'http3' && (matrix.openssl == 'openssl3' || matrix.openssl == 'awslc')
with:
path: ngtcp2-openssl3/build
@@ -527,7 +541,7 @@ jobs:
- uses: actions/setup-go@v6
if: matrix.buildtool != 'distcheck'
with:
go-version: "1.24"
go-version: "1.25"
- name: Integration test
# Integration tests for nghttpx; autotools erases build
# artifacts.
@@ -549,7 +563,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
submodules: recursive
- name: Prepare for i386
@@ -597,10 +611,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
submodules: recursive
- uses: microsoft/setup-msbuild@v2
- uses: microsoft/setup-msbuild@v3
- name: Configure cmake
run: cmake -B build -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_GENERATOR_PLATFORM=${{ matrix.platform }} -DVCPKG_TARGET_TRIPLET=${{ matrix.arch}}-windows -DBUILD_STATIC_LIBS=ON -DBUILD_TESTING=ON
- name: Build nghttp2
@@ -622,7 +636,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive

View File

@@ -14,11 +14,11 @@ jobs:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v4
- name: Build
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: docker
build-args: NGHTTP2_BRANCH=${{ github.ref_name }}

View File

@@ -24,7 +24,7 @@ jobs:
fuzz-seconds: 600
dry-run: false
- name: Upload Crash
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: failure()
with:
name: artifacts

View File

@@ -24,7 +24,7 @@
cmake_minimum_required(VERSION 3.14)
# XXX using 1.8.90 instead of 1.9.0-DEV
project(nghttp2 VERSION 1.68.1 LANGUAGES C)
project(nghttp2 VERSION 1.68.90 LANGUAGES C)
# See versioning rule:
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html

View File

@@ -126,7 +126,7 @@ following libraries are required:
<https://github.com/quictls/openssl/tree/OpenSSL_1_1_1w+quic>`_; or
wolfSSL; or LibreSSL (does not support 0RTT); or aws-lc; or
`BoringSSL <https://boringssl.googlesource.com/boringssl/>`_ (commit
db1a8456167249f95b854a1cd24c6b553d0f1567); or OpenSSL >= 3.5.0
52975ff6ea9fb076e53025b82f2e80a23b027a5c); or OpenSSL >= 3.5.0
* `ngtcp2 <https://github.com/ngtcp2/ngtcp2>`_ >= 1.16.0
* `nghttp3 <https://github.com/ngtcp2/nghttp3>`_ >= 1.12.0
@@ -340,7 +340,7 @@ Build aws-lc:
.. code-block:: text
$ git clone --depth 1 -b v1.62.0 https://github.com/aws/aws-lc
$ git clone --depth 1 -b v1.66.2 https://github.com/aws/aws-lc
$ cd aws-lc
$ cmake -B build -DDISABLE_GO=ON --install-prefix=$PWD/opt
$ make -j$(nproc) -C build
@@ -351,7 +351,7 @@ Build nghttp3:
.. code-block:: text
$ git clone --depth 1 -b v1.12.0 https://github.com/ngtcp2/nghttp3
$ git clone --depth 1 -b v1.15.0 https://github.com/ngtcp2/nghttp3
$ cd nghttp3
$ git submodule update --init --depth 1
$ autoreconf -i
@@ -364,7 +364,7 @@ Build ngtcp2:
.. code-block:: text
$ git clone --depth 1 -b v1.17.0 https://github.com/ngtcp2/ngtcp2
$ git clone --depth 1 -b v1.20.0 https://github.com/ngtcp2/ngtcp2
$ cd ngtcp2
$ git submodule update --init --depth 1
$ autoreconf -i

View File

@@ -25,7 +25,7 @@ dnl Do not change user variables!
dnl https://www.gnu.org/software/automake/manual/html_node/Flag-Variables-Ordering.html
AC_PREREQ(2.61)
AC_INIT([nghttp2], [1.68.1], [t-tujikawa@users.sourceforge.net])
AC_INIT([nghttp2], [1.69.0-DEV], [t-tujikawa@users.sourceforge.net])
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "H2LOAD" "1" "Mar 18, 2026" "1.68.1" "nghttp2"
.TH "H2LOAD" "1" "Oct 25, 2025" "1.68.0" "nghttp2"
.SH NAME
h2load \- HTTP/2 benchmarking tool
.SH SYNOPSIS

View File

@@ -3,107 +3,96 @@
OUTPUT
------
REQUEST METRICS
~~~~~~~~~~~~~~~
requests
total
The number of requests h2load was instructed to make.
The total number of requests h2load was instructed to make.
started
The number of requests h2load has started.
The number of requests initiated by the tool.
done
The number of requests completed.
The number of requests that reached completion.
succeeded
The number of requests completed successfully. Only HTTP status
code 2xx or 3xx are considered as success.
Requests resulting in an HTTP 2xx or 3xx status code.
failed
The number of requests failed, including HTTP level failures
(non-successful HTTP status code).
The total number of failed requests. This includes both
``errored`` requests and requests that completed with a
non-2xx/3xx status code.
errored
The number of requests failed, except for HTTP level failures.
This is the subset of the number reported in ``failed`` and most
likely the network level failures or stream was reset by
RST_STREAM.
A subset of ``failed`` where the requests failed due to
network-level issues (e.g., TCP resets, ``RST_STREAM``) rather
than HTTP status codes.
timeout
The number of requests whose connection timed out before they were
completed. This is the subset of the number reported in
``errored``.
A subset of ``errored`` where the connection timed out before
completion.
status codes
The number of status code h2load received.
The specific count of received HTTP status codes categorized by
class (2xx, 3xx, 4xx, 5xx).
TRAFFIC METRICS
~~~~~~~~~~~~~~~
traffic
total
The number of bytes received from the server "on the wire". If
requests were made via TLS, this value is the number of decrypted
bytes.
Total application data bytes received "on the wire" (decrypted if
using TLS).
headers
The number of response header bytes from the server without
decompression. The ``space savings`` shows efficiency of header
compression. Let ``decompressed(headers)`` to the number of bytes
used for header fields after decompression. The ``space savings``
is calculated by (1 - ``headers`` / ``decompressed(headers)``) *
100. For HTTP/1.1, this is usually 0.00%, since it does not have
header compression. For HTTP/2, it shows some insightful numbers.
Total bytes used for response headers (pre-decompression).
space savings
Header compression efficiency, calculated as:
(1 - headers / decompressed_headers) * 100
where ``headers`` is the compressed size and
``decompressed_headers`` is the size after decompression.
data
The number of response body bytes received from the server.
Total bytes received in response bodies.
time for request
min
The minimum time taken for request and response.
max
The maximum time taken for request and response.
mean
The mean time taken for request and response.
sd
The standard deviation of the time taken for request and response.
+/- sd
The fraction of the number of requests within standard deviation
range (mean +/- sd) against total number of successful requests.
PERFORMANCE STATISTICS
~~~~~~~~~~~~~~~~~~~~~~
time for connect
min
The minimum time taken to connect to a server including TLS
handshake.
max
The maximum time taken to connect to a server including TLS
handshake.
mean
The mean time taken to connect to a server including TLS
handshake.
sd
The standard deviation of the time taken to connect to a server.
+/- sd
The fraction of the number of connections within standard
deviation range (mean +/- sd) against total number of successful
connections.
Metric Definitions
request
The duration from sending the first byte of a request to receiving
the last byte of the response.
connect
The time taken to establish a connection, including TLS
handshakes.
TTFB
The duration until the first byte of application data is received
from the server (decrypted if using TLS).
req/s
The requests per second measured individually across all clients.
min RTT
The minimum RTT (QUIC).
smoothed RTT
The smoothed RTT (QUIC).
packets sent
The number of packets sent (QUIC).
packets recv
The number of packets received (QUIC).
packets lost
The number of packets declared lost (QUIC).
GRO packets
The number of packets received in a single recvmsg call (QUIC).
time for 1st byte (of (decrypted in case of TLS) application data)
min
The minimum time taken to get 1st byte from a server.
max
The maximum time taken to get 1st byte from a server.
Distribution Fields
min / max
The absolute minimum and maximum values recorded.
median
The 50th percentile value.
p95 / p99
The 95th and 99th percentiles, indicating tail performance.
mean
The mean time taken to get 1st byte from a server.
The arithmetic average of all samples.
sd
The standard deviation of the time taken to get 1st byte from a
server.
The standard deviation (measure of data dispersion).
+/- sd
The fraction of the number of connections within standard
deviation range (mean +/- sd) against total number of successful
connections.
req/s
min
The minimum request per second among all clients.
max
The maximum request per second among all clients.
mean
The mean request per second among all clients.
sd
The standard deviation of request per second among all clients.
server.
+/- sd
The fraction of the number of connections within standard
deviation range (mean +/- sd) against total number of successful
connections.
The percentage of successful samples falling within one standard
deviation of the mean (mean +/- sd).
FLOW CONTROL
------------

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "NGHTTP" "1" "Mar 18, 2026" "1.68.1" "nghttp2"
.TH "NGHTTP" "1" "Oct 25, 2025" "1.68.0" "nghttp2"
.SH NAME
nghttp \- HTTP/2 client
.SH SYNOPSIS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "NGHTTPD" "1" "Mar 18, 2026" "1.68.1" "nghttp2"
.TH "NGHTTPD" "1" "Oct 25, 2025" "1.68.0" "nghttp2"
.SH NAME
nghttpd \- HTTP/2 server
.SH SYNOPSIS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "NGHTTPX" "1" "Mar 18, 2026" "1.68.1" "nghttp2"
.TH "NGHTTPX" "1" "Oct 25, 2025" "1.68.0" "nghttp2"
.SH NAME
nghttpx \- HTTP/2 proxy
.SH SYNOPSIS

View File

@@ -243,6 +243,10 @@ exists and serves incoming requests.
If you want to just reload configuration file without executing new
binary, send SIGHUP to nghttpx main process.
For TCP connections, nghttpx does moderate effort not to lose a
connection during this process. To make it more robust, consider to
enable ``net.ipv4.tcp_migrate_req``.
Re-opening log files
--------------------

View File

@@ -2,75 +2,83 @@ FROM debian:12 as build
ARG NGHTTP2_BRANCH=master
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
git clang-19 make binutils autoconf automake autotools-dev libtool \
pkg-config cmake cmake-data \
zlib1g-dev libev-dev libjemalloc-dev ruby-dev libc-ares-dev bison \
libelf-dev libbrotli-dev
RUN <<EOF
set -e
RUN git clone --recursive --shallow-submodules --depth 1 -b v1.62.0 https://github.com/aws/aws-lc && \
cd aws-lc && \
export CC=clang-19 CXX=clang++-19 && \
cmake -B build -DDISABLE_GO=ON && \
make -j$(nproc) -C build && \
cmake --install build && \
cd .. && \
rm -rf aws-lc
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
git clang-19 make binutils autoconf automake autotools-dev libtool \
pkg-config cmake cmake-data \
zlib1g-dev libev-dev libjemalloc-dev ruby-dev libc-ares-dev bison \
libelf-dev libbrotli-dev
RUN git clone --recursive --shallow-submodules --depth 1 -b v1.12.0 https://github.com/ngtcp2/nghttp3 && \
cd nghttp3 && \
autoreconf -i && \
./configure --disable-dependency-tracking --enable-lib-only \
CC=clang-19 CXX=clang++-19 && \
make -j$(nproc) && \
make install-strip && \
cd .. && \
rm -rf nghttp3
git clone --recursive --shallow-submodules --depth 1 -b v1.66.2 \
https://github.com/aws/aws-lc
cd aws-lc
export CC=clang-19 CXX=clang++-19
cmake -B build -DDISABLE_GO=ON
make -j$(nproc) -C build
cmake --install build
cd ..
rm -rf aws-lc
RUN git clone --recursive --shallow-submodules --depth 1 -b v1.17.0 https://github.com/ngtcp2/ngtcp2 && \
cd ngtcp2 && \
autoreconf -i && \
./configure --disable-dependency-tracking --enable-lib-only \
--with-boringssl \
CC=clang-19 CXX=clang++-19 \
LIBTOOL_LDFLAGS="-static-libtool-libs" \
BORINGSSL_LIBS="-l:libssl.a -l:libcrypto.a" \
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig" && \
make -j$(nproc) && \
make install-strip && \
cd .. && \
rm -rf ngtcp2
git clone --recursive --shallow-submodules --depth 1 -b v1.15.0 \
https://github.com/ngtcp2/nghttp3
cd nghttp3
autoreconf -i
./configure --disable-dependency-tracking --enable-lib-only \
CC=clang-19 CXX=clang++-19
make -j$(nproc)
make install-strip
cd ..
rm -rf nghttp3
RUN git clone --depth 1 -b v1.6.2 https://github.com/libbpf/libbpf && \
cd libbpf && \
CC=clang-19 PREFIX=/usr/local make -C src install && \
cd .. && \
rm -rf libbpf
git clone --recursive --shallow-submodules --depth 1 -b v1.20.0 \
https://github.com/ngtcp2/ngtcp2
cd ngtcp2
autoreconf -i
./configure --disable-dependency-tracking --enable-lib-only \
--with-boringssl \
CC=clang-19 CXX=clang++-19 \
LIBTOOL_LDFLAGS="-static-libtool-libs" \
BORINGSSL_LIBS="-l:libssl.a -l:libcrypto.a" \
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig"
make -j$(nproc)
make install-strip
cd ..
rm -rf ngtcp2
RUN git clone --recursive --shallow-submodules --depth 1 -b $NGHTTP2_BRANCH https://github.com/nghttp2/nghttp2 && \
cd nghttp2 && \
autoreconf -i && \
./configure --disable-dependency-tracking --disable-examples \
--disable-hpack-tools \
--with-mruby \
--enable-http3 --with-libbpf \
--with-libbrotlienc --with-libbrotlidec \
CC=clang-19 CXX=clang++-19 \
LIBTOOL_LDFLAGS="-static-libtool-libs" \
OPENSSL_LIBS="-l:libssl.a -l:libcrypto.a" \
LIBEV_LIBS="-l:libev.a" \
JEMALLOC_LIBS="-l:libjemalloc.a" \
LIBCARES_LIBS="-l:libcares.a" \
ZLIB_LIBS="-l:libz.a" \
LIBBPF_LIBS="-L/usr/local/lib64 -l:libbpf.a -l:libelf.a" \
LIBBROTLIENC_LIBS="-l:libbrotlienc.a -l:libbrotlicommon.a" \
LIBBROTLIDEC_LIBS="-l:libbrotlidec.a -l:libbrotlicommon.a" \
LDFLAGS="-static-libgcc -static-libstdc++" \
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig" && \
make -j$(nproc) install-strip && \
cd .. && \
rm -rf nghttp2
git clone --depth 1 -b v1.6.2 https://github.com/libbpf/libbpf
cd libbpf
CC=clang-19 PREFIX=/usr/local make -C src install
cd ..
rm -rf libbpf
git clone --recursive --shallow-submodules --depth 1 -b $NGHTTP2_BRANCH \
https://github.com/nghttp2/nghttp2
cd nghttp2
autoreconf -i
./configure --disable-dependency-tracking --disable-examples \
--disable-hpack-tools \
--with-mruby \
--enable-http3 --with-libbpf \
--with-libbrotlienc --with-libbrotlidec \
CC=clang-19 CXX=clang++-19 \
LIBTOOL_LDFLAGS="-static-libtool-libs" \
OPENSSL_LIBS="-l:libssl.a -l:libcrypto.a" \
LIBEV_LIBS="-l:libev.a" \
JEMALLOC_LIBS="-l:libjemalloc.a" \
LIBCARES_LIBS="-l:libcares.a" \
ZLIB_LIBS="-l:libz.a" \
LIBBPF_LIBS="-L/usr/local/lib64 -l:libbpf.a -l:libelf.a" \
LIBBROTLIENC_LIBS="-l:libbrotlienc.a -l:libbrotlicommon.a" \
LIBBROTLIDEC_LIBS="-l:libbrotlidec.a -l:libbrotlicommon.a" \
LDFLAGS="-static-libgcc -static-libstdc++" \
PKG_CONFIG_PATH="/usr/local/lib64/pkgconfig"
make -j$(nproc) install-strip
cd ..
rm -rf nghttp2
EOF
FROM gcr.io/distroless/base-nossl-debian12

View File

@@ -35,7 +35,7 @@ enum {''')
def gen_index_header(tokens, prefix, comp_fun, return_type, fail_value):
print('''\
{} lookup_token(const std::string_view &name) {{
{} lookup_token(std::string_view name) {{
switch (name.size()) {{'''.format(return_type))
b = build_header(tokens)
for size in sorted(b.keys()):

17
go.mod
View File

@@ -1,20 +1,17 @@
module github.com/nghttp2/nghttp2
go 1.24.0
go 1.25.0
require (
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874
github.com/quic-go/quic-go v0.55.0
github.com/quic-go/quic-go v0.59.0
github.com/tatsuhiro-t/go-nghttp2 v0.0.0-20240121064059-46ccb0a462a8
golang.org/x/net v0.46.0
golang.org/x/net v0.52.0
)
require (
github.com/quic-go/qpack v0.5.1 // indirect
golang.org/x/crypto v0.43.0 // indirect
golang.org/x/mod v0.28.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/text v0.30.0 // indirect
golang.org/x/tools v0.37.0 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
)

36
go.sum
View File

@@ -2,33 +2,25 @@ github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8b
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tatsuhiro-t/go-nghttp2 v0.0.0-20240121064059-46ccb0a462a8 h1:zKJxuRe+a0O34V81GAZWOrotuU6mveT30QLjJ7OPMMg=
github.com/tatsuhiro-t/go-nghttp2 v0.0.0-20240121064059-46ccb0a462a8/go.mod h1:gTqc3Q4boc+cKRlSFywTYdX9t6VGRcsThlNIWwaL3Dc=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -926,6 +926,228 @@ func TestH1H1CONNECTMethod(t *testing.T) {
}
}
// TestH1H1OPTIONSServerWide tests that server-wide OPTIONS request.
func TestH1H1OPTIONS(t *testing.T) {
st := newServerTester(t, options{})
defer st.Close()
if _, err := io.WriteString(st.conn, "OPTIONS * HTTP/1.1\r\nTest-Case: TestH1H1OPTIONSServerWide\r\nHost: 127.0.0.1\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
}
// TestH1H1OPTIONSProxyServerWide tests that server-wide OPTIONS
// request in proxy request.
func TestH1H1OPTIONSProxyServerWide(t *testing.T) {
st := newServerTester(t, options{})
defer st.Close()
if _, err := io.WriteString(st.conn, "OPTIONS http://127.0.0.1 HTTP/1.1\r\nTest-Case: TestH1H1OPTIONSProxyServerWide\r\nHost: 127.0.0.1\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
}
// TestH1H1BadMethodAsterisk tests that "*" in HTTP request other than
// OPTIONS request.
func TestH1H1BadMethodAsterisk(t *testing.T) {
st := newServerTester(t, options{})
defer st.Close()
if _, err := io.WriteString(st.conn, "GET * HTTP/1.1\r\nTest-Case: TestH1H1BadMethodAsterisk\r\nHost: 127.0.0.1\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusBadRequest; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
}
// TestH1H1AsteriskPrefix tests that a path starting with "*" in HTTP
// request.
func TestH1H1AsteriskPrefix(t *testing.T) {
st := newServerTester(t, options{})
defer st.Close()
if _, err := io.WriteString(st.conn, "OPTIONS *foo HTTP/1.1\r\nTest-Case: TestH1H1AsteriskPrefix\r\nHost: 127.0.0.1\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusBadRequest; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
}
// TestH1H2OPTIONSServerWide tests that server-wide OPTIONS request.
func TestH1H2OPTIONS(t *testing.T) {
opts := options{
args: []string{"--http2-bridge"},
}
st := newServerTester(t, opts)
defer st.Close()
if _, err := io.WriteString(st.conn, "OPTIONS * HTTP/1.1\r\nTest-Case: TestH1H2OPTIONSServerWide\r\nHost: 127.0.0.1\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
}
// TestH1H2OPTIONSProxyServerWide tests that server-wide OPTIONS
// request in proxy request.
func TestH1H2OPTIONSProxyServerWide(t *testing.T) {
opts := options{
args: []string{"--http2-bridge"},
}
st := newServerTester(t, opts)
defer st.Close()
if _, err := io.WriteString(st.conn, "OPTIONS http://127.0.0.1 HTTP/1.1\r\nTest-Case: TestH1H2OPTIONSProxyServerWide\r\nHost: 127.0.0.1\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusOK; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
}
// TestH1H2BadMethodAsterisk tests that "*" in HTTP request other than
// OPTIONS request.
func TestH1H2BadMethodAsterisk(t *testing.T) {
opts := options{
args: []string{"--http2-bridge"},
}
st := newServerTester(t, opts)
defer st.Close()
if _, err := io.WriteString(st.conn, "GET * HTTP/1.1\r\nTest-Case: TestH1H2BadMethodAsterisk\r\nHost: 127.0.0.1\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusBadRequest; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
}
// TestH1H2AsteriskPrefix tests that a path starting with "*" in HTTP
// request.
func TestH1H2AsteriskPrefix(t *testing.T) {
opts := options{
args: []string{"--http2-bridge"},
}
st := newServerTester(t, opts)
defer st.Close()
if _, err := io.WriteString(st.conn, "OPTIONS *foo HTTP/1.1\r\nTest-Case: TestH1H2AsteriskPrefix\r\nHost: 127.0.0.1\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusBadRequest; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
}
// // TestH1H2ConnectFailure tests that server handles the situation that
// // connection attempt to HTTP/2 backend failed.
// func TestH1H2ConnectFailure(t *testing.T) {
@@ -1748,3 +1970,33 @@ func TestH1H1RequestHTTP10TransferEncoding(t *testing.T) {
t.Errorf("status: %v; want %v", got, want)
}
}
// TestH1H2BadHost tests that invalid character in host is treated as
// bad request.
func TestH1H2BadHost(t *testing.T) {
opts := options{
args: []string{"--http2-bridge"},
}
st := newServerTester(t, opts)
defer st.Close()
if _, err := io.WriteString(st.conn, "GET / HTTP/1.1\r\nTest-Case: TestH1H2BadHost\r\nHost: 127.0.0.1\x8c\r\n\r\n"); err != nil {
t.Fatalf("Error io.WriteString() = %v", err)
}
resp, err := http.ReadResponse(bufio.NewReader(st.conn), nil)
if err != nil {
t.Fatalf("Error http.ReadResponse() = %v", err)
}
defer resp.Body.Close()
if got, want := resp.StatusCode, http.StatusBadRequest; got != want {
t.Errorf("status: %v; want %v", got, want)
}
if _, err := io.ReadAll(resp.Body); err != nil {
t.Fatalf("Error io.ReadAll() = %v", err)
}
}

View File

@@ -323,14 +323,14 @@ func (st *serverTester) Close() {
close(done)
}()
if err := st.cmd.Process.Signal(syscall.SIGQUIT); err != nil {
if err := st.cmd.Process.Signal(syscall.SIGQUIT); err != nil && !errors.Is(err, os.ErrProcessDone) {
st.t.Errorf("Error st.cmd.Process.Signal() = %v", err)
}
select {
case <-done:
case <-time.After(10 * time.Second):
if err := st.cmd.Process.Kill(); err != nil {
if err := st.cmd.Process.Kill(); err != nil && !errors.Is(err, os.ErrProcessDone) {
st.t.Errorf("Error st.cmd.Process.Kill() = %v", err)
}

View File

@@ -51,7 +51,7 @@ set(NGHTTP2_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
set(NGHTTP2_VERSION_CONFIG "${NGHTTP2_GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
set(NGHTTP2_PROJECT_CONFIG "${NGHTTP2_GENERATED_DIR}/${PROJECT_NAME}Config.cmake")
set(NGHTTP2_TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
set(NGHTTP2_CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
set(NGHTTP2_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
set(NGHTTP2_NAMESPACE "${PROJECT_NAME}::")
set(NGHTTP2_VERSION ${PROJECT_VERSION})

View File

@@ -3252,7 +3252,7 @@ NGHTTP2_EXTERN void nghttp2_option_set_max_continuations(nghttp2_option *option,
* regenerated per second. When a suspicious activity is detected,
* some amount of tokens are consumed. If there is no token
* available, GOAWAY is sent to tear down the connection. |burst| and
* |rate| default to 1000 and 33 respectively.
* |rate| default to 10000 and 330 respectively.
*/
NGHTTP2_EXTERN void nghttp2_option_set_glitch_rate_limit(nghttp2_option *option,
uint64_t burst,

View File

@@ -26,6 +26,8 @@
#include <string.h>
#include "nghttp2_helper.h"
static int select_alpn(const unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen,
const char *key, unsigned int keylen) {
@@ -41,7 +43,7 @@ static int select_alpn(const unsigned char **out, unsigned char *outlen,
}
#define NGHTTP2_HTTP_1_1_ALPN "\x8http/1.1"
#define NGHTTP2_HTTP_1_1_ALPN_LEN (sizeof(NGHTTP2_HTTP_1_1_ALPN) - 1)
#define NGHTTP2_HTTP_1_1_ALPN_LEN nghttp2_strlen_lit(NGHTTP2_HTTP_1_1_ALPN)
int nghttp2_select_next_protocol(unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen) {

View File

@@ -34,10 +34,7 @@
#include "nghttp2_buf.h"
#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1)
#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1)
#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1)
#define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1)
#define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1)
/* The number of bytes of frame header. */
#define NGHTTP2_FRAME_HDLEN 9

View File

@@ -35,9 +35,10 @@
/* Make scalar initialization form of nghttp2_hd_entry */
#define MAKE_STATIC_ENT(N, V, T, H) \
{ \
{NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \
{NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \
{(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \
{NULL, NULL, (uint8_t *)(N), nghttp2_strlen_lit((N)), -1}, \
{NULL, NULL, (uint8_t *)(V), nghttp2_strlen_lit((V)), -1}, \
{(uint8_t *)(N), (uint8_t *)(V), nghttp2_strlen_lit((N)), \
nghttp2_strlen_lit((V)), 0}, \
T, \
H, \
}

View File

@@ -104,15 +104,15 @@ int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src,
}
void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) {
ctx->fstate = NGHTTP2_HUFF_ACCEPTED;
ctx->fstate = 0;
ctx->flags = NGHTTP2_HUFF_ACCEPTED;
}
nghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
nghttp2_buf *buf, const uint8_t *src,
size_t srclen, int final) {
const uint8_t *end = src + srclen;
nghttp2_huff_decode node = {ctx->fstate, 0};
const nghttp2_huff_decode *t = &node;
nghttp2_huff_decode t = {ctx->fstate, ctx->flags, 0};
uint8_t c;
/* We use the decoding algorithm described in
@@ -121,20 +121,21 @@ nghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx,
- https://github.com/nghttp2/nghttp2/files/15141264/Prefix.pdf */
for (; src != end;) {
c = *src++;
t = &huff_decode_table[t->fstate & 0x1ff][c >> 4];
if (t->fstate & NGHTTP2_HUFF_SYM) {
*buf->last++ = t->sym;
t = huff_decode_table[t.fstate][c >> 4];
if (t.flags & NGHTTP2_HUFF_SYM) {
*buf->last++ = t.sym;
}
t = &huff_decode_table[t->fstate & 0x1ff][c & 0xf];
if (t->fstate & NGHTTP2_HUFF_SYM) {
*buf->last++ = t->sym;
t = huff_decode_table[t.fstate][c & 0xf];
if (t.flags & NGHTTP2_HUFF_SYM) {
*buf->last++ = t.sym;
}
}
ctx->fstate = t->fstate;
ctx->fstate = t.fstate;
ctx->flags = t.flags;
if (final && !(ctx->fstate & NGHTTP2_HUFF_ACCEPTED)) {
if (final && !(ctx->flags & NGHTTP2_HUFF_ACCEPTED)) {
return NGHTTP2_ERR_HEADER_COMP;
}

View File

@@ -34,9 +34,9 @@
typedef enum {
/* FSA accepts this state as the end of huffman encoding
sequence. */
NGHTTP2_HUFF_ACCEPTED = 1 << 14,
NGHTTP2_HUFF_ACCEPTED = 1,
/* This state emits symbol */
NGHTTP2_HUFF_SYM = 1 << 15,
NGHTTP2_HUFF_SYM = 1 << 1,
} nghttp2_huff_decode_flag;
typedef struct {
@@ -48,6 +48,7 @@ typedef struct {
a special node and it is a terminal state that means decoding
failed. */
uint16_t fstate;
uint8_t flags;
/* symbol if NGHTTP2_HUFF_SYM flag set */
uint8_t sym;
} nghttp2_huff_decode;
@@ -57,6 +58,7 @@ typedef nghttp2_huff_decode huff_decode_table_type[16];
typedef struct {
/* fstate is the current huffman decoding state. */
uint16_t fstate;
uint8_t flags;
} nghttp2_hd_huff_decode_context;
typedef struct {

File diff suppressed because it is too large Load Diff

View File

@@ -61,7 +61,15 @@ nghttp2_min_def(uint32, uint32_t)
nghttp2_min_def(uint64, uint64_t)
nghttp2_min_def(size, size_t)
#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0)
/*
* nghttp2_strlen_lit returns the length of string literal |S|. This
* macro assumes |S| is NULL-terminated string literal. It must not
* be used with pointers.
*/
#define nghttp2_strlen_lit(S) (sizeof(S) - 1)
#define lstreq(A, B, N) \
(nghttp2_strlen_lit((A)) == (N) && memcmp((A), (B), (N)) == 0)
#define nghttp2_struct_of(ptr, type, member) \
((type *)(void *)((char *)(ptr) - offsetof(type, member)))

View File

@@ -49,7 +49,8 @@ static int memieq(const void *a, const void *b, size_t n) {
return 1;
}
#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N)))
#define lstrieq(A, B, N) \
(nghttp2_strlen_lit((A)) == (N) && memieq((A), (B), (N)))
static int64_t parse_uint(const uint8_t *s, size_t len) {
int64_t n = 0;

View File

@@ -39,7 +39,6 @@ typedef int (*nghttp2_less)(const void *lhs, const void *rhs);
/* Internal error code. They must be in the range [-499, -100],
inclusive. */
typedef enum {
NGHTTP2_ERR_CREDENTIAL_PENDING = -101,
NGHTTP2_ERR_IGN_HEADER_BLOCK = -103,
NGHTTP2_ERR_IGN_PAYLOAD = -104,
/*

View File

@@ -33,12 +33,11 @@
#define NGHTTP2_INITIAL_HASHBITS 4
void nghttp2_map_init(nghttp2_map *map, uint32_t seed, nghttp2_mem *mem) {
map->mem = mem;
map->hashbits = 0;
map->table = NULL;
map->seed = seed;
map->size = 0;
void nghttp2_map_init(nghttp2_map *map, uint64_t seed, nghttp2_mem *mem) {
*map = (nghttp2_map){
.mem = mem,
.seed = seed,
};
}
void nghttp2_map_free(nghttp2_map *map) {
@@ -46,30 +45,27 @@ void nghttp2_map_free(nghttp2_map *map) {
return;
}
nghttp2_mem_free(map->mem, map->table);
nghttp2_mem_free(map->mem, map->keys);
}
int nghttp2_map_each(const nghttp2_map *map, int (*func)(void *data, void *ptr),
void *ptr) {
int rv;
size_t i;
nghttp2_map_bucket *bkt;
size_t tablelen;
if (map->size == 0) {
return 0;
}
tablelen = 1u << map->hashbits;
tablelen = (size_t)1 << map->hashbits;
for (i = 0; i < tablelen; ++i) {
bkt = &map->table[i];
if (bkt->data == NULL) {
if (map->psl[i] == 0) {
continue;
}
rv = func(bkt->data, ptr);
rv = func(map->data[i], ptr);
if (rv != 0) {
return rv;
}
@@ -78,175 +74,230 @@ int nghttp2_map_each(const nghttp2_map *map, int (*func)(void *data, void *ptr),
return 0;
}
static size_t map_hash(const nghttp2_map *map, nghttp2_map_key_type key) {
/* hasher from
https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs
We do not perform finalization here because we use top bits
anyway. */
uint32_t h = ((uint32_t)key + map->seed) * 0x93d765dd;
return (size_t)((h * 2654435769u) >> (32 - map->hashbits));
}
/* Hasher from
https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs
to maximize the output's sensitivity to all input bits. */
#define NGHTTP2_MAP_HASHER 0xf1357aea2e62a9c5ull
/* 64-bit Fibonacci hashing constant, Golden Ratio constant, to get
the high bits with the good distribution. */
#define NGHTTP2_MAP_FIBO 0x9e3779b97f4a7c15ull
static void map_bucket_swap(nghttp2_map_bucket *a, nghttp2_map_bucket *b) {
nghttp2_map_bucket c = *a;
static size_t map_index(const nghttp2_map *map, nghttp2_map_key_type key32) {
uint64_t key = (uint64_t)key32;
*a = *b;
*b = c;
key += map->seed;
key *= NGHTTP2_MAP_HASHER;
return (size_t)((key * NGHTTP2_MAP_FIBO) >> (64 - map->hashbits));
}
#ifndef WIN32
void nghttp2_map_print_distance(const nghttp2_map *map) {
size_t i;
size_t idx;
nghttp2_map_bucket *bkt;
size_t tablelen;
if (map->size == 0) {
return;
}
tablelen = 1u << map->hashbits;
tablelen = (size_t)1 << map->hashbits;
for (i = 0; i < tablelen; ++i) {
bkt = &map->table[i];
if (bkt->data == NULL) {
if (map->psl[i] == 0) {
fprintf(stderr, "@%zu <EMPTY>\n", i);
continue;
}
idx = map_hash(map, bkt->key);
fprintf(stderr, "@%zu hash=%zu key=%d base=%zu distance=%u\n", i,
map_hash(map, bkt->key), bkt->key, idx, bkt->psl);
idx = map_index(map, map->keys[i]);
fprintf(stderr, "@%zu key=%d base=%zu distance=%u\n", i, map->keys[i], idx,
map->psl[i] - 1);
}
}
#endif /* !defined(WIN32) */
static int map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) {
size_t idx = map_hash(map, key);
nghttp2_map_bucket b = {
.key = key,
.data = data,
};
nghttp2_map_bucket *bkt;
size_t mask = (1u << map->hashbits) - 1;
static void map_set_entry(nghttp2_map *map, size_t idx,
nghttp2_map_key_type key, void *data, size_t psl) {
map->keys[idx] = key;
map->data[idx] = data;
map->psl[idx] = (uint8_t)psl;
}
#define NGHTTP2_SWAP(TYPE, A, B) \
do { \
TYPE t = (TYPE) * (A); \
\
*(A) = *(B); \
*(B) = t; \
} while (0)
/*
* map_insert inserts |key| and |data| to |map|, and returns the index
* where the pair is stored if it succeeds. Otherwise, it returns one
* of the following negative error codes:
*
* NGHTTP2_ERR_INVALID_ARGUMENT
* The another data associated to |key| is already present.
*/
static nghttp2_ssize map_insert(nghttp2_map *map, nghttp2_map_key_type key,
void *data) {
size_t idx = map_index(map, key);
size_t mask = ((size_t)1 << map->hashbits) - 1;
size_t psl = 1;
size_t kpsl;
for (;;) {
bkt = &map->table[idx];
kpsl = map->psl[idx];
if (bkt->data == NULL) {
*bkt = b;
if (kpsl == 0) {
map_set_entry(map, idx, key, data, psl);
++map->size;
return 0;
return (nghttp2_ssize)idx;
}
if (b.psl > bkt->psl) {
map_bucket_swap(bkt, &b);
} else if (bkt->key == key) {
/* TODO This check is just a waste after first swap or if this
function is called from map_resize. That said, there is no
difference with or without this conditional in performance
wise. */
if (psl > kpsl) {
NGHTTP2_SWAP(nghttp2_map_key_type, &key, &map->keys[idx]);
NGHTTP2_SWAP(void *, &data, &map->data[idx]);
NGHTTP2_SWAP(uint8_t, &psl, &map->psl[idx]);
} else if (map->keys[idx] == key) {
/* This check ensures that no duplicate keys are inserted. But
it is just a waste after first swap or if this function is
called from map_resize. That said, there is no difference
with or without this conditional in performance wise. */
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
++b.psl;
++psl;
idx = (idx + 1) & mask;
}
}
/* NGHTTP2_MAP_MAX_HASHBITS is the maximum number of bits used for
hash table. The theoretical limit of the maximum number of keys
that can be stored is 1 << NGHTTP2_MAP_MAX_HASHBITS. */
#define NGHTTP2_MAP_MAX_HASHBITS (sizeof(size_t) * 8 - 1)
static int map_resize(nghttp2_map *map, size_t new_hashbits) {
size_t i;
nghttp2_map_bucket *bkt;
size_t tablelen;
int rv;
nghttp2_ssize idx;
nghttp2_map new_map = {
.table = nghttp2_mem_calloc(map->mem, 1u << new_hashbits,
sizeof(nghttp2_map_bucket)),
.mem = map->mem,
.seed = map->seed,
.hashbits = new_hashbits,
};
(void)rv;
void *buf;
(void)idx;
if (new_map.table == NULL) {
if (new_hashbits > NGHTTP2_MAP_MAX_HASHBITS) {
return NGHTTP2_ERR_NOMEM;
}
tablelen = (size_t)1 << new_hashbits;
buf = nghttp2_mem_calloc(map->mem, tablelen,
sizeof(nghttp2_map_key_type) + sizeof(void *) +
sizeof(uint8_t));
if (buf == NULL) {
return NGHTTP2_ERR_NOMEM;
}
new_map.keys = buf;
new_map.data =
(void *)((uint8_t *)new_map.keys + tablelen * sizeof(nghttp2_map_key_type));
new_map.psl = (uint8_t *)new_map.data + tablelen * sizeof(void *);
if (map->size) {
tablelen = 1u << map->hashbits;
tablelen = (size_t)1 << map->hashbits;
for (i = 0; i < tablelen; ++i) {
bkt = &map->table[i];
if (bkt->data == NULL) {
if (map->psl[i] == 0) {
continue;
}
rv = map_insert(&new_map, bkt->key, bkt->data);
idx = map_insert(&new_map, map->keys[i], map->data[i]);
assert(0 == rv);
/* map_insert must not fail because all keys are unique during
resize. */
assert(idx >= 0);
}
}
nghttp2_mem_free(map->mem, map->table);
map->table = new_map.table;
nghttp2_mem_free(map->mem, map->keys);
map->keys = new_map.keys;
map->data = new_map.data;
map->psl = new_map.psl;
map->hashbits = new_hashbits;
return 0;
}
/* NGHTTP2_MAX_PSL_RESIZE_THRESH is the maximum psl threshold. If
reached, resize the table. */
#define NGHTTP2_MAX_PSL_RESIZE_THRESH 128
int nghttp2_map_insert(nghttp2_map *map, nghttp2_map_key_type key, void *data) {
int rv;
size_t tablelen;
nghttp2_ssize idx;
assert(data);
/* Load factor is 7/8 */
/* Under the very initial condition, that is map->size == 0 and
map->hashbits == 0, 8 > 7 still holds nicely. */
if ((map->size + 1) * 8 > (1u << map->hashbits) * 7) {
if (map->hashbits) {
rv = map_resize(map, map->hashbits + 1);
if (rv != 0) {
return rv;
}
} else {
rv = map_resize(map, NGHTTP2_INITIAL_HASHBITS);
if (rv != 0) {
return rv;
}
/* tablelen is incorrect if map->hashbits == 0 which leads to
tablelen = 1, but it is only used to check the load factor, and
it works in this special case. */
tablelen = (size_t)1 << map->hashbits;
/* Load factor is 7 / 8. Because tablelen is power of 2, (tablelen
- (tablelen >> 3)) computes tablelen * 7 / 8. */
if (map->size + 1 >= (tablelen - (tablelen >> 3))) {
rv = map_resize(map, map->hashbits ? map->hashbits + 1
: NGHTTP2_INITIAL_HASHBITS);
if (rv != 0) {
return rv;
}
idx = map_insert(map, key, data);
if (idx < 0) {
return (int)idx;
}
return 0;
}
rv = map_insert(map, key, data);
if (rv != 0) {
return rv;
idx = map_insert(map, key, data);
if (idx < 0) {
return (int)idx;
}
return 0;
/* Resize if psl reaches really large value which is almost
improbable, but just in case. */
if (map->psl[idx] - 1 < NGHTTP2_MAX_PSL_RESIZE_THRESH) {
return 0;
}
return map_resize(map, map->hashbits + 1);
}
void *nghttp2_map_find(const nghttp2_map *map, nghttp2_map_key_type key) {
size_t idx;
nghttp2_map_bucket *bkt;
size_t psl = 0;
size_t psl = 1;
size_t mask;
if (map->size == 0) {
return NULL;
}
idx = map_hash(map, key);
mask = (1u << map->hashbits) - 1;
idx = map_index(map, key);
mask = ((size_t)1 << map->hashbits) - 1;
for (;;) {
bkt = &map->table[idx];
if (bkt->data == NULL || psl > bkt->psl) {
if (psl > map->psl[idx]) {
return NULL;
}
if (bkt->key == key) {
return bkt->data;
if (map->keys[idx] == key) {
return map->data[idx];
}
++psl;
@@ -256,38 +307,36 @@ void *nghttp2_map_find(const nghttp2_map *map, nghttp2_map_key_type key) {
int nghttp2_map_remove(nghttp2_map *map, nghttp2_map_key_type key) {
size_t idx;
nghttp2_map_bucket *b, *bkt;
size_t psl = 0;
size_t dest;
size_t psl = 1, kpsl;
size_t mask;
if (map->size == 0) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
idx = map_hash(map, key);
mask = (1u << map->hashbits) - 1;
idx = map_index(map, key);
mask = ((size_t)1 << map->hashbits) - 1;
for (;;) {
bkt = &map->table[idx];
if (bkt->data == NULL || psl > bkt->psl) {
if (psl > map->psl[idx]) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
if (bkt->key == key) {
b = bkt;
if (map->keys[idx] == key) {
dest = idx;
idx = (idx + 1) & mask;
for (;;) {
bkt = &map->table[idx];
if (bkt->data == NULL || bkt->psl == 0) {
b->data = NULL;
kpsl = map->psl[idx];
if (kpsl <= 1) {
map->psl[dest] = 0;
break;
}
--bkt->psl;
*b = *bkt;
b = bkt;
map_set_entry(map, dest, map->keys[idx], map->data[idx], kpsl - 1);
dest = idx;
idx = (idx + 1) & mask;
}
@@ -307,7 +356,7 @@ void nghttp2_map_clear(nghttp2_map *map) {
return;
}
memset(map->table, 0, sizeof(*map->table) * (1u << map->hashbits));
memset(map->psl, 0, sizeof(*map->psl) * ((size_t)1 << map->hashbits));
map->size = 0;
}

View File

@@ -38,16 +38,15 @@
typedef int32_t nghttp2_map_key_type;
typedef struct nghttp2_map_bucket {
uint32_t psl;
nghttp2_map_key_type key;
void *data;
} nghttp2_map_bucket;
typedef struct nghttp2_map {
nghttp2_map_bucket *table;
nghttp2_map_key_type *keys;
void **data;
/* psl is the Probe Sequence Length. 0 has special meaning that the
element is not stored at i-th position if psl[i] == 0. Because
of this, the actual psl value is psl[i] - 1 if psl[i] > 0. */
uint8_t *psl;
nghttp2_mem *mem;
uint32_t seed;
uint64_t seed;
size_t size;
size_t hashbits;
} nghttp2_map;
@@ -55,7 +54,7 @@ typedef struct nghttp2_map {
/*
* nghttp2_map_init initializes the map |map|.
*/
void nghttp2_map_init(nghttp2_map *map, uint32_t seed, nghttp2_mem *mem);
void nghttp2_map_init(nghttp2_map *map, uint64_t seed, nghttp2_mem *mem);
/*
* nghttp2_map_free deallocates any resources allocated for |map|.

View File

@@ -53,6 +53,18 @@ nghttp2_data_provider_wrap_v2(nghttp2_data_provider_wrap *dpw,
return dpw;
}
int nghttp2_data_provider_wrap_contains_read_callback(
const nghttp2_data_provider_wrap *dpw) {
switch (dpw->version) {
case NGHTTP2_DATA_PROVIDER_V1:
return dpw->data_prd.v1.read_callback != NULL;
case NGHTTP2_DATA_PROVIDER_V2:
return dpw->data_prd.v2.read_callback != NULL;
default:
return 0;
}
}
void nghttp2_outbound_item_init(nghttp2_outbound_item *item) {
item->cycle = 0;
item->qnext = NULL;

View File

@@ -39,10 +39,6 @@
typedef struct nghttp2_data_provider_wrap {
int version;
union {
struct {
nghttp2_data_source source;
void *read_callback;
};
nghttp2_data_provider v1;
nghttp2_data_provider2 v2;
} data_prd;
@@ -56,6 +52,11 @@ nghttp2_data_provider_wrap *
nghttp2_data_provider_wrap_v2(nghttp2_data_provider_wrap *dpw,
const nghttp2_data_provider2 *data_prd);
/* nghttp2_data_provider_wrap_contains_read_callback returns nonzero
if |dpw| contains read_callback in either version. */
int nghttp2_data_provider_wrap_contains_read_callback(
const nghttp2_data_provider_wrap *dpw);
/* struct used for HEADERS and PUSH_PROMISE frame */
typedef struct {
nghttp2_data_provider_wrap dpw;
@@ -110,6 +111,12 @@ typedef struct {
uint8_t flags;
} nghttp2_goaway_aux_data;
typedef struct {
/* nonzero if RST_STREAM should be sent even if stream is not
found. */
uint8_t continue_without_stream;
} nghttp2_rst_stream_aux_data;
/* struct used for extension frame */
typedef struct {
/* nonzero if this extension frame is serialized by library
@@ -122,6 +129,7 @@ typedef union {
nghttp2_data_aux_data data;
nghttp2_headers_aux_data headers;
nghttp2_goaway_aux_data goaway;
nghttp2_rst_stream_aux_data rst_stream;
nghttp2_ext_aux_data ext;
} nghttp2_aux_data;

View File

@@ -438,7 +438,7 @@ static int session_new(nghttp2_session **session_ptr,
size_t max_deflate_dynamic_table_size =
NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE;
size_t i;
uint32_t map_seed;
uint64_t map_seed;
if (mem == NULL) {
mem = nghttp2_mem_default();
@@ -1051,7 +1051,6 @@ int nghttp2_session_add_item(nghttp2_session *session,
nghttp2_outbound_queue_push(&session->ob_syn, item);
item->queued = 1;
return 0;
;
}
nghttp2_outbound_queue_push(&session->ob_reg, item);
@@ -1189,6 +1188,10 @@ int nghttp2_session_add_rst_stream_continue(nghttp2_session *session,
frame = &item->frame;
nghttp2_frame_rst_stream_init(&frame->rst_stream, stream_id, error_code);
item->aux_data.rst_stream.continue_without_stream =
(uint8_t)(continue_without_stream != 0);
rv = nghttp2_session_add_item(session, item);
if (rv != 0) {
nghttp2_frame_rst_stream_free(&frame->rst_stream);
@@ -2141,6 +2144,12 @@ static int session_prep_frame(nghttp2_session *session,
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
if (!item->aux_data.rst_stream.continue_without_stream &&
!nghttp2_session_get_stream(session, frame->rst_stream.hd.stream_id)) {
return NGHTTP2_ERR_STREAM_CLOSED;
}
nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream);
return 0;
case NGHTTP2_SETTINGS: {
@@ -2584,7 +2593,7 @@ static int session_after_frame_sent1(nghttp2_session *session) {
}
/* We assume aux_data is a pointer to nghttp2_headers_aux_data */
aux_data = &item->aux_data.headers;
if (aux_data->dpw.data_prd.read_callback) {
if (nghttp2_data_provider_wrap_contains_read_callback(&aux_data->dpw)) {
/* nghttp2_submit_data_shared() makes a copy of
aux_data->dpw */
rv = nghttp2_submit_data_shared(session, NGHTTP2_FLAG_END_STREAM,
@@ -2615,7 +2624,7 @@ static int session_after_frame_sent1(nghttp2_session *session) {
}
/* We assume aux_data is a pointer to nghttp2_headers_aux_data */
aux_data = &item->aux_data.headers;
if (aux_data->dpw.data_prd.read_callback) {
if (nghttp2_data_provider_wrap_contains_read_callback(&aux_data->dpw)) {
rv = nghttp2_submit_data_shared(session, NGHTTP2_FLAG_END_STREAM,
frame->hd.stream_id, &aux_data->dpw);
if (nghttp2_is_fatal(rv)) {
@@ -2795,7 +2804,10 @@ static int session_call_send_data(nghttp2_session *session,
aux_data = &item->aux_data.data;
rv = session->callbacks.send_data_callback(session, frame, buf->pos, length,
&aux_data->dpw.data_prd.source,
/* This is fine because
of Common Initial
Sequence rule. */
&aux_data->dpw.data_prd.v2.source,
session->user_data);
switch (rv) {
@@ -2853,8 +2865,12 @@ static nghttp2_ssize nghttp2_session_mem_send_internal(nghttp2_session *session,
nghttp2_frame *frame = &item->frame;
/* The library is responsible for the transmission of
WINDOW_UPDATE frame, so we don't call error callback for
it. */
it. As for RST_STREAM, if it is not sent due to missing
stream, we also do not call error callback because it may
cause a lot of noises.*/
if (frame->hd.type != NGHTTP2_WINDOW_UPDATE &&
(frame->hd.type != NGHTTP2_RST_STREAM ||
rv != NGHTTP2_ERR_STREAM_CLOSED) &&
session->callbacks.on_frame_not_send_callback(
session, frame, rv, session->user_data) != 0) {
nghttp2_outbound_item_free(item, mem);
@@ -5477,15 +5493,6 @@ nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session,
DEBUGF("recv: DATA not allowed stream_id=%d\n",
iframe->frame.hd.stream_id);
rv = session_update_glitch_ratelim(session);
if (rv != 0) {
return rv;
}
if (iframe->state == NGHTTP2_IB_IGN_ALL) {
return (nghttp2_ssize)inlen;
}
iframe->state = NGHTTP2_IB_IGN_DATA;
break;
}
@@ -7402,13 +7409,13 @@ int nghttp2_session_pack_data(nghttp2_session *session, nghttp2_bufs *bufs,
case NGHTTP2_DATA_PROVIDER_V1:
payloadlen = (nghttp2_ssize)aux_data->dpw.data_prd.v1.read_callback(
session, frame->hd.stream_id, buf->pos, datamax, &data_flags,
&aux_data->dpw.data_prd.source, session->user_data);
&aux_data->dpw.data_prd.v1.source, session->user_data);
break;
case NGHTTP2_DATA_PROVIDER_V2:
payloadlen = aux_data->dpw.data_prd.v2.read_callback(
session, frame->hd.stream_id, buf->pos, datamax, &data_flags,
&aux_data->dpw.data_prd.source, session->user_data);
&aux_data->dpw.data_prd.v2.source, session->user_data);
break;
default:

View File

@@ -107,8 +107,8 @@ typedef struct {
#define NGHTTP2_DEFAULT_STREAM_RESET_RATE 33
/* The default values for glitch rate limiter. */
#define NGHTTP2_DEFAULT_GLITCH_BURST 1000
#define NGHTTP2_DEFAULT_GLITCH_RATE 33
#define NGHTTP2_DEFAULT_GLITCH_BURST 10000
#define NGHTTP2_DEFAULT_GLITCH_RATE 330
/* The default max number of CONTINUATION frames following an incoming
HEADER frame. */

View File

@@ -57,7 +57,7 @@ static int32_t submit_headers_shared(nghttp2_session *session, uint8_t flags,
nghttp2_outbound_item_init(item);
if (dpw != NULL && dpw->data_prd.read_callback != NULL) {
if (dpw != NULL && nghttp2_data_provider_wrap_contains_read_callback(dpw)) {
item->aux_data.headers.dpw = *dpw;
}
@@ -649,7 +649,7 @@ fail_item_malloc:
static uint8_t set_request_flags(const nghttp2_data_provider_wrap *dpw) {
uint8_t flags = NGHTTP2_FLAG_NONE;
if (dpw == NULL || dpw->data_prd.read_callback == NULL) {
if (dpw == NULL || !nghttp2_data_provider_wrap_contains_read_callback(dpw)) {
flags |= NGHTTP2_FLAG_END_STREAM;
}
@@ -700,7 +700,7 @@ int32_t nghttp2_submit_request2(nghttp2_session *session,
static uint8_t set_response_flags(const nghttp2_data_provider_wrap *dpw) {
uint8_t flags = NGHTTP2_FLAG_NONE;
if (dpw == NULL || dpw->data_prd.read_callback == NULL) {
if (dpw == NULL || !nghttp2_data_provider_wrap_contains_read_callback(dpw)) {
flags |= NGHTTP2_FLAG_END_STREAM;
}
return flags;

View File

@@ -356,8 +356,8 @@ def _build_transition_table(ctx, node):
def huffman_tree_build_transition_table(ctx):
_build_transition_table(ctx, ctx.root)
NGHTTP2_HUFF_ACCEPTED = 1 << 14
NGHTTP2_HUFF_SYM = 1 << 15
NGHTTP2_HUFF_ACCEPTED = 1
NGHTTP2_HUFF_SYM = 1 << 1
def _print_transition_table(node):
if node.term is not None:
@@ -381,7 +381,7 @@ def _print_transition_table(node):
flags |= NGHTTP2_HUFF_ACCEPTED
elif nd.accept:
flags |= NGHTTP2_HUFF_ACCEPTED
print(' {{0x{:02x}, {}}},'.format(id | flags, out))
print(' {{0x{:02x}, {}, {}}},'.format(id, flags, out))
print('},')
_print_transition_table(node.left)
_print_transition_table(node.right)
@@ -390,22 +390,10 @@ def huffman_tree_print_transition_table(ctx):
_print_transition_table(ctx.root)
print('/* 256 */')
print('{')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
print(' {0x100, 0},')
for _ in range(16):
print(' {0x100, 0, 0},')
print('},')
if __name__ == '__main__':
@@ -458,6 +446,7 @@ enum {{
print('''\
typedef struct {
uint16_t fstate;
uint8_t flags;
uint8_t sym;
} nghttp2_huff_decode;
''')

View File

@@ -56,6 +56,7 @@ if(ENABLE_APP)
set(HELPER_OBJECTS
util.cc
http2.cc timegm.c app_helper.cc nghttp2_gzip.c
network.cc
)
# nghttp client
@@ -82,6 +83,7 @@ if(ENABLE_APP)
http2.cc h2load.cc
timegm.c
tls.cc
network.cc
h2load_http2_session.cc
h2load_http1_session.cc
)
@@ -97,6 +99,7 @@ if(ENABLE_APP)
util.cc http2.cc timegm.c
app_helper.cc
tls.cc
network.cc
shrpx_config.cc
shrpx_accept_handler.cc
shrpx_connection_handler.cc
@@ -190,6 +193,7 @@ if(ENABLE_APP)
memchunk_test.cc
template_test.cc
base64_test.cc
network_test.cc
${CMAKE_SOURCE_DIR}/tests/munit/munit.c
)
if(ENABLE_HTTP3)
@@ -249,6 +253,8 @@ if(ENABLE_HPACK_TOOLS)
comp_helper.c
util.cc
timegm.c
tls.cc
network.cc
)
add_executable(inflatehd ${inflatehd_SOURCES})
add_executable(deflatehd ${deflatehd_SOURCES})

View File

@@ -39,7 +39,7 @@ HtmlParser::HtmlParser(const std::string &base_uri)
HtmlParser::~HtmlParser() { htmlFreeParserCtxt(parser_ctx_); }
namespace {
std::string_view get_attr(const xmlChar **attrs, const std::string_view &name) {
std::string_view get_attr(const xmlChar **attrs, std::string_view name) {
if (attrs == nullptr) {
return ""sv;
}
@@ -55,7 +55,7 @@ std::string_view get_attr(const xmlChar **attrs, const std::string_view &name) {
namespace {
ResourceType
get_resource_type_for_preload_as(const std::string_view &attribute_value) {
get_resource_type_for_preload_as(std::string_view attribute_value) {
if (util::strieq("image"sv, attribute_value)) {
return REQ_IMG;
} else if (util::strieq("style"sv, attribute_value)) {
@@ -69,7 +69,7 @@ get_resource_type_for_preload_as(const std::string_view &attribute_value) {
} // namespace
namespace {
void add_link(ParserData *parser_data, const std::string_view &uri,
void add_link(ParserData *parser_data, std::string_view uri,
ResourceType res_type) {
auto u = xmlBuildURI(
reinterpret_cast<const xmlChar *>(uri.data()),
@@ -175,27 +175,30 @@ xmlSAXHandler saxHandler = {
};
} // namespace
int HtmlParser::parse_chunk(const char *chunk, size_t size, int fin) {
int HtmlParser::parse_chunk(std::span<const uint8_t> chunk, int fin) {
if (!parser_ctx_) {
parser_ctx_ = htmlCreatePushParserCtxt(
&saxHandler, &parser_data_, chunk, static_cast<int>(size),
base_uri_.c_str(), XML_CHAR_ENCODING_NONE);
&saxHandler, &parser_data_, reinterpret_cast<const char *>(chunk.data()),
static_cast<int>(chunk.size()), base_uri_.c_str(),
XML_CHAR_ENCODING_NONE);
if (!parser_ctx_) {
return -1;
} else {
if (fin) {
return parse_chunk_internal(nullptr, 0, fin);
return parse_chunk_internal({}, fin);
} else {
return 0;
}
}
} else {
return parse_chunk_internal(chunk, size, fin);
return parse_chunk_internal(chunk, fin);
}
}
int HtmlParser::parse_chunk_internal(const char *chunk, size_t size, int fin) {
int rv = htmlParseChunk(parser_ctx_, chunk, static_cast<int>(size), fin);
int HtmlParser::parse_chunk_internal(std::span<const uint8_t> chunk, int fin) {
int rv =
htmlParseChunk(parser_ctx_, reinterpret_cast<const char *>(chunk.data()),
static_cast<int>(chunk.size()), fin);
if (rv == 0) {
return 0;
} else {

View File

@@ -27,8 +27,10 @@
#include "nghttp2_config.h"
#include <cstdint>
#include <vector>
#include <string>
#include <span>
#ifdef HAVE_LIBXML2
# include <libxml/HTMLparser.h>
@@ -58,12 +60,12 @@ class HtmlParser {
public:
HtmlParser(const std::string &base_uri);
~HtmlParser();
int parse_chunk(const char *chunk, size_t size, int fin);
int parse_chunk(std::span<const uint8_t> chunk, int fin);
const std::vector<std::pair<std::string, ResourceType>> &get_links() const;
void clear_links();
private:
int parse_chunk_internal(const char *chunk, size_t size, int fin);
int parse_chunk_internal(std::span<const uint8_t> chunk, int fin);
std::string base_uri_;
htmlParserCtxtPtr parser_ctx_;
@@ -75,7 +77,7 @@ private:
class HtmlParser {
public:
HtmlParser(const std::string &base_uri) {}
int parse_chunk(const char *chunk, size_t size, int fin) { return 0; }
int parse_chunk(std::span<const uint8_t> chunk, int fin) { return 0; }
const std::vector<std::pair<std::string, ResourceType>> &get_links() const {
return links_;
}

View File

@@ -545,8 +545,6 @@ Http2Handler::Http2Handler(Sessions *sessions, int fd, SSL *ssl,
session_(nullptr),
sessions_(sessions),
ssl_(ssl),
data_pending_(nullptr),
data_pendinglen_(0),
fd_(fd) {
ev_timer_init(&settings_timerev_, settings_timeout_cb, 10., 0.);
ev_io_init(&wev_, writecb, fd, EV_WRITE);
@@ -599,17 +597,14 @@ void Http2Handler::start_settings_timer() {
}
int Http2Handler::fill_wb() {
if (data_pending_) {
auto n = std::min(wb_.wleft(), data_pendinglen_);
wb_.write(data_pending_, n);
if (n < data_pendinglen_) {
data_pending_ += n;
data_pendinglen_ -= n;
if (!data_pending_.empty()) {
auto n = wb_.write(data_pending_);
if (n < data_pending_.size()) {
data_pending_ = data_pending_.subspan(n);
return 0;
}
data_pending_ = nullptr;
data_pendinglen_ = 0;
data_pending_ = {};
}
for (;;) {
@@ -624,10 +619,10 @@ int Http2Handler::fill_wb() {
if (datalen == 0) {
break;
}
auto n = wb_.write(data, as_unsigned(datalen));
if (n < static_cast<decltype(n)>(datalen)) {
data_pending_ = data + n;
data_pendinglen_ = as_unsigned(datalen) - n;
auto chunk = std::span{data, as_unsigned(datalen)};
auto n = wb_.write(chunk);
if (n < chunk.size()) {
data_pending_ = chunk.subspan(n);
break;
}
}
@@ -924,9 +919,8 @@ int Http2Handler::verify_alpn_result() {
return -1;
}
int Http2Handler::submit_file_response(const std::string_view &status,
Stream *stream, time_t last_modified,
off_t file_length,
int Http2Handler::submit_file_response(std::string_view status, Stream *stream,
time_t last_modified, off_t file_length,
const std::string *content_type,
nghttp2_data_provider2 *data_prd) {
std::string last_modified_str;
@@ -962,8 +956,8 @@ int Http2Handler::submit_file_response(const std::string_view &status,
nvlen, data_prd);
}
int Http2Handler::submit_response(const std::string_view &status,
int32_t stream_id, const HeaderRefs &headers,
int Http2Handler::submit_response(std::string_view status, int32_t stream_id,
const HeaderRefs &headers,
nghttp2_data_provider2 *data_prd) {
auto nva = std::vector<nghttp2_nv>();
nva.reserve(4 + headers.size());
@@ -987,8 +981,7 @@ int Http2Handler::submit_response(const std::string_view &status,
return r;
}
int Http2Handler::submit_response(const std::string_view &status,
int32_t stream_id,
int Http2Handler::submit_response(std::string_view status, int32_t stream_id,
nghttp2_data_provider2 *data_prd) {
auto nva = std::to_array({
http2::make_field(":status"sv, status),
@@ -1017,7 +1010,7 @@ int Http2Handler::submit_non_final_response(const std::string &status,
}
int Http2Handler::submit_push_promise(Stream *stream,
const std::string_view &push_path) {
std::string_view push_path) {
auto authority = stream->header.authority;
if (authority.empty()) {
@@ -1213,7 +1206,7 @@ bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
namespace {
void prepare_redirect_response(Stream *stream, Http2Handler *hd,
const std::string_view &path, int status) {
std::string_view path, int status) {
auto scheme = stream->header.scheme;
auto authority = stream->header.authority;
@@ -1906,7 +1899,9 @@ public:
ev_io_start(sessions_->get_loop(), &w_);
}
void accept_connection() {
for (;;) {
constexpr size_t max_num_accept = 10;
for (size_t i = 0; i < max_num_accept; ++i) {
#ifdef HAVE_ACCEPT4
auto fd = accept4(fd_, nullptr, nullptr, SOCK_NONBLOCK);
#else // !defined(HAVE_ACCEPT4)

View File

@@ -37,6 +37,7 @@
#include <unordered_map>
#include <memory>
#include <string_view>
#include <span>
#include "ssl_compat.h"
@@ -178,21 +179,21 @@ public:
int connection_made();
int verify_alpn_result();
int submit_file_response(const std::string_view &status, Stream *stream,
int submit_file_response(std::string_view status, Stream *stream,
time_t last_modified, off_t file_length,
const std::string *content_type,
nghttp2_data_provider2 *data_prd);
int submit_response(const std::string_view &status, int32_t stream_id,
int submit_response(std::string_view status, int32_t stream_id,
nghttp2_data_provider2 *data_prd);
int submit_response(const std::string_view &status, int32_t stream_id,
int submit_response(std::string_view status, int32_t stream_id,
const HeaderRefs &headers,
nghttp2_data_provider2 *data_prd);
int submit_non_final_response(const std::string &status, int32_t stream_id);
int submit_push_promise(Stream *stream, const std::string_view &push_path);
int submit_push_promise(Stream *stream, std::string_view push_path);
int submit_rst_stream(Stream *stream, uint32_t error_code);
@@ -230,8 +231,7 @@ private:
nghttp2_session *session_;
Sessions *sessions_;
SSL *ssl_;
const uint8_t *data_pending_;
size_t data_pendinglen_;
std::span<const uint8_t> data_pending_;
int fd_;
};

View File

@@ -92,7 +92,7 @@ if ENABLE_APP
bin_PROGRAMS += nghttp nghttpd nghttpx
HELPER_OBJECTS = util.cc \
http2.cc timegm.c app_helper.cc nghttp2_gzip.c
http2.cc timegm.c app_helper.cc nghttp2_gzip.c network.cc
HELPER_HFILES = util.h \
http2.h timegm.h app_helper.h nghttp2_config.h \
nghttp2_gzip.h network.h
@@ -120,7 +120,8 @@ h2load_SOURCES = util.cc util.h \
tls.cc tls.h ssl_compat.h \
h2load_session.h \
h2load_http2_session.cc h2load_http2_session.h \
h2load_http1_session.cc h2load_http1_session.h
h2load_http1_session.cc h2load_http1_session.h \
network.cc network.h
if ENABLE_HTTP3
h2load_SOURCES += \
@@ -132,6 +133,7 @@ NGHTTPX_SRCS = \
util.cc util.h http2.cc http2.h timegm.c timegm.h base64.h \
app_helper.cc app_helper.h \
tls.cc tls.h ssl_compat.h \
network.cc network.h \
shrpx_config.cc shrpx_config.h \
shrpx_error.h \
shrpx_accept_handler.cc shrpx_accept_handler.h \
@@ -229,6 +231,7 @@ nghttpx_unittest_SOURCES = shrpx-unittest.cc \
memchunk_test.cc memchunk_test.h \
template_test.cc template_test.h \
base64_test.cc base64_test.h \
network_test.cc network_test.h \
$(top_srcdir)/tests/munit/munit.c $(top_srcdir)/tests/munit/munit.h \
$(top_srcdir)/tests/munit/munitxx.h
if ENABLE_HTTP3
@@ -262,7 +265,9 @@ bin_PROGRAMS += inflatehd deflatehd
HPACK_TOOLS_COMMON_SRCS = \
comp_helper.c comp_helper.h \
util.cc util.h \
timegm.c timegm.h
timegm.c timegm.h \
tls.cc tls.h \
network.cc network.h
inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS)

View File

@@ -198,7 +198,7 @@ struct BlockAllocator {
// Makes a copy of a range [|first|, |last|). The resulting string
// will be NULL-terminated.
template <std::input_iterator I>
template <std::forward_iterator I>
std::string_view make_string_ref(BlockAllocator &alloc, I first, I last) {
auto dst = static_cast<char *>(
alloc.alloc(static_cast<size_t>(std::ranges::distance(first, last) + 1)));
@@ -210,7 +210,7 @@ std::string_view make_string_ref(BlockAllocator &alloc, I first, I last) {
// Makes a copy of |r| as std::string_view. The resulting string will be
// NULL-terminated.
template <std::ranges::input_range R>
template <std::ranges::forward_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
std::string_view make_string_ref(BlockAllocator &alloc, R &&r) {
return make_string_ref(alloc, std::ranges::begin(r), std::ranges::end(r));
@@ -223,7 +223,7 @@ constexpr size_t concat_string_ref_count(size_t acc) { return acc; }
// private function used in concat_string_ref. This function counts
// the sum of length of given arguments. The calculated length is
// accumulated, and passed to the next function.
template <std::ranges::input_range R, std::ranges::input_range... Args>
template <std::ranges::sized_range R, std::ranges::sized_range... Args>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
constexpr size_t concat_string_ref_count(size_t acc, R &&r, Args &&...args) {
return concat_string_ref_count(acc + std::ranges::size(r), args...);
@@ -246,7 +246,7 @@ uint8_t *concat_string_ref_copy(uint8_t *p, R &&r, Args &&...args) {
// Returns the string which is the concatenation of |args| in the
// given order. The resulting string will be NULL-terminated.
template <std::ranges::input_range... Args>
template <std::ranges::sized_range... Args>
std::string_view concat_string_ref(BlockAllocator &alloc, Args &&...args) {
auto len = concat_string_ref_count(0, args...);
auto dst = static_cast<uint8_t *>(alloc.alloc(len + 1));
@@ -262,9 +262,9 @@ std::string_view concat_string_ref(BlockAllocator &alloc, Args &&...args) {
// obtained from alloc.alloc() or alloc.realloc(), and attempts to use
// unused memory region by using alloc.realloc(). If value is empty,
// then just call concat_string_ref().
template <std::ranges::input_range... Args>
template <std::ranges::sized_range... Args>
std::string_view realloc_concat_string_ref(BlockAllocator &alloc,
const std::string_view &value,
std::string_view value,
Args &&...args) {
if (value.empty()) {
return concat_string_ref(alloc, std::forward<Args>(args)...);

View File

@@ -46,7 +46,7 @@ inline constexpr char B64_CHARS[] = {
constexpr size_t encode_length(size_t n) { return (n + 2) / 3 * 4; }
template <std::input_iterator I, std::weakly_incrementable O>
template <std::forward_iterator I, std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
constexpr O encode(I first, I last, O result) {
using result_type = std::iter_value_t<O>;
@@ -96,14 +96,14 @@ constexpr O encode(I first, I last, O result) {
return p;
}
template <std::ranges::input_range R, std::weakly_incrementable O>
template <std::ranges::forward_range R, std::weakly_incrementable O>
requires(std::indirectly_writable<O, char> &&
!std::is_array_v<std::remove_cvref_t<R>>)
constexpr O encode(R &&r, O result) {
return encode(std::ranges::begin(r), std::ranges::end(r), std::move(result));
}
template <std::ranges::input_range R>
template <std::ranges::sized_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
constexpr std::string encode(R &&r) {
std::string res;
@@ -135,7 +135,7 @@ inline constexpr int B64_INDEX_TABLE[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1};
template <std::input_iterator I, std::weakly_incrementable O>
template <std::forward_iterator I, std::weakly_incrementable O>
requires(std::indirectly_writable<O, uint8_t>)
constexpr O decode(I first, I last, O result) {
using result_type = std::iter_value_t<O>;
@@ -177,7 +177,7 @@ constexpr O decode(I first, I last, O result) {
return p;
}
template <std::ranges::input_range R>
template <std::ranges::sized_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
std::span<const uint8_t> decode(BlockAllocator &balloc, R &&r) {
auto len = std::ranges::size(r);

View File

@@ -41,13 +41,12 @@ template <size_t N> struct Buffer {
constexpr size_t wleft() const noexcept {
return as_unsigned(&buf[N] - last);
}
// Writes up to min(wleft(), |count|) bytes from buffer pointed by
// |src|. Returns number of bytes written.
constexpr size_t write(const void *src, size_t count) {
count = std::min(count, wleft());
auto p = static_cast<const uint8_t *>(src);
last = std::ranges::copy_n(p, as_signed(count), last).out;
return count;
// Writes up to min(wleft(), |src|.size()) bytes from |src|.
// Returns number of bytes written.
constexpr size_t write(std::span<const uint8_t> src) {
src = src.first(std::min(src.size(), wleft()));
last = std::ranges::copy(src, last).out;
return src.size();
}
constexpr size_t write(size_t count) {
count = std::min(count, wleft());
@@ -70,6 +69,7 @@ template <size_t N> struct Buffer {
constexpr uint8_t *begin() noexcept { return buf; }
constexpr uint8_t &operator[](size_t n) { return buf[n]; }
constexpr const uint8_t &operator[](size_t n) const { return buf[n]; }
constexpr std::span<uint8_t> wbuffer() { return {last, wleft()}; }
uint8_t buf[N];
uint8_t *pos, *last;
};

View File

@@ -34,6 +34,8 @@
#include "buffer.h"
using namespace std::literals;
namespace nghttp2 {
namespace {
@@ -52,7 +54,7 @@ void test_buffer_write(void) {
assert_size(0, ==, b.rleft());
assert_size(16, ==, b.wleft());
b.write("012", 3);
b.write(as_uint8_span(std::span{"012"sv}));
assert_size(3, ==, b.rleft());
assert_size(13, ==, b.wleft());
@@ -64,7 +66,7 @@ void test_buffer_write(void) {
assert_size(13, ==, b.wleft());
assert_ptrdiff(3, ==, b.pos - std::ranges::begin(b.buf));
auto n = b.write("0123456789ABCDEF", 16);
auto n = b.write(as_uint8_span(std::span{"0123456789ABCDEF"sv}));
assert_size(13, ==, n);

File diff suppressed because it is too large Load Diff

View File

@@ -151,6 +151,14 @@ struct Config {
// sni is the value sent in TLS SNI, overriding DNS name of the
// remote host.
std::string sni;
// Plot histogram.
bool histogram{};
// Path to TLS session file.
std::string tls_session_file;
// TLS session read from file.
SSL_SESSION *tls_session{};
// Path to file to write the measurement results.
std::string output_file;
Config();
~Config();
@@ -197,24 +205,56 @@ struct ClientStat {
std::chrono::steady_clock::time_point connect_time;
// time to first byte (TTFB)
std::chrono::steady_clock::time_point ttfb;
// The minimum RTT (QUIC)
std::chrono::nanoseconds min_rtt;
// The smoothed RTT (QUIC)
std::chrono::nanoseconds smoothed_rtt;
// The number of packets sent (QUIC)
uint64_t pkt_sent;
// The number of packets received (QUIC)
uint64_t pkt_recv;
// The number of packets declared lost (QUIC)
uint64_t pkt_lost;
};
struct SDStat {
// min, max, mean and sd (standard deviation)
double min, max, mean, sd;
struct GROStat {
// The number of packets received in a single recvmsg (QUIC)
size_t num_pkts;
};
template <typename T> struct SDStat {
// min, max, median, p95, and p99
T min, max, median, p95, p99;
// mean and sd (standard deviation)
double mean, sd;
// percentage of samples inside mean -/+ sd
double within_sd;
// sampled data
std::vector<T> samples;
};
struct SDStats {
// time for request
SDStat request;
SDStat<double> request;
// time for connect
SDStat connect;
SDStat<double> connect;
// time to first byte (TTFB)
SDStat ttfb;
SDStat<double> ttfb;
// request per second for each client
SDStat rps;
SDStat<double> rps;
// minimum RTT (QUIC)
SDStat<double> min_rtt;
// smoothed RTT (QUIC)
SDStat<double> smoothed_rtt;
// the number of packets sent (QUIC)
SDStat<uint64_t> pkt_sent;
// the number of packets received (QUIC)
SDStat<uint64_t> pkt_recv;
// the number of packets declared lost (QUIC)
SDStat<uint64_t> pkt_lost;
// the number of packets received in a single recvmsg call (QUIC)
SDStat<uint64_t> gro_pkts;
};
struct Stats {
@@ -256,6 +296,8 @@ struct Stats {
std::vector<RequestStat> req_stats;
// The statistics per client
std::vector<ClientStat> client_stats;
// The statistics about GRO, sampled across all clients.
std::vector<GROStat> gro_stats;
// The number of UDP datagrams received.
size_t udp_dgram_recv;
// The number of UDP datagrams sent.
@@ -289,6 +331,7 @@ struct Worker {
Stats stats;
Sampling request_times_smp;
Sampling client_smp;
Sampling gro_smp;
struct ev_loop *loop;
SSL_CTX *ssl_ctx;
Config *config;
@@ -296,6 +339,8 @@ struct Worker {
uint32_t id;
bool tls_info_report_done;
bool app_info_report_done;
bool tls_session_store_done{};
SSL_SESSION *tls_session{};
size_t nconns_made;
// number of clients this worker handles
size_t nclients;
@@ -313,7 +358,7 @@ struct Worker {
// worker
Phase current_phase;
// We need to keep track of the clients in order to stop them when needed
std::vector<Client *> clients;
std::unordered_map<uint32_t, Client *> clients;
// This is only active when there is not a bounded number of requests
// specified
ev_timer duration_watcher;
@@ -326,12 +371,14 @@ struct Worker {
void run();
void sample_req_stat(RequestStat *req_stat);
void sample_client_stat(ClientStat *cstat);
void sample_gro_stat(const GROStat &gro_stat);
void report_progress();
void report_rate_progress();
// This function calls the destructors of all the clients.
void stop_all_clients();
// This function frees a client from the list of clients for this Worker.
void free_client(Client *);
void write_tls_session(const std::string &path);
};
struct Stream {
@@ -442,9 +489,10 @@ struct Client {
void process_abandoned_streams();
void report_tls_info();
void report_app_info();
void terminate_session();
int terminate_session();
// Asks client to create new connection, instead of just fail.
void try_new_connection();
uint32_t get_id() const;
int do_read();
int do_write();
@@ -457,14 +505,14 @@ struct Client {
int read_tls();
int write_tls();
int on_read(const uint8_t *data, size_t len);
int on_read(std::span<const uint8_t> data);
int on_write();
int connection_made();
void on_request(int64_t stream_id);
void on_header(int64_t stream_id, const uint8_t *name, size_t namelen,
const uint8_t *value, size_t valuelen);
void on_header(int64_t stream_id, std::span<const uint8_t> name,
std::span<const uint8_t> value);
void on_status_code(int64_t stream_id, uint16_t status);
// |success| == true means that the request/response was exchanged
// |successfully, but it does not mean response carried successful
@@ -494,7 +542,7 @@ struct Client {
int read_quic();
int write_quic();
ngtcp2_ssize write_quic_pkt(ngtcp2_path *path, ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen, ngtcp2_tstamp ts);
std::span<uint8_t> dest, ngtcp2_tstamp ts);
std::span<const uint8_t> write_udp(const sockaddr *addr, socklen_t addrlen,
std::span<const uint8_t> data,
size_t gso_size);
@@ -507,7 +555,7 @@ struct Client {
int quic_handshake_completed();
int quic_recv_stream_data(uint32_t flags, int64_t stream_id,
const uint8_t *data, size_t datalen);
std::span<const uint8_t> data);
int quic_acked_stream_data_offset(int64_t stream_id, size_t datalen);
int quic_stream_close(int64_t stream_id, uint64_t app_error_code);
int quic_stream_reset(int64_t stream_id, uint64_t app_error_code);
@@ -515,8 +563,6 @@ struct Client {
int quic_extend_max_local_streams();
int quic_extend_max_stream_data(int64_t stream_id);
int quic_write_client_handshake(ngtcp2_encryption_level level,
const uint8_t *data, size_t datalen);
int quic_pkt_timeout();
void quic_restart_pkt_timer();
void quic_write_qlog(const void *data, size_t datalen);

View File

@@ -190,24 +190,22 @@ int Http1Session::submit_request() {
if (config->data_fd == -1 || config->data_length == 0) {
// increment for next request
stream_req_counter_ += 2;
return 0;
}
return on_write();
}
int Http1Session::on_read(const uint8_t *data, size_t len) {
auto htperr =
llhttp_execute(&htp_, reinterpret_cast<const char *>(data), len);
int Http1Session::on_read(std::span<const uint8_t> data) {
auto htperr = llhttp_execute(
&htp_, reinterpret_cast<const char *>(data.data()), data.size());
auto nread = htperr == HPE_OK
? len
? data.size()
: static_cast<size_t>(reinterpret_cast<const uint8_t *>(
llhttp_get_error_pos(&htp_)) -
data);
data.data());
if (client_->worker->config->verbose) {
std::cout.write(reinterpret_cast<const char *>(data),
std::cout.write(reinterpret_cast<const char *>(data.data()),
static_cast<std::streamsize>(nread));
}

View File

@@ -38,13 +38,13 @@ struct Client;
class Http1Session : public Session {
public:
Http1Session(Client *client);
virtual ~Http1Session();
virtual void on_connect();
virtual int submit_request();
virtual int on_read(const uint8_t *data, size_t len);
virtual int on_write();
virtual void terminate();
virtual size_t max_concurrent_streams();
~Http1Session() override;
void on_connect() override;
int submit_request() override;
int on_read(std::span<const uint8_t> data) override;
int on_write() override;
void terminate() override;
size_t max_concurrent_streams() override;
Client *get_client();
int32_t stream_req_counter_;
int32_t stream_resp_counter_;

View File

@@ -50,7 +50,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
if (frame->hd.type != NGHTTP2_HEADERS) {
return 0;
}
client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
client->on_header(frame->hd.stream_id, {name, namelen}, {value, valuelen});
client->worker->stats.bytes_head_decomp += namelen + valuelen;
if (client->worker->config->verbose) {
@@ -186,7 +186,8 @@ void Http2Session::on_connect() {
nghttp2_session_callbacks_new(&callbacks);
auto callbacks_deleter = defer(nghttp2_session_callbacks_del, callbacks);
auto callbacks_deleter =
defer([callbacks] { nghttp2_session_callbacks_del(callbacks); });
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
on_frame_recv_callback);
@@ -278,13 +279,13 @@ int Http2Session::submit_request() {
return 0;
}
int Http2Session::on_read(const uint8_t *data, size_t len) {
auto rv = nghttp2_session_mem_recv2(session_, data, len);
int Http2Session::on_read(std::span<const uint8_t> data) {
auto rv = nghttp2_session_mem_recv2(session_, data.data(), data.size());
if (rv < 0) {
return -1;
}
assert(static_cast<size_t>(rv) == len);
assert(static_cast<size_t>(rv) == data.size());
if (nghttp2_session_want_read(session_) == 0 &&
nghttp2_session_want_write(session_) == 0 && client_->wb.rleft() == 0) {

View File

@@ -36,13 +36,13 @@ struct Client;
class Http2Session : public Session {
public:
Http2Session(Client *client);
virtual ~Http2Session();
virtual void on_connect();
virtual int submit_request();
virtual int on_read(const uint8_t *data, size_t len);
virtual int on_write();
virtual void terminate();
virtual size_t max_concurrent_streams();
~Http2Session() override;
void on_connect() override;
int submit_request() override;
int on_read(std::span<const uint8_t> data) override;
int on_write() override;
void terminate() override;
size_t max_concurrent_streams() override;
private:
Client *client_;

View File

@@ -127,7 +127,7 @@ int64_t Http3Session::submit_request_internal() {
return stream_id;
}
int Http3Session::on_read(const uint8_t *data, size_t len) { return -1; }
int Http3Session::on_read(std::span<const uint8_t> data) { return -1; }
int Http3Session::on_write() { return -1; }
@@ -178,16 +178,15 @@ namespace {
int recv_data(nghttp3_conn *conn, int64_t stream_id, const uint8_t *data,
size_t datalen, void *user_data, void *stream_user_data) {
auto s = static_cast<Http3Session *>(user_data);
s->recv_data(stream_id, data, datalen);
s->recv_data(stream_id, {data, datalen});
return 0;
}
} // namespace
void Http3Session::recv_data(int64_t stream_id, const uint8_t *data,
size_t datalen) {
void Http3Session::recv_data(int64_t stream_id, std::span<const uint8_t> data) {
client_->record_ttfb();
client_->worker->stats.bytes_body += datalen;
consume(stream_id, datalen);
client_->worker->stats.bytes_body += data.size();
consume(stream_id, data.size());
}
namespace {
@@ -228,15 +227,15 @@ int recv_header(nghttp3_conn *conn, int64_t stream_id, int32_t token,
auto s = static_cast<Http3Session *>(user_data);
auto k = nghttp3_rcbuf_get_buf(name);
auto v = nghttp3_rcbuf_get_buf(value);
s->recv_header(stream_id, &k, &v);
s->recv_header(stream_id, {k.base, k.len}, {v.base, v.len});
return 0;
}
} // namespace
void Http3Session::recv_header(int64_t stream_id, const nghttp3_vec *name,
const nghttp3_vec *value) {
client_->on_header(stream_id, name->base, name->len, value->base, value->len);
client_->worker->stats.bytes_head_decomp += name->len + value->len;
void Http3Session::recv_header(int64_t stream_id, std::span<const uint8_t> name,
std::span<const uint8_t> value) {
client_->on_header(stream_id, name, value);
client_->worker->stats.bytes_head_decomp += name.size() + value.size();
}
namespace {
@@ -423,10 +422,11 @@ int Http3Session::init_conn() {
}
ssize_t Http3Session::read_stream(uint32_t flags, int64_t stream_id,
const uint8_t *data, size_t datalen) {
auto nconsumed = nghttp3_conn_read_stream2(
conn_, stream_id, data, datalen, flags & NGTCP2_STREAM_DATA_FLAG_FIN,
ngtcp2_conn_get_timestamp(client_->quic.conn));
std::span<const uint8_t> data) {
auto nconsumed =
nghttp3_conn_read_stream2(conn_, stream_id, data.data(), data.size(),
flags & NGTCP2_STREAM_DATA_FLAG_FIN,
ngtcp2_conn_get_timestamp(client_->quic.conn));
if (nconsumed < 0) {
std::cerr << "nghttp3_conn_read_stream2: "
<< nghttp3_strerror(static_cast<int>(nconsumed)) << std::endl;

View File

@@ -36,22 +36,22 @@ struct Client;
class Http3Session : public Session {
public:
Http3Session(Client *client);
virtual ~Http3Session();
virtual void on_connect();
virtual int submit_request();
virtual int on_read(const uint8_t *data, size_t len);
virtual int on_write();
virtual void terminate();
virtual size_t max_concurrent_streams();
~Http3Session() override;
void on_connect() override;
int submit_request() override;
int on_read(std::span<const uint8_t> data) override;
int on_write() override;
void terminate() override;
size_t max_concurrent_streams() override;
int init_conn();
int stream_close(int64_t stream_id, uint64_t app_error_code);
int end_stream(int64_t stream_id);
void recv_data(int64_t stream_id, const uint8_t *data, size_t datalen);
void recv_data(int64_t stream_id, std::span<const uint8_t> data);
void consume(int64_t stream_id, size_t nconsumed);
void begin_headers(int64_t stream_id);
void recv_header(int64_t stream_id, const nghttp3_vec *name,
const nghttp3_vec *value);
void recv_header(int64_t stream_id, std::span<const uint8_t> name,
std::span<const uint8_t> value);
int stop_sending(int64_t stream_id, uint64_t app_error_code);
int reset_stream(int64_t stream_id, uint64_t app_error_code);
@@ -60,8 +60,8 @@ public:
int extend_max_local_streams();
int64_t submit_request_internal();
ssize_t read_stream(uint32_t flags, int64_t stream_id, const uint8_t *data,
size_t datalen);
ssize_t read_stream(uint32_t flags, int64_t stream_id,
std::span<const uint8_t> data);
ssize_t write_stream(int64_t &stream_id, int &fin, nghttp3_vec *vec,
size_t veccnt);
void block_stream(int64_t stream_id);

View File

@@ -71,7 +71,7 @@ int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
uint64_t offset, const uint8_t *data, size_t datalen,
void *user_data, void *stream_user_data) {
auto c = static_cast<Client *>(user_data);
if (c->quic_recv_stream_data(flags, stream_id, data, datalen) != 0) {
if (c->quic_recv_stream_data(flags, stream_id, {data, datalen}) != 0) {
// TODO Better to do this gracefully rather than
// NGTCP2_ERR_CALLBACK_FAILURE. Perhaps, call
// ngtcp2_conn_write_application_close() ?
@@ -82,13 +82,13 @@ int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
} // namespace
int Client::quic_recv_stream_data(uint32_t flags, int64_t stream_id,
const uint8_t *data, size_t datalen) {
std::span<const uint8_t> data) {
if (worker->current_phase == Phase::MAIN_DURATION) {
worker->stats.bytes_total += datalen;
worker->stats.bytes_total += data.size();
}
auto s = static_cast<Http3Session *>(session.get());
auto nconsumed = s->read_stream(flags, stream_id, data, datalen);
auto nconsumed = s->read_stream(flags, stream_id, data);
if (nconsumed == -1) {
return -1;
}
@@ -344,6 +344,8 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
const sockaddr *remote_addr, socklen_t remote_addrlen) {
int rv;
auto config = worker->config;
if (!ssl) {
ssl = SSL_new(worker->ssl_ctx);
@@ -368,6 +370,14 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
#else // !OPENSSL_3_5_0_API
SSL_set_quic_use_legacy_codepoint(ssl, 0);
#endif // !OPENSSL_3_5_0_API
if (config->tls_session && !SSL_set_session(ssl, config->tls_session)) {
std::cerr << "Could not set TLS session" << std::endl;
}
if (!config->tls_session_file.empty()) {
SSL_set_ex_data(ssl, 1, worker);
}
}
auto callbacks = ngtcp2_callbacks{
@@ -402,8 +412,6 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
return -1;
}
auto config = worker->config;
ngtcp2_settings settings;
ngtcp2_settings_default(&settings);
if (config->verbose) {
@@ -479,6 +487,18 @@ int Client::quic_init(const sockaddr *local_addr, socklen_t local_addrlen,
}
void Client::quic_free() {
if (quic.conn) {
ngtcp2_conn_info ci;
ngtcp2_conn_get_conn_info(quic.conn, &ci);
cstat.min_rtt = std::chrono::nanoseconds(ci.min_rtt);
cstat.smoothed_rtt = std::chrono::nanoseconds(ci.smoothed_rtt);
cstat.pkt_sent = ci.pkt_sent;
cstat.pkt_recv = ci.pkt_recv;
cstat.pkt_lost = ci.pkt_lost;
}
#if OPENSSL_3_5_0_API
ngtcp2_crypto_ossl_ctx_del(quic.ossl_ctx);
#endif // OPENSSL_3_5_0_API
@@ -512,22 +532,6 @@ void Client::quic_close_connection() {
as_unsigned(nwrite));
}
int Client::quic_write_client_handshake(ngtcp2_encryption_level level,
const uint8_t *data, size_t datalen) {
int rv;
assert(level < 2);
rv = ngtcp2_conn_submit_crypto_data(quic.conn, level, data, datalen);
if (rv != 0) {
std::cerr << "ngtcp2_conn_submit_crypto_data: " << ngtcp2_strerror(rv)
<< std::endl;
return -1;
}
return 0;
}
void quic_pkt_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
auto c = static_cast<Client *>(w->data);
@@ -565,7 +569,7 @@ void Client::quic_restart_pkt_timer() {
int Client::read_quic() {
std::array<uint8_t, 64_k> buf;
sockaddr_union su;
sockaddr_storage ss;
int rv;
size_t pktcnt = 0;
ngtcp2_pkt_info pi;
@@ -578,7 +582,7 @@ int Client::read_quic() {
uint8_t msg_ctrl[CMSG_SPACE(sizeof(int))];
msghdr msg{
.msg_name = &su,
.msg_name = &ss,
.msg_iov = &msg_iov,
.msg_iovlen = 1,
.msg_control = msg_ctrl,
@@ -587,7 +591,7 @@ int Client::read_quic() {
auto ts = quic_timestamp();
for (;;) {
msg.msg_namelen = sizeof(su);
msg.msg_namelen = sizeof(ss);
msg.msg_controllen = sizeof(msg_ctrl);
auto nread = recvmsg(fd, &msg, 0);
@@ -602,21 +606,24 @@ int Client::read_quic() {
assert(quic.conn);
size_t num_pkts;
if (gso_size) {
worker->stats.udp_dgram_recv +=
(as_unsigned(nread) + gso_size - 1) / gso_size;
num_pkts = (as_unsigned(nread) + gso_size - 1) / gso_size;
} else {
++worker->stats.udp_dgram_recv;
num_pkts = 1;
}
worker->stats.udp_dgram_recv += num_pkts;
worker->sample_gro_stat(GROStat{
.num_pkts = num_pkts,
});
auto path = ngtcp2_path{
{
&local_addr.su.sa,
local_addr.len,
},
{
&su.sa,
msg.msg_namelen,
.local{as_ngtcp2_addr(local_addr)},
.remote{
.addr = reinterpret_cast<sockaddr *>(&ss),
.addrlen = msg.msg_namelen,
},
};
@@ -664,13 +671,12 @@ ngtcp2_ssize write_pkt(ngtcp2_conn *conn, ngtcp2_path *path,
ngtcp2_tstamp ts, void *user_data) {
auto c = static_cast<Client *>(user_data);
return c->write_quic_pkt(path, pi, dest, destlen, ts);
return c->write_quic_pkt(path, pi, {dest, destlen}, ts);
}
} // namespace
ngtcp2_ssize Client::write_quic_pkt(ngtcp2_path *path, ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen,
ngtcp2_tstamp ts) {
std::span<uint8_t> dest, ngtcp2_tstamp ts) {
std::array<nghttp3_vec, 16> vec;
auto s = static_cast<Http3Session *>(session.get());
@@ -697,8 +703,8 @@ ngtcp2_ssize Client::write_quic_pkt(ngtcp2_path *path, ngtcp2_pkt_info *pi,
}
auto nwrite = ngtcp2_conn_writev_stream(
quic.conn, path, nullptr, dest, destlen, &ndatalen, flags, stream_id,
reinterpret_cast<const ngtcp2_vec *>(v), vcnt, ts);
quic.conn, path, nullptr, dest.data(), dest.size(), &ndatalen, flags,
stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt, ts);
if (nwrite < 0) {
switch (nwrite) {
case NGTCP2_ERR_STREAM_DATA_BLOCKED:
@@ -794,9 +800,7 @@ void Client::on_send_blocked(const ngtcp2_addr &remote_addr,
auto &p = quic.tx.blocked;
memcpy(&p.remote_addr.su, remote_addr.addr, remote_addr.addrlen);
p.remote_addr.len = remote_addr.addrlen;
p.remote_addr.set(remote_addr.addr);
p.data = data;
p.gso_size = gso_size;
@@ -808,8 +812,8 @@ int Client::send_blocked_packet() {
auto &p = quic.tx.blocked;
auto rest =
write_udp(&p.remote_addr.su.sa, p.remote_addr.len, p.data, p.gso_size);
auto rest = write_udp(p.remote_addr.as_sockaddr(), p.remote_addr.size(),
p.data, p.gso_size);
if (!rest.empty()) {
p.data = rest;

View File

@@ -44,7 +44,7 @@ public:
virtual int submit_request() = 0;
// Called when incoming bytes are available. The subclass has to
// return the number of bytes read.
virtual int on_read(const uint8_t *data, size_t len) = 0;
virtual int on_read(std::span<const uint8_t> data) = 0;
// Called when write is available. Returns 0 on success, otherwise
// return -1.
virtual int on_write() = 0;

View File

@@ -253,7 +253,7 @@ std::string_view stringify_status(BlockAllocator &balloc,
struct Capitalizer {
template <std::weakly_incrementable O>
requires(std::indirectly_writable<O, char>)
constexpr O operator()(const std::string_view &s, O result) noexcept {
constexpr O operator()(std::string_view s, O result) noexcept {
using result_type = std::iter_value_t<O>;
*result++ = static_cast<result_type>(util::upcase(s[0]));
@@ -271,7 +271,7 @@ struct Capitalizer {
};
namespace {
void capitalize_long(DefaultMemchunks *buf, const std::string_view &s) {
void capitalize_long(DefaultMemchunks *buf, std::string_view s) {
buf->append(util::upcase(s[0]));
auto it = std::ranges::begin(s) + 1;
@@ -294,7 +294,7 @@ void capitalize_long(DefaultMemchunks *buf, const std::string_view &s) {
}
} // namespace
void capitalize(DefaultMemchunks *buf, const std::string_view &s) {
void capitalize(DefaultMemchunks *buf, std::string_view s) {
assert(!s.empty());
constexpr size_t max_namelen = 32;
@@ -307,21 +307,20 @@ void capitalize(DefaultMemchunks *buf, const std::string_view &s) {
buf->append(s.size(), std::bind_front(Capitalizer{}, s));
}
Headers::value_type to_header(const std::string_view &name,
const std::string_view &value, bool no_index,
int32_t token) {
Headers::value_type to_header(std::string_view name, std::string_view value,
bool no_index, int32_t token) {
return Header(std::string{std::ranges::begin(name), std::ranges::end(name)},
std::string{std::ranges::begin(value), std::ranges::end(value)},
no_index, token);
}
void add_header(Headers &nva, const std::string_view &name,
const std::string_view &value, bool no_index, int32_t token) {
void add_header(Headers &nva, std::string_view name, std::string_view value,
bool no_index, int32_t token) {
nva.push_back(to_header(name, value, no_index, token));
}
const Headers::value_type *get_header(const Headers &nva,
const std::string_view &name) {
std::string_view name) {
const Headers::value_type *res = nullptr;
for (auto &nv : nva) {
if (nv.name == name) {
@@ -585,11 +584,11 @@ void erase_header(HeaderRef *hd) {
}
std::string_view rewrite_location_uri(BlockAllocator &balloc,
const std::string_view &uri,
std::string_view uri,
const urlparse_url &u,
const std::string_view &match_host,
const std::string_view &request_authority,
const std::string_view &upstream_scheme) {
std::string_view match_host,
std::string_view request_authority,
std::string_view upstream_scheme) {
// We just rewrite scheme and authority.
if ((u.field_set & (1 << URLPARSE_HOST)) == 0) {
return ""sv;
@@ -649,7 +648,7 @@ std::string_view rewrite_location_uri(BlockAllocator &balloc,
return as_string_view(std::ranges::begin(iov), p);
}
int parse_http_status_code(const std::string_view &src) {
int parse_http_status_code(std::string_view src) {
if (src.size() != 3) {
return -1;
}
@@ -672,7 +671,7 @@ int parse_http_status_code(const std::string_view &src) {
// This function was generated by genheaderfunc.py. Inspired by h2o
// header lookup. https://github.com/h2o/h2o
int lookup_token(const std::string_view &name) {
int lookup_token(std::string_view name) {
switch (name.size()) {
case 2:
switch (name[1]) {
@@ -1025,8 +1024,7 @@ namespace {
// Returns true if link-param does not match pattern |pat| of length
// |patlen| or it has empty value (""). |pat| should be parmname
// followed by "=".
bool check_link_param_empty(const std::string_view &s,
const std::string_view &pat) {
bool check_link_param_empty(std::string_view s, std::string_view pat) {
return s.size() < pat.size() ||
!std::ranges::equal(s.substr(0, pat.size()), pat, util::CaseCmp()) ||
(s.size() >= pat.size() + 2 &&
@@ -1039,8 +1037,7 @@ bool check_link_param_empty(const std::string_view &s,
namespace {
// Returns true if link-param consists of only parmname, and it
// matches string [pat, pat + patlen).
bool check_link_param_without_value(const std::string_view &s,
const std::string_view &pat) {
bool check_link_param_without_value(std::string_view s, std::string_view pat) {
if (s.size() < pat.size()) {
return false;
}
@@ -1284,7 +1281,7 @@ almost_done:
}
} // namespace
std::vector<LinkHeader> parse_link_header(const std::string_view &src) {
std::vector<LinkHeader> parse_link_header(std::string_view src) {
std::vector<LinkHeader> res;
for (auto first = std::ranges::begin(src); first != std::ranges::end(src);) {
auto rv = parse_next_link_header_once(first, std::ranges::end(src));
@@ -1297,10 +1294,8 @@ std::vector<LinkHeader> parse_link_header(const std::string_view &src) {
return res;
}
std::string path_join(const std::string_view &base_path,
const std::string_view &base_query,
const std::string_view &rel_path,
const std::string_view &rel_query) {
std::string path_join(std::string_view base_path, std::string_view base_query,
std::string_view rel_path, std::string_view rel_query) {
BlockAllocator balloc(1024, 1024);
return std::string{
@@ -1321,7 +1316,7 @@ bool expect_response_body(int method_token, uint32_t status_code) {
}
// This function was generated by genmethodfunc.py.
int lookup_method_token(const std::string_view &name) {
int lookup_method_token(std::string_view name) {
switch (name.size()) {
case 3:
switch (name[2]) {
@@ -1523,7 +1518,7 @@ std::string_view to_method_string(int method_token) {
llhttp_method_name(static_cast<llhttp_method>(method_token))};
}
std::string_view get_pure_path_component(const std::string_view &uri) {
std::string_view get_pure_path_component(std::string_view uri) {
int rv;
urlparse_url u;
@@ -1542,9 +1537,8 @@ std::string_view get_pure_path_component(const std::string_view &uri) {
int construct_push_component(BlockAllocator &balloc, std::string_view &scheme,
std::string_view &authority,
std::string_view &path,
const std::string_view &base,
const std::string_view &uri) {
std::string_view &path, std::string_view base,
std::string_view uri) {
int rv;
std::string_view rel, relq;
@@ -1648,11 +1642,10 @@ template <typename InputIt> InputIt eat_dir(InputIt first, InputIt last) {
}
} // namespace
std::string_view path_join(BlockAllocator &balloc,
const std::string_view &base_path,
const std::string_view &base_query,
const std::string_view &rel_path,
const std::string_view &rel_query) {
std::string_view path_join(BlockAllocator &balloc, std::string_view base_path,
std::string_view base_query,
std::string_view rel_path,
std::string_view rel_query) {
auto res =
make_byte_ref(balloc, std::max(static_cast<size_t>(1), base_path.size()) +
rel_path.size() + 1 +
@@ -1741,9 +1734,8 @@ std::string_view path_join(BlockAllocator &balloc,
return as_string_view(std::ranges::begin(res), p);
}
std::string_view normalize_path(BlockAllocator &balloc,
const std::string_view &path,
const std::string_view &query) {
std::string_view normalize_path(BlockAllocator &balloc, std::string_view path,
std::string_view query) {
// First, decode %XX for unreserved characters, then do
// http2::path_join
@@ -1790,8 +1782,8 @@ std::string_view normalize_path(BlockAllocator &balloc,
}
std::string_view normalize_path_colon(BlockAllocator &balloc,
const std::string_view &path,
const std::string_view &query) {
std::string_view path,
std::string_view query) {
// First, decode %XX for unreserved characters and ':', then do
// http2::path_join
@@ -1837,15 +1829,14 @@ std::string_view normalize_path_colon(BlockAllocator &balloc,
as_string_view(std::ranges::begin(result), p), query);
}
std::string normalize_path(const std::string_view &path,
const std::string_view &query) {
std::string normalize_path(std::string_view path, std::string_view query) {
BlockAllocator balloc(1024, 1024);
return std::string{normalize_path(balloc, path, query)};
}
std::string_view rewrite_clean_path(BlockAllocator &balloc,
const std::string_view &src) {
std::string_view src) {
if (src.empty() || src[0] != '/') {
return src;
}
@@ -1861,7 +1852,7 @@ std::string_view rewrite_clean_path(BlockAllocator &balloc,
std::string_view{query, fragment});
}
bool contains_trailers(const std::string_view &s) {
bool contains_trailers(std::string_view s) {
constexpr auto trailers = "trailers"sv;
for (auto p = std::ranges::begin(s), end = std::ranges::end(s);; ++p) {
@@ -1888,7 +1879,7 @@ bool contains_trailers(const std::string_view &s) {
}
std::string_view make_websocket_accept_token(uint8_t *dest,
const std::string_view &key) {
std::string_view key) {
static constexpr auto magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"sv;
std::array<char, base64::encode_length(16) + magic.size()> s;
auto p = std::ranges::copy(key, std::ranges::begin(s)).out;
@@ -1906,7 +1897,7 @@ bool legacy_http1(int major, int minor) {
return major <= 0 || (major == 1 && minor == 0);
}
bool check_transfer_encoding(const std::string_view &s) {
bool check_transfer_encoding(std::string_view s) {
if (s.empty()) {
return false;
}

View File

@@ -70,7 +70,7 @@ struct Header {
};
struct HeaderRef {
HeaderRef(const std::string_view &name, const std::string_view &value,
HeaderRef(std::string_view name, std::string_view value,
bool no_index = false, int32_t token = -1)
: name(name), value(value), token(token), no_index(no_index) {}
@@ -103,30 +103,29 @@ std::string_view get_reason_phrase(unsigned int status_code);
std::string_view stringify_status(BlockAllocator &balloc,
unsigned int status_code);
void capitalize(DefaultMemchunks *buf, const std::string_view &s);
void capitalize(DefaultMemchunks *buf, std::string_view s);
Headers::value_type to_header(const std::string_view &name,
const std::string_view &value, bool no_index,
int32_t token);
Headers::value_type to_header(std::string_view name, std::string_view value,
bool no_index, int32_t token);
// Add name/value pairs to |nva|. If |no_index| is true, this
// name/value pair won't be indexed when it is forwarded to the next
// hop.
void add_header(Headers &nva, const std::string_view &name,
const std::string_view &value, bool no_index, int32_t token);
void add_header(Headers &nva, std::string_view name, std::string_view value,
bool no_index, int32_t token);
// Returns pointer to the entry in |nva| which has name |name|. If
// more than one entries which have the name |name|, last occurrence
// in |nva| is returned. If no such entry exist, returns nullptr.
const Headers::value_type *get_header(const Headers &nva,
const std::string_view &name);
std::string_view name);
// Returns true if the value of |nv| is not empty.
bool non_empty_value(const HeaderRefs::value_type *nv);
// Create nghttp2_nv from |name|, |value| and |flags|.
inline nghttp2_nv make_field_flags(const std::string_view &name,
const std::string_view &value,
inline nghttp2_nv make_field_flags(std::string_view name,
std::string_view value,
uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
auto ns = as_uint8_span(std::span{name});
auto vs = as_uint8_span(std::span{value});
@@ -137,8 +136,7 @@ inline nghttp2_nv make_field_flags(const std::string_view &name,
// Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2
// library does not copy them.
inline nghttp2_nv make_field(const std::string_view &name,
const std::string_view &value,
inline nghttp2_nv make_field(std::string_view name, std::string_view value,
uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
return make_field_flags(name, value,
static_cast<uint8_t>(NGHTTP2_NV_FLAG_NO_COPY_NAME |
@@ -149,8 +147,7 @@ inline nghttp2_nv make_field(const std::string_view &name,
// Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2
// library copies |value| unless |flags| includes
// NGHTTP2_NV_FLAG_NO_COPY_VALUE.
inline nghttp2_nv make_field_v(const std::string_view &name,
const std::string_view &value,
inline nghttp2_nv make_field_v(std::string_view name, std::string_view value,
uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
return make_field_flags(
name, value, static_cast<uint8_t>(NGHTTP2_NV_FLAG_NO_COPY_NAME | flags));
@@ -159,8 +156,7 @@ inline nghttp2_nv make_field_v(const std::string_view &name,
// Creates nghttp2_nv from |name|, |value| and |flags|. nghttp2
// library copies |name| and |value| unless |flags| includes
// NGHTTP2_NV_FLAG_NO_COPY_NAME or NGHTTP2_NV_FLAG_NO_COPY_VALUE.
inline nghttp2_nv make_field_nv(const std::string_view &name,
const std::string_view &value,
inline nghttp2_nv make_field_nv(std::string_view name, std::string_view value,
uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
return make_field_flags(name, value, flags);
}
@@ -260,14 +256,14 @@ void erase_header(HeaderRef *hd);
// location URI is not subject to the rewrite, this function returns
// empty string.
std::string_view rewrite_location_uri(BlockAllocator &balloc,
const std::string_view &uri,
std::string_view uri,
const urlparse_url &u,
const std::string_view &match_host,
const std::string_view &request_authority,
const std::string_view &upstream_scheme);
std::string_view match_host,
std::string_view request_authority,
std::string_view upstream_scheme);
// Returns parsed HTTP status code. Returns -1 on failure.
int parse_http_status_code(const std::string_view &src);
int parse_http_status_code(std::string_view src);
// Header fields to be indexed, except HD_MAXIDX which is convenient
// member to get maximum value.
@@ -320,7 +316,7 @@ using HeaderIndex = std::array<int16_t, HD_MAXIDX>;
// Looks up header token for header name |name|. Only headers we are
// interested in are tokenized. If header name cannot be tokenized,
// returns -1.
int lookup_token(const std::string_view &name);
int lookup_token(std::string_view name);
// Initializes |hdidx|, header index. The |hdidx| must point to the
// array containing at least HD_MAXIDX elements.
@@ -337,23 +333,20 @@ struct LinkHeader {
// URI-reference found after searching all input, returned uri field
// is empty. This imply that empty URI-reference is ignored during
// parsing.
std::vector<LinkHeader> parse_link_header(const std::string_view &src);
std::vector<LinkHeader> parse_link_header(std::string_view src);
// Constructs path by combining base path |base_path| with another
// path |rel_path|. The base path and another path can have optional
// query component. This function assumes |base_path| is normalized.
// In other words, it does not contain ".." or "." path components
// and starts with "/" if it is not empty.
std::string path_join(const std::string_view &base,
const std::string_view &base_query,
const std::string_view &rel_path,
const std::string_view &rel_query);
std::string path_join(std::string_view base, std::string_view base_query,
std::string_view rel_path, std::string_view rel_query);
std::string_view path_join(BlockAllocator &balloc,
const std::string_view &base_path,
const std::string_view &base_query,
const std::string_view &rel_path,
const std::string_view &rel_query);
std::string_view path_join(BlockAllocator &balloc, std::string_view base_path,
std::string_view base_query,
std::string_view rel_path,
std::string_view rel_query);
// true if response has body, taking into account the request method
// and status code.
@@ -366,7 +359,7 @@ bool expect_response_body(uint32_t status_code);
// Looks up method token for method name |name|. Only methods defined
// in llhttp.h (llhttp_method) are tokenized. If method name cannot
// be tokenized, returns -1.
int lookup_method_token(const std::string_view &name);
int lookup_method_token(std::string_view name);
// Returns string representation of |method_token|. This is wrapper
// around llhttp_method_name from llhttp. If |method_token| is
@@ -374,27 +367,25 @@ int lookup_method_token(const std::string_view &name);
// be NULL-terminated.
std::string_view to_method_string(int method_token);
std::string_view normalize_path(BlockAllocator &balloc,
const std::string_view &path,
const std::string_view &query);
std::string_view normalize_path(BlockAllocator &balloc, std::string_view path,
std::string_view query);
// normalize_path_colon is like normalize_path, but it additionally
// does percent-decoding %3A in order to workaround the issue that ':'
// cannot be included in backend pattern.
std::string_view normalize_path_colon(BlockAllocator &balloc,
const std::string_view &path,
const std::string_view &query);
std::string_view path,
std::string_view query);
std::string normalize_path(const std::string_view &path,
const std::string_view &query);
std::string normalize_path(std::string_view path, std::string_view query);
std::string_view rewrite_clean_path(BlockAllocator &balloc,
const std::string_view &src);
std::string_view src);
// Returns path component of |uri|. The returned path does not
// include query component. This function returns empty string if it
// fails.
std::string_view get_pure_path_component(const std::string_view &uri);
std::string_view get_pure_path_component(std::string_view uri);
// Deduces scheme, authority and path from given |uri|, and stores
// them in |scheme|, |authority|, and |path| respectively. If |uri|
@@ -403,19 +394,18 @@ std::string_view get_pure_path_component(const std::string_view &uri);
// succeeds, or -1.
int construct_push_component(BlockAllocator &balloc, std::string_view &scheme,
std::string_view &authority,
std::string_view &path,
const std::string_view &base,
const std::string_view &uri);
std::string_view &path, std::string_view base,
std::string_view uri);
// Returns true if te header field value |s| contains "trailers".
bool contains_trailers(const std::string_view &s);
bool contains_trailers(std::string_view s);
// Creates Sec-WebSocket-Accept value for |key|. The capacity of
// buffer pointed by |dest| must have at least 24 bytes (base64
// encoded length of 16 bytes data). It returns empty string in case
// of error.
std::string_view make_websocket_accept_token(uint8_t *dest,
const std::string_view &key);
std::string_view key);
// Returns true if HTTP version represents pre-HTTP/1.1 (e.g.,
// HTTP/0.9 or HTTP/1.0).
@@ -424,7 +414,7 @@ bool legacy_http1(int major, int minor);
// Returns true if transfer-encoding field value |s| conforms RFC
// strictly. This function does not allow empty value, BWS, and empty
// list elements.
bool check_transfer_encoding(const std::string_view &s);
bool check_transfer_encoding(std::string_view s);
// Encodes |extpri| in the wire format.
std::string encode_extpri(const nghttp2_extpri &extpri);

View File

@@ -41,8 +41,8 @@ namespace nghttp2 {
namespace http3 {
// Create nghttp3_nv from |name|, |value| and |flags|.
inline nghttp3_nv make_field_flags(const std::string_view &name,
const std::string_view &value,
inline nghttp3_nv make_field_flags(std::string_view name,
std::string_view value,
uint8_t flags = NGHTTP3_NV_FLAG_NONE) {
auto ns = as_uint8_span(std::span{name});
auto vs = as_uint8_span(std::span{value});
@@ -53,8 +53,7 @@ inline nghttp3_nv make_field_flags(const std::string_view &name,
// Creates nghttp3_nv from |name|, |value| and |flags|. nghttp3
// library does not copy them.
inline nghttp3_nv make_field(const std::string_view &name,
const std::string_view &value,
inline nghttp3_nv make_field(std::string_view name, std::string_view value,
uint8_t flags = NGHTTP3_NV_FLAG_NONE) {
return make_field_flags(name, value,
static_cast<uint8_t>(NGHTTP3_NV_FLAG_NO_COPY_NAME |

View File

@@ -172,7 +172,7 @@ template <typename Memchunk> struct Memchunks {
*tail->last++ = as_unsigned(c);
++len;
}
template <std::input_iterator I> void append(I first, I last) {
template <std::forward_iterator I> void append(I first, I last) {
if (first == last) {
return;
}
@@ -181,14 +181,16 @@ template <typename Memchunk> struct Memchunks {
head = tail = pool->get();
}
auto inlen = static_cast<size_t>(std::ranges::distance(first, last));
for (;;) {
auto n = std::min(static_cast<size_t>(std::ranges::distance(first, last)),
tail->left());
auto n = std::min(inlen, tail->left());
auto iores = std::ranges::copy_n(first, as_signed(n), tail->last);
first = iores.in;
tail->last = iores.out;
len += n;
if (first == last) {
inlen -= n;
if (inlen == 0) {
break;
}
@@ -202,7 +204,7 @@ template <typename Memchunk> struct Memchunks {
auto s = static_cast<const uint8_t *>(src);
append(s, s + count);
}
template <std::ranges::input_range R>
template <std::ranges::forward_range R>
requires(!std::is_array_v<std::remove_cvref_t<R>>)
void append(R &&r) {
append(std::ranges::begin(r), std::ranges::end(r));
@@ -238,26 +240,25 @@ template <typename Memchunk> struct Memchunks {
}
return len;
}
size_t remove(void *dest, size_t count) {
size_t remove(std::span<uint8_t> dest) {
assert(mark == nullptr);
if (!tail || count == 0) {
if (!tail || dest.empty()) {
return 0;
}
auto first = static_cast<uint8_t *>(dest);
auto last = first + count;
auto destlen = dest.size();
auto m = head;
while (m) {
auto next = m->next;
auto n = std::min(static_cast<size_t>(last - first), m->len());
auto n = std::min(dest.size(), m->len());
assert(m->len());
auto iores = std::ranges::copy_n(m->pos, as_signed(n), first);
m->pos = iores.in;
first = iores.out;
m->pos =
std::ranges::copy_n(m->pos, as_signed(n), std::ranges::begin(dest)).in;
dest = dest.subspan(n);
len -= n;
if (m->len() > 0) {
break;
@@ -270,7 +271,7 @@ template <typename Memchunk> struct Memchunks {
tail = nullptr;
}
return as_unsigned(first - static_cast<uint8_t *>(dest));
return destlen - dest.size();
}
size_t remove(Memchunks &dest, size_t count) {
assert(mark == nullptr);
@@ -431,6 +432,13 @@ template <typename Memchunk> struct Memchunks {
}
size_t rleft() const { return len; }
size_t rleft_mark() const { return len - mark_offset; }
std::span<const uint8_t> peek() const {
if (!head) {
return {};
}
return {head->pos, head->len()};
}
void reset() {
for (auto m = head; m;) {
auto next = m->next;
@@ -560,6 +568,9 @@ template <typename Memchunk> struct MemchunkBuffer {
uint8_t *begin() { return std::ranges::begin(chunk->buf); }
uint8_t &operator[](size_t n) { return chunk->buf[n]; }
const uint8_t &operator[](size_t n) const { return chunk->buf[n]; }
// Returns the readable chunk of data.
std::span<const uint8_t> peek() const { return {chunk->pos, chunk->len()}; }
std::span<uint8_t> wbuffer() { return {chunk->last, chunk->left()}; }
Pool<Memchunk> *pool;
Memchunk *chunk;

View File

@@ -40,11 +40,14 @@ const MunitTest tests[]{
munit_void_test(test_pool_recycle),
munit_void_test(test_memchunks_append),
munit_void_test(test_memchunks_drain),
munit_void_test(test_memchunks_remove),
munit_void_test(test_memchunks_riovec),
munit_void_test(test_memchunks_peek),
munit_void_test(test_memchunks_recycle),
munit_void_test(test_memchunks_reset),
munit_void_test(test_memchunks_reserve),
munit_void_test(test_memchunkbuffer_drain_reset),
munit_void_test(test_memchunkbuffer_peek),
munit_test_end(),
};
} // namespace
@@ -128,19 +131,19 @@ void test_memchunks_append(void) {
assert_size(15, ==, m->left());
assert_size(17, ==, chunks.rleft());
char buf[16];
std::array<uint8_t, 16> buf;
size_t nread;
nread = chunks.remove(buf, 8);
nread = chunks.remove(std::span{buf}.first(8));
assert_size(8, ==, nread);
assert_memory_equal(nread, "01234567", buf);
assert_memory_equal(nread, "01234567", buf.data());
assert_size(9, ==, chunks.rleft());
nread = chunks.remove(buf, sizeof(buf));
nread = chunks.remove(buf);
assert_size(9, ==, nread);
assert_memory_equal(nread, "89abcdef@", buf);
assert_memory_equal(nread, "89abcdef@", buf.data());
assert_size(0, ==, chunks.rleft());
assert_null(chunks.head);
assert_null(chunks.tail);
@@ -159,12 +162,31 @@ void test_memchunks_drain(void) {
assert_size(3, ==, nread);
char buf[16];
std::array<uint8_t, 16> buf;
nread = chunks.remove(buf, sizeof(buf));
nread = chunks.remove(buf);
assert_size(7, ==, nread);
assert_memory_equal(nread, "3456789", buf);
assert_memory_equal(nread, "3456789", buf.data());
}
void test_memchunks_remove(void) {
MemchunkPool16 pool;
Memchunks16 chunks(&pool);
chunks.append("0123456789"sv);
std::array<uint8_t, 16> buf;
auto nread = chunks.remove(std::span{buf}.first(1));
assert_size(1, ==, nread);
assert_memory_equal(nread, "0", buf.data());
nread = chunks.remove(buf);
assert_size(9, ==, nread);
assert_memory_equal(nread, "123456789", buf.data());
}
void test_memchunks_riovec(void) {
@@ -200,6 +222,24 @@ void test_memchunks_riovec(void) {
assert_size(m->len(), ==, iov[0].iov_len);
}
void test_memchunks_peek(void) {
MemchunkPool16 pool;
Memchunks16 chunks(&pool);
assert_true(chunks.peek().empty());
std::array<char, 3 * 16> buf{};
chunks.append(buf.data(), buf.size());
auto data = chunks.peek();
auto m = chunks.head;
assert_ptr_equal(m->buf.data(), data.data());
assert_size(m->len(), ==, data.size());
}
void test_memchunks_recycle(void) {
MemchunkPool16 pool;
{
@@ -298,4 +338,16 @@ void test_memchunkbuffer_drain_reset(void) {
assert_true(buf.begin() + buf.rleft() == buf.chunk->last);
}
void test_memchunkbuffer_peek(void) {
MemchunkPool16 pool;
MemchunkBuffer16 buf(&pool);
buf.ensure_chunk();
auto data = "0123456789"sv;
std::ranges::copy(data, buf.begin());
buf.write(data.size());
assert_stdsv_equal(data, as_string_view(buf.peek()));
}
} // namespace nghttp2

View File

@@ -40,11 +40,14 @@ extern const MunitSuite memchunk_suite;
munit_void_test_decl(test_pool_recycle)
munit_void_test_decl(test_memchunks_append)
munit_void_test_decl(test_memchunks_drain)
munit_void_test_decl(test_memchunks_remove)
munit_void_test_decl(test_memchunks_riovec)
munit_void_test_decl(test_memchunks_peek)
munit_void_test_decl(test_memchunks_recycle)
munit_void_test_decl(test_memchunks_reset)
munit_void_test_decl(test_memchunks_reserve)
munit_void_test_decl(test_memchunkbuffer_drain_reset)
munit_void_test_decl(test_memchunkbuffer_peek)
} // namespace nghttp2

164
src/network.cc Normal file
View File

@@ -0,0 +1,164 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2025 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "network.h"
#include <cassert>
#include <cstdlib>
namespace nghttp2 {
const sockaddr *as_sockaddr(const Sockaddr &skaddr) {
return std::visit(
[](auto &&arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>,
std::monostate>) {
assert(0);
abort();
}
return reinterpret_cast<const sockaddr *>(&arg);
},
skaddr);
}
sockaddr *as_sockaddr(Sockaddr &skaddr) {
return std::visit(
[](auto &&arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>,
std::monostate>) {
assert(0);
abort();
}
return reinterpret_cast<sockaddr *>(&arg);
},
skaddr);
}
int sockaddr_family(const Sockaddr &skaddr) {
return as_sockaddr(skaddr)->sa_family;
}
uint16_t sockaddr_port(const Sockaddr &skaddr) {
return std::visit(
[](auto &&arg) -> uint16_t {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, sockaddr_in>) {
return ntohs(arg.sin_port);
}
if constexpr (std::is_same_v<T, sockaddr_in6>) {
return ntohs(arg.sin6_port);
}
#ifndef _WIN32
// The existing codebase expects this.
if constexpr (std::is_same_v<T, sockaddr_un>) {
return 0;
}
#endif // !defined(_WIN32)
assert(0);
abort();
},
skaddr);
}
void sockaddr_port(Sockaddr &skaddr, uint16_t port) {
std::visit(
[port](auto &&arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, sockaddr_in>) {
arg.sin_port = htons(port);
return;
}
if constexpr (std::is_same_v<T, sockaddr_in6>) {
arg.sin6_port = htons(port);
return;
}
#ifndef _WIN32
// The existing codebase expects this.
if constexpr (std::is_same_v<T, sockaddr_un>) {
return;
}
#endif // !defined(_WIN32)
assert(0);
abort();
},
skaddr);
}
void sockaddr_set(Sockaddr &skaddr, const sockaddr *sa) {
switch (sa->sa_family) {
case AF_INET:
skaddr.emplace<sockaddr_in>(*reinterpret_cast<const sockaddr_in *>(sa));
return;
case AF_INET6:
skaddr.emplace<sockaddr_in6>(*reinterpret_cast<const sockaddr_in6 *>(sa));
return;
#ifndef _WIN32
case AF_UNIX:
skaddr.emplace<sockaddr_un>(*reinterpret_cast<const sockaddr_un *>(sa));
return;
#endif // !defined(_WIN32)
default:
assert(0);
abort();
}
}
socklen_t sockaddr_size(const Sockaddr &skaddr) {
return std::visit(
[](auto &&arg) { return static_cast<socklen_t>(sizeof(arg)); }, skaddr);
}
bool sockaddr_empty(const Sockaddr &skaddr) {
return std::holds_alternative<std::monostate>(skaddr);
}
const sockaddr *Address::as_sockaddr() const {
return nghttp2::as_sockaddr(skaddr);
}
sockaddr *Address::as_sockaddr() { return nghttp2::as_sockaddr(skaddr); }
int Address::family() const { return sockaddr_family(skaddr); }
uint16_t Address::port() const { return sockaddr_port(skaddr); }
void Address::port(uint16_t port) { sockaddr_port(skaddr, port); }
void Address::set(const sockaddr *sa) { sockaddr_set(skaddr, sa); }
socklen_t Address::size() const { return sockaddr_size(skaddr); }
bool Address::empty() const { return sockaddr_empty(skaddr); }
} // namespace nghttp2

View File

@@ -45,23 +45,92 @@
# include <arpa/inet.h>
#endif // defined(HAVE_ARPA_INET_H)
#include <variant>
#ifdef ENABLE_HTTP3
# include <ngtcp2/ngtcp2.h>
#endif // defined(ENABLE_HTTP3)
namespace nghttp2 {
union sockaddr_union {
sockaddr_storage storage;
sockaddr sa;
sockaddr_in6 in6;
sockaddr_in in;
using Sockaddr = std::variant<std::monostate, sockaddr_in, sockaddr_in6
#ifndef _WIN32
sockaddr_un un;
,
sockaddr_un
#endif // !defined(_WIN32)
};
>;
// as_sockaddr returns the pointer to the stored address casted to
// const sockaddr *.
[[nodiscard]] const sockaddr *as_sockaddr(const Sockaddr &skaddr);
[[nodiscard]] sockaddr *as_sockaddr(Sockaddr &skaddr);
// sockaddr_family returns the address family.
[[nodiscard]] int sockaddr_family(const Sockaddr &skaddr);
// sockaddr_port returns the port.
[[nodiscard]] uint16_t sockaddr_port(const Sockaddr &skaddr);
// sockaddr_port sets |port| to |skaddr|.
void sockaddr_port(Sockaddr &skaddr, uint16_t port);
// sockaddr_set stores |sa| to |skaddr|. The address family is
// determined by |sa|->sa_family, and |sa| must point to the memory
// that contains valid object which is either sockaddr_in,
// sockaddr_in6, or sockaddr_un.
void sockaddr_set(Sockaddr &skaddr, const sockaddr *sa);
// sockaddr_size returns the size of the stored address. If no
// meaningful address is set, the return value is implementation
// dependent.
[[nodiscard]] socklen_t sockaddr_size(const Sockaddr &skaddr);
// sockaddr_empty returns true if |skaddr| does not contain any
// meaningful address.
[[nodiscard]] bool sockaddr_empty(const Sockaddr &skaddr);
struct Address {
socklen_t len;
union sockaddr_union su;
// as_sockaddr returns the pointer to the stored address casted to
// const sockaddr *.
[[nodiscard]] const sockaddr *as_sockaddr() const;
[[nodiscard]] sockaddr *as_sockaddr();
// family returns the address family.
[[nodiscard]] int family() const;
// port returns the port.
[[nodiscard]] uint16_t port() const;
// port sets |port| to this address.
void port(uint16_t port);
// set stores |sa| to this address. The address family is
// determined by |sa|->sa_family, and |sa| must point to the memory
// that contains valid object which is either sockaddr_in,
// sockaddr_in6, or sockaddr_un.
void set(const sockaddr *sa);
// size returns the size of the stored address. If no meaningful
// address is set, the return value is implementation dependent.
[[nodiscard]] socklen_t size() const;
// empty returns true if this address does not contain any
// meaningful address.
[[nodiscard]] bool empty() const;
Sockaddr skaddr;
};
#ifdef ENABLE_HTTP3
[[nodiscard]] inline ngtcp2_addr as_ngtcp2_addr(const Address &addr) {
return {
.addr = const_cast<sockaddr *>(addr.as_sockaddr()),
.addrlen = addr.size(),
};
}
[[nodiscard]] inline ngtcp2_addr as_ngtcp2_addr(Address &addr) {
return {
.addr = addr.as_sockaddr(),
.addrlen = addr.size(),
};
}
#endif // defined(ENABLE_HTTP3)
} // namespace nghttp2
#endif // !defined(NETWORK_H)

164
src/network_test.cc Normal file
View File

@@ -0,0 +1,164 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2025 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "network_test.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <cstring>
#include <iostream>
#include "munitxx.h"
#include <nghttp2/nghttp2.h>
#include "network.h"
using namespace std::literals;
namespace nghttp2 {
namespace {
const MunitTest tests[]{
munit_void_test(test_network_address),
munit_test_end(),
};
} // namespace
const MunitSuite network_suite{
"/network", tests, nullptr, 1, MUNIT_SUITE_OPTION_NONE,
};
namespace {
Address parse_addr(const char *ipaddr, const char *port) {
addrinfo hints{
.ai_flags = AI_NUMERICHOST
#ifdef AI_NUMERICSERV
| AI_NUMERICSERV
#endif // defined(AI_NUMERICSERV)
,
.ai_family = AF_UNSPEC,
};
addrinfo *res = nullptr;
auto rv = getaddrinfo(ipaddr, port, &hints, &res);
assert_int(0, ==, rv);
assert_not_null(res);
Address addr;
addr.set(res->ai_addr);
freeaddrinfo(res);
return addr;
}
} // namespace
void test_network_address(void) {
// Not set
{
Address addr;
assert_true(addr.empty());
}
// IPv4
{
constexpr auto ipaddr = "10.1.0.100";
auto addr = parse_addr(ipaddr, "443");
assert_ptr_equal(&std::get<sockaddr_in>(addr.skaddr), addr.as_sockaddr());
assert_size(sizeof(sockaddr_in), ==, addr.size());
assert_false(addr.empty());
assert_int(AF_INET, ==, addr.family());
assert_uint16(443, ==, addr.port());
addr.port(8443);
assert_uint16(8443, ==, addr.port());
in_addr r;
assert_int(1, ==, inet_pton(AF_INET, ipaddr, &r));
const auto &inaddr = std::get<sockaddr_in>(addr.skaddr);
assert_memory_equal(sizeof(in_addr), &r, &inaddr.sin_addr);
}
// IPv6
{
constexpr auto ipaddr = "2001:db8::1";
auto addr = parse_addr(ipaddr, "443");
assert_ptr_equal(&std::get<sockaddr_in6>(addr.skaddr), addr.as_sockaddr());
assert_size(sizeof(sockaddr_in6), ==, addr.size());
assert_false(addr.empty());
assert_int(AF_INET6, ==, addr.family());
assert_uint16(443, ==, addr.port());
addr.port(8443);
assert_uint16(8443, ==, addr.port());
in6_addr r;
assert_int(1, ==, inet_pton(AF_INET6, ipaddr, &r));
const auto &inaddr = std::get<sockaddr_in6>(addr.skaddr);
assert_memory_equal(sizeof(in6_addr), &r, &inaddr.sin6_addr);
}
#ifndef _WIN32
// UNIX
{
constexpr char path[] = "/unix.sock";
Address addr;
auto &unaddr = addr.skaddr.emplace<sockaddr_un>();
unaddr.sun_family = AF_UNIX;
memcpy(unaddr.sun_path, path, sizeof(path));
assert_ptr_equal(&std::get<sockaddr_un>(addr.skaddr), addr.as_sockaddr());
assert_size(sizeof(sockaddr_un), ==, addr.size());
assert_false(addr.empty());
assert_int(AF_UNIX, ==, addr.family());
assert_uint16(0, ==, addr.port());
addr.port(8443);
assert_uint16(0, ==, addr.port());
}
#endif // !defined(_WIN32)
}
} // namespace nghttp2

44
src/network_test.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2025 nghttp2 contributors
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef NETWORK_TEST_H
#define NETWORK_TEST_H
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif // defined(HAVE_CONFIG_H)
#define MUNIT_ENABLE_ASSERT_ALIASES
#include "munit.h"
namespace nghttp2 {
extern const MunitSuite network_suite;
munit_void_test_decl(test_network_address)
} // namespace nghttp2
#endif // !defined(NETWORK_TEST_H)

View File

@@ -210,12 +210,11 @@ void Request::init_html_parser() {
html_parser = std::make_unique<HtmlParser>(base_uri);
}
int Request::update_html_parser(const uint8_t *data, size_t len, int fin) {
int Request::update_html_parser(std::span<const uint8_t> data, int fin) {
if (!html_parser) {
return 0;
}
return html_parser->parse_chunk(reinterpret_cast<const char *>(data), len,
fin);
return html_parser->parse_chunk(data, fin);
}
std::string Request::make_reqpath() const {
@@ -234,7 +233,7 @@ std::string Request::make_reqpath() const {
namespace {
// Perform special handling |host| if it is IPv6 literal and includes
// zone ID per RFC 6874.
std::string decode_host(const std::string_view &host) {
std::string decode_host(std::string_view host) {
auto zone_start = std::ranges::find(host, '%');
if (zone_start == std::ranges::end(host) ||
!util::ipv6_numeric_addr(
@@ -727,7 +726,8 @@ void HttpClient::disconnect() {
int HttpClient::read_clear() {
ev_timer_again(loop, &rt);
std::array<uint8_t, 8_k> buf;
std::array<uint8_t, 8_k> rawbuf;
auto buf = std::span<uint8_t>{rawbuf};
for (;;) {
ssize_t nread;
@@ -744,7 +744,7 @@ int HttpClient::read_clear() {
return -1;
}
if (on_readfn(*this, buf.data(), as_unsigned(nread)) != 0) {
if (on_readfn(*this, buf.first(as_unsigned(nread))) != 0) {
return -1;
}
}
@@ -988,19 +988,19 @@ int HttpClient::on_upgrade_connect() {
return 0;
}
int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) {
int HttpClient::on_upgrade_read(std::span<const uint8_t> data) {
int rv;
auto htperr =
llhttp_execute(htp.get(), reinterpret_cast<const char *>(data), len);
auto htperr = llhttp_execute(
htp.get(), reinterpret_cast<const char *>(data.data()), data.size());
auto nread = htperr == HPE_OK
? len
? data.size()
: static_cast<size_t>(reinterpret_cast<const uint8_t *>(
llhttp_get_error_pos(htp.get())) -
data);
data.data());
if (config.verbose) {
std::cout.write(reinterpret_cast<const char *>(data),
std::cout.write(reinterpret_cast<const char *>(data.data()),
static_cast<std::streamsize>(nread));
}
@@ -1040,7 +1040,7 @@ int HttpClient::on_upgrade_read(const uint8_t *data, size_t len) {
// Read remaining data in the buffer because it is not notified
// callback anymore.
rv = on_readfn(*this, data + nread, len - nread);
rv = on_readfn(*this, data.subspan(nread));
if (rv != 0) {
return rv;
}
@@ -1145,19 +1145,19 @@ int HttpClient::connection_made() {
return 0;
}
int HttpClient::on_read(const uint8_t *data, size_t len) {
int HttpClient::on_read(std::span<const uint8_t> data) {
if (config.hexdump) {
util::hexdump(stdout, data, len);
util::hexdump(stdout, data.data(), data.size());
}
auto rv = nghttp2_session_mem_recv2(session, data, len);
auto rv = nghttp2_session_mem_recv2(session, data.data(), data.size());
if (rv < 0) {
std::cerr << "[ERROR] nghttp2_session_mem_recv2() returned error: "
<< nghttp2_strerror(static_cast<int>(rv)) << std::endl;
return -1;
}
assert(static_cast<size_t>(rv) == len);
assert(static_cast<size_t>(rv) == data.size());
if (nghttp2_session_want_read(session) == 0 &&
nghttp2_session_want_write(session) == 0 && wb.rleft() == 0) {
@@ -1247,9 +1247,11 @@ int HttpClient::read_tls() {
ERR_clear_error();
std::array<uint8_t, 8_k> buf;
std::array<uint8_t, 8_k> rawbuf;
auto buf = std::span<uint8_t>{rawbuf};
for (;;) {
auto rv = SSL_read(ssl, buf.data(), buf.size());
auto rv = SSL_read(ssl, buf.data(), static_cast<int>(buf.size()));
if (rv <= 0) {
auto err = SSL_get_error(ssl, rv);
@@ -1264,7 +1266,7 @@ int HttpClient::read_tls() {
}
}
if (on_readfn(*this, buf.data(), static_cast<size_t>(rv)) != 0) {
if (on_readfn(*this, buf.first(static_cast<size_t>(rv))) != 0) {
return -1;
}
}
@@ -1569,12 +1571,12 @@ void HttpClient::output_har(FILE *outfile) {
#endif // defined(HAVE_JANSSON)
namespace {
void update_html_parser(HttpClient *client, Request *req, const uint8_t *data,
size_t len, int fin) {
void update_html_parser(HttpClient *client, Request *req,
std::span<const uint8_t> data, int fin) {
if (!req->html_parser) {
return;
}
req->update_html_parser(data, len, fin);
req->update_html_parser(data, fin);
auto scheme = req->get_real_scheme();
auto host = req->get_real_host();
@@ -1638,39 +1640,44 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
req->response_len += len;
auto chunk = std::span{data, len};
if (req->inflater) {
while (len > 0) {
const size_t MAX_OUTLEN = 4_k;
std::array<uint8_t, MAX_OUTLEN> out;
size_t outlen = MAX_OUTLEN;
size_t tlen = len;
int rv =
nghttp2_gzip_inflate(req->inflater, out.data(), &outlen, data, &tlen);
constexpr size_t MAX_OUTLEN = 4_k;
std::array<uint8_t, MAX_OUTLEN> rawout;
while (!chunk.empty()) {
auto out = std::span<uint8_t>{rawout};
size_t outlen = out.size();
auto tlen = chunk.size();
int rv = nghttp2_gzip_inflate(req->inflater, out.data(), &outlen,
chunk.data(), &tlen);
if (rv != 0) {
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
NGHTTP2_INTERNAL_ERROR);
break;
}
out = out.first(outlen);
if (!config.null_out) {
std::cout.write(reinterpret_cast<const char *>(out.data()),
static_cast<std::streamsize>(outlen));
static_cast<std::streamsize>(out.size()));
}
update_html_parser(client, req, out.data(), outlen, 0);
data += tlen;
len -= tlen;
update_html_parser(client, req, out, 0);
chunk = chunk.subspan(tlen);
}
return 0;
}
if (!config.null_out) {
std::cout.write(reinterpret_cast<const char *>(data),
static_cast<std::streamsize>(len));
std::cout.write(reinterpret_cast<const char *>(chunk.data()),
static_cast<std::streamsize>(chunk.size()));
}
update_html_parser(client, req, data, len, 0);
update_html_parser(client, req, chunk, 0);
return 0;
}
@@ -2071,7 +2078,7 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
req->continue_timer->stop();
}
update_html_parser(client, req, nullptr, 0, 1);
update_html_parser(client, req, {}, 1);
++client->complete;
if (client->all_requests_processed()) {
@@ -2381,7 +2388,8 @@ int run(char **uris, int n) {
nghttp2_session_callbacks *callbacks;
nghttp2_session_callbacks_new(&callbacks);
auto cbsdel = defer(nghttp2_session_callbacks_del, callbacks);
auto cbsdel =
defer([callbacks] { nghttp2_session_callbacks_del(callbacks); });
nghttp2_session_callbacks_set_on_stream_close_callback(
callbacks, on_stream_close_callback);

View File

@@ -150,7 +150,7 @@ struct Request {
void init_inflater();
void init_html_parser();
int update_html_parser(const uint8_t *data, size_t len, int fin);
int update_html_parser(std::span<const uint8_t> data, int fin);
std::string make_reqpath() const;
@@ -239,8 +239,8 @@ struct HttpClient {
int do_write();
int on_upgrade_connect();
int on_upgrade_read(const uint8_t *data, size_t len);
int on_read(const uint8_t *data, size_t len);
int on_upgrade_read(std::span<const uint8_t> data);
int on_read(std::span<const uint8_t> data);
int on_write();
int connection_made();
@@ -281,7 +281,7 @@ struct HttpClient {
ev_timer rt;
ev_timer settings_timer;
std::function<int(HttpClient &)> readfn, writefn;
std::function<int(HttpClient &, const uint8_t *, size_t)> on_readfn;
std::function<int(HttpClient &, std::span<const uint8_t>)> on_readfn;
std::function<int(HttpClient &)> on_writefn;
nghttp2_session *session;
const nghttp2_session_callbacks *callbacks;

View File

@@ -45,6 +45,7 @@
#include "tls.h"
#include "shrpx_router_test.h"
#include "shrpx_log.h"
#include "network_test.h"
#ifdef ENABLE_HTTP3
# include "siphash_test.h"
#endif // defined(ENABLE_HTTP3)
@@ -66,6 +67,7 @@ int main(int argc, char *argv[]) {
memchunk_suite,
template_suite,
base64_suite,
network_suite,
#ifdef ENABLE_HTTP3
siphash_suite,
#endif // defined(ENABLE_HTTP3)

View File

@@ -739,22 +739,24 @@ int create_unix_domain_server_socket(
return -1;
}
sockaddr_union addr;
addr.un.sun_family = AF_UNIX;
if (faddr.host.size() + 1 > sizeof(addr.un.sun_path)) {
Address addr;
auto &unaddr = addr.skaddr.emplace<sockaddr_un>();
unaddr.sun_family = AF_UNIX;
if (faddr.host.size() + 1 > sizeof(unaddr.sun_path)) {
LOG(FATAL) << "UNIX domain socket path " << faddr.host << " is too long > "
<< sizeof(addr.un.sun_path);
<< sizeof(unaddr.sun_path);
close(fd);
return -1;
}
// copy path including terminal NULL
std::ranges::copy_n(faddr.host.data(), as_signed(faddr.host.size() + 1),
addr.un.sun_path);
unaddr.sun_path);
// unlink (remove) already existing UNIX domain socket path
unlink(faddr.host.data());
if (bind(fd, &addr.sa, sizeof(addr.un)) != 0) {
if (bind(fd, reinterpret_cast<const sockaddr *>(&unaddr), sizeof(unaddr)) !=
0) {
auto error = errno;
LOG(FATAL) << "Failed to bind UNIX domain socket: "
<< xsi_strerror(error, errbuf.data(), errbuf.size());
@@ -1643,7 +1645,7 @@ void fill_default_config(Config *config) {
memcachedconf.family = AF_UNSPEC;
}
ticketconf.cipher = EVP_aes_128_cbc();
ticketconf.cipher = nghttp2::tls::aes_128_cbc();
}
{

View File

@@ -43,7 +43,14 @@ namespace shrpx {
namespace {
void acceptcb(struct ev_loop *loop, ev_io *w, int revent) {
auto h = static_cast<AcceptHandler *>(w->data);
h->accept_connection();
constexpr size_t max_num_accept = 10;
for (size_t i = 0; i < max_num_accept; ++i) {
if (h->accept_connection() != 0) {
break;
}
}
}
} // namespace
@@ -59,20 +66,24 @@ AcceptHandler::~AcceptHandler() {
close(faddr_->fd);
}
void AcceptHandler::accept_connection() {
sockaddr_union sockaddr;
socklen_t addrlen = sizeof(sockaddr);
int AcceptHandler::accept_connection() {
sockaddr_storage ss;
socklen_t addrlen = sizeof(ss);
int cfd;
while ((
#ifdef HAVE_ACCEPT4
auto cfd =
accept4(faddr_->fd, &sockaddr.sa, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
cfd = accept4(faddr_->fd, reinterpret_cast<sockaddr *>(&ss),
&addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC)
#else // !defined(HAVE_ACCEPT4)
auto cfd = accept(faddr_->fd, &sockaddr.sa, &addrlen);
cfd = accept(faddr_->fd, reinterpret_cast<sockaddr *>(&ss), &addrlen)
#endif // !defined(HAVE_ACCEPT4)
) == -1 &&
errno == EINTR)
;
if (cfd == -1) {
switch (errno) {
case EINTR:
case ENETDOWN:
case EPROTO:
case ENOPROTOOPT:
@@ -83,15 +94,15 @@ void AcceptHandler::accept_connection() {
case EHOSTUNREACH:
case EOPNOTSUPP:
case ENETUNREACH:
return;
return -1;
case EMFILE:
case ENFILE:
LOG(WARN) << "acceptor: running out file descriptor; disable acceptor "
"temporarily";
worker_->sleep_listener(get_config()->conn.listener.timeout.sleep);
return;
return -1;
default:
return;
return -1;
}
}
@@ -100,7 +111,52 @@ void AcceptHandler::accept_connection() {
util::make_socket_closeonexec(cfd);
#endif // !defined(HAVE_ACCEPT4)
worker_->handle_connection(cfd, &sockaddr.sa, addrlen, faddr_);
worker_->handle_connection(cfd, reinterpret_cast<const sockaddr *>(&ss),
addrlen, faddr_);
return 0;
}
void AcceptHandler::drain_connection() {
sockaddr_storage ss;
socklen_t addrlen = sizeof(ss);
int cfd;
for (;;) {
while ((
#ifdef HAVE_ACCEPT4
cfd = accept4(faddr_->fd, reinterpret_cast<sockaddr *>(&ss),
&addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC)
#else // !defined(HAVE_ACCEPT4)
cfd =
accept(faddr_->fd, reinterpret_cast<sockaddr *>(&ss), &addrlen)
#endif // !defined(HAVE_ACCEPT4)
) == -1 &&
errno == EINTR)
;
if (cfd == -1) {
switch (errno) {
case EAGAIN:
#if EAGAIN != EWOULDBLOCK
case EWOULDBLOCK:
#endif // EAGAIN != EWOULDBLOCK
case EMFILE:
case ENFILE:
return;
default:
continue;
}
}
#ifndef HAVE_ACCEPT4
util::make_socket_nonblocking(cfd);
util::make_socket_closeonexec(cfd);
#endif // !defined(HAVE_ACCEPT4)
worker_->handle_connection(cfd, reinterpret_cast<const sockaddr *>(&ss),
addrlen, faddr_);
}
}
void AcceptHandler::enable() { ev_io_start(worker_->get_loop(), &wev_); }

View File

@@ -38,7 +38,8 @@ class AcceptHandler {
public:
AcceptHandler(Worker *worker, const UpstreamAddr *faddr);
~AcceptHandler();
void accept_connection();
int accept_connection();
void drain_connection();
void enable();
void disable();
int get_fd() const;

View File

@@ -91,7 +91,7 @@ void APIDownstreamConnection::detach_downstream(Downstream *downstream) {
int APIDownstreamConnection::send_reply(unsigned int http_status,
APIStatusCode api_status,
const std::string_view &data) {
std::string_view data) {
shutdown_read_ = true;
auto upstream = downstream_->get_upstream();
@@ -150,7 +150,7 @@ int APIDownstreamConnection::send_reply(unsigned int http_status,
break;
}
if (upstream->send_reply(downstream_, buf.data(), buf.size()) != 0) {
if (upstream->send_reply(downstream_, buf) != 0) {
return -1;
}
@@ -158,7 +158,7 @@ int APIDownstreamConnection::send_reply(unsigned int http_status,
}
namespace {
const APIEndpoint *lookup_api(const std::string_view &path) {
const APIEndpoint *lookup_api(std::string_view path) {
switch (path.size()) {
case 26:
switch (path[25]) {
@@ -292,8 +292,8 @@ int APIDownstreamConnection::error_method_not_allowed() {
return send_reply(405, APIStatusCode::FAILURE);
}
int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
size_t datalen) {
int APIDownstreamConnection::push_upload_data_chunk(
std::span<const uint8_t> data) {
if (shutdown_read_ || !api_->require_body) {
return 0;
}
@@ -308,14 +308,20 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data,
}
ssize_t nwrite;
while ((nwrite = write(fd_, data, datalen)) == -1 && errno == EINTR)
;
if (nwrite == -1) {
auto error = errno;
LOG(ERROR) << "Could not write API request body: errno=" << error;
send_reply(500, APIStatusCode::FAILURE);
return 0;
for (; !data.empty();) {
while ((nwrite = write(fd_, data.data(), data.size())) == -1 &&
errno == EINTR)
;
if (nwrite == -1) {
auto error = errno;
LOG(ERROR) << "Could not write API request body: errno=" << error;
send_reply(500, APIStatusCode::FAILURE);
return 0;
}
data = data.subspan(as_unsigned(nwrite));
}
// We don't have to call Upstream::resume_read() here, because
@@ -349,7 +355,9 @@ int APIDownstreamConnection::handle_backendconfig() {
return 0;
}
auto unmapper = defer(munmap, rp, req.recv_body_length);
auto unmapper = defer([rp, size = req.recv_body_length] {
munmap(rp, static_cast<size_t>(size));
});
Config new_config{};
new_config.conn.downstream = std::make_shared<DownstreamConfig>();

View File

@@ -67,32 +67,32 @@ struct APIEndpoint {
class APIDownstreamConnection : public DownstreamConnection {
public:
APIDownstreamConnection(Worker *worker);
virtual ~APIDownstreamConnection();
virtual int attach_downstream(Downstream *downstream);
virtual void detach_downstream(Downstream *downstream);
~APIDownstreamConnection() override;
int attach_downstream(Downstream *downstream) override;
void detach_downstream(Downstream *downstream) override;
virtual int push_request_headers();
virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);
virtual int end_upload_data();
int push_request_headers() override;
int push_upload_data_chunk(std::span<const uint8_t> data) override;
int end_upload_data() override;
virtual void pause_read(IOCtrlReason reason);
virtual int resume_read(IOCtrlReason reason, size_t consumed);
virtual void force_resume_read();
void pause_read(IOCtrlReason reason) override;
int resume_read(IOCtrlReason reason, size_t consumed) override;
void force_resume_read() override;
virtual int on_read();
virtual int on_write();
int on_read() override;
int on_write() override;
virtual void on_upstream_change(Upstream *upstream);
void on_upstream_change(Upstream *upstream) override;
// true if this object is poolable.
virtual bool poolable() const;
bool poolable() const override;
virtual const std::shared_ptr<DownstreamAddrGroup> &
get_downstream_addr_group() const;
virtual DownstreamAddr *get_addr() const;
const std::shared_ptr<DownstreamAddrGroup> &
get_downstream_addr_group() const override;
DownstreamAddr *get_addr() const override;
int send_reply(unsigned int http_status, APIStatusCode api_status,
const std::string_view &data = ""sv);
std::string_view data = ""sv);
int error_method_not_allowed();
// Handles backendconfig API request.

View File

@@ -133,7 +133,7 @@ int ClientHandler::read_clear() {
return 0;
}
auto nread = conn_.read_clear(rb_.last(), rb_.wleft());
auto nread = conn_.read_clear(rb_.wbuffer());
if (nread == 0) {
if (rb_.rleft() == 0) {
@@ -187,7 +187,7 @@ int ClientHandler::proxy_protocol_peek_clear() {
assert(rb_.rleft() == 0);
auto nread = conn_.peek_clear(rb_.last(), rb_.wleft());
auto nread = conn_.peek_clear(rb_.wbuffer());
if (nread < 0) {
return -1;
}
@@ -263,7 +263,7 @@ int ClientHandler::read_tls() {
return 0;
}
auto nread = conn_.read_tls(rb_.last(), rb_.wleft());
auto nread = conn_.read_tls(rb_.wbuffer());
if (nread == 0) {
if (rb_.rleft() == 0) {
@@ -282,26 +282,19 @@ int ClientHandler::read_tls() {
}
int ClientHandler::write_tls() {
struct iovec iov;
ERR_clear_error();
if (on_write() != 0) {
return -1;
}
auto iovcnt = upstream_->response_riovec(&iov, 1);
if (iovcnt == 0) {
conn_.start_tls_write_idle();
conn_.wlimit.stopw();
ev_timer_stop(conn_.loop, &conn_.wt);
return 0;
}
for (;;) {
auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len);
if (on_write() != 0) {
return -1;
}
auto data = upstream_->response_peek();
if (data.empty()) {
break;
}
auto nwrite = conn_.write_tls(data);
if (nwrite < 0) {
return -1;
}
@@ -311,12 +304,14 @@ int ClientHandler::write_tls() {
}
upstream_->response_drain(as_unsigned(nwrite));
iovcnt = upstream_->response_riovec(&iov, 1);
if (iovcnt == 0) {
return 0;
}
}
conn_.start_tls_write_idle();
conn_.wlimit.stopw();
ev_timer_stop(conn_.loop, &conn_.wt);
return 0;
}
#ifdef ENABLE_HTTP3
@@ -446,8 +441,8 @@ int ClientHandler::upstream_http1_connhd_read() {
}
ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
const std::string_view &ipaddr,
const std::string_view &port, int family,
std::string_view ipaddr, std::string_view port,
int family,
const UpstreamAddr *faddr)
: // We use balloc_ for TLS session ID (64), ipaddr (IPv6) (39),
// port (5), forwarded-for (IPv6) (41), alpn (5), proxyproto
@@ -520,8 +515,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
}
}
void ClientHandler::init_forwarded_for(int family,
const std::string_view &ipaddr) {
void ClientHandler::init_forwarded_for(int family, std::string_view ipaddr) {
if (family == AF_INET6) {
// 2 for '[' and ']'
auto len = 2 + ipaddr.size();
@@ -707,7 +701,7 @@ int ClientHandler::on_read() {
}
int ClientHandler::on_write() { return on_write_(*this); }
const std::string_view &ClientHandler::get_ipaddr() const { return ipaddr_; }
std::string_view ClientHandler::get_ipaddr() const { return ipaddr_; }
bool ClientHandler::get_should_close_after_write() const {
return should_close_after_write_;
@@ -739,7 +733,7 @@ void ClientHandler::pool_downstream_connection(
namespace {
// Computes 32bits hash for session affinity for IP address |ip|.
uint32_t compute_affinity_from_ip(const std::string_view &ip) {
uint32_t compute_affinity_from_ip(std::string_view ip) {
int rv;
std::array<uint8_t, 32> buf;
@@ -809,9 +803,8 @@ Http2Session *ClientHandler::get_http2_session(
return session;
}
uint32_t
ClientHandler::get_affinity_cookie(Downstream *downstream,
const std::string_view &cookie_name) {
uint32_t ClientHandler::get_affinity_cookie(Downstream *downstream,
std::string_view cookie_name) {
auto h = downstream->find_affinity_cookie(cookie_name);
if (h) {
return h;
@@ -1264,13 +1257,13 @@ ssize_t parse_proxy_line_port(const uint8_t *first, const uint8_t *last) {
}
if (*p == '0') {
if (p + 1 != last && util::is_digit(as_signed(*(p + 1)))) {
if (p + 1 != last && util::is_digit(static_cast<char>(*(p + 1)))) {
return -1;
}
return 1;
}
for (; p != last && util::is_digit(as_signed(*p)); ++p) {
for (; p != last && util::is_digit(static_cast<char>(*p)); ++p) {
port *= 10;
port += *p - '0';
@@ -1295,7 +1288,7 @@ int ClientHandler::on_proxy_protocol_finish() {
rb_.reset();
if (conn_.read_nolim_clear(rb_.pos(), len) < 0) {
if (conn_.read_nolim_clear({rb_.pos(), len}) < 0) {
return -1;
}
@@ -1693,7 +1686,7 @@ const UpstreamAddr *ClientHandler::get_upstream_addr() const { return faddr_; }
Connection *ClientHandler::get_connection() { return &conn_; }
void ClientHandler::set_tls_sni(const std::string_view &sni) {
void ClientHandler::set_tls_sni(std::string_view sni) {
sni_ = make_string_ref(balloc_, sni);
}

View File

@@ -67,9 +67,8 @@ class Http3Upstream;
class ClientHandler {
public:
ClientHandler(Worker *worker, int fd, SSL *ssl,
const std::string_view &ipaddr, const std::string_view &port,
int family, const UpstreamAddr *faddr);
ClientHandler(Worker *worker, int fd, SSL *ssl, std::string_view ipaddr,
std::string_view port, int family, const UpstreamAddr *faddr);
~ClientHandler();
int noop();
@@ -107,7 +106,7 @@ public:
void reset_upstream_write_timeout(ev_tstamp t);
int validate_next_proto();
const std::string_view &get_ipaddr() const;
std::string_view get_ipaddr() const;
bool get_should_close_after_write() const;
void set_should_close_after_write(bool f);
Upstream *get_upstream();
@@ -143,7 +142,7 @@ public:
Worker *get_worker() const;
// Initializes forwarded_for_.
void init_forwarded_for(int family, const std::string_view &ipaddr);
void init_forwarded_for(int family, std::string_view ipaddr);
using ReadBuf = DefaultMemchunkBuffer;
@@ -179,7 +178,7 @@ public:
// Returns an affinity cookie value for |downstream|. |cookie_name|
// is used to inspect cookie header field in request header fields.
uint32_t get_affinity_cookie(Downstream *downstream,
const std::string_view &cookie_name);
std::string_view cookie_name);
DownstreamAddr *get_downstream_addr_strict_affinity(
int &err, const std::shared_ptr<SharedDownstreamAddr> &shared_addr,
@@ -194,7 +193,7 @@ public:
// Stores |sni| which is TLS SNI extension value client sent in this
// connection.
void set_tls_sni(const std::string_view &sni);
void set_tls_sni(std::string_view sni);
// Returns TLS SNI extension value client sent in this connection.
std::string_view get_tls_sni() const;

View File

@@ -131,8 +131,8 @@ struct HostPort {
namespace {
std::optional<HostPort> split_host_port(BlockAllocator &balloc,
const std::string_view &hostport,
const std::string_view &opt) {
std::string_view hostport,
std::string_view opt) {
// host and port in |hostport| is separated by single ','.
auto sep = std::ranges::find(hostport, ',');
if (sep == std::ranges::end(hostport)) {
@@ -160,7 +160,7 @@ std::optional<HostPort> split_host_port(BlockAllocator &balloc,
} // namespace
namespace {
bool is_secure(const std::string_view &filename) {
bool is_secure(std::string_view filename) {
struct stat buf;
int rv = stat(filename.data(), &buf);
if (rv == 0) {
@@ -182,7 +182,7 @@ read_tls_ticket_key_file(const std::vector<std::string_view> &files,
keys.resize(files.size());
auto enc_keylen = static_cast<size_t>(EVP_CIPHER_key_length(cipher));
auto hmac_keylen = static_cast<size_t>(EVP_MD_size(hmac));
if (cipher == EVP_aes_128_cbc()) {
if (cipher == nghttp2::tls::aes_128_cbc()) {
// backward compatibility, as a legacy of using same file format
// with nginx and apache.
hmac_keylen = 16;
@@ -251,7 +251,7 @@ read_tls_ticket_key_file(const std::vector<std::string_view> &files,
#ifdef ENABLE_HTTP3
std::shared_ptr<QUICKeyingMaterials>
read_quic_secret_file(const std::string_view &path) {
read_quic_secret_file(std::string_view path) {
constexpr size_t expectedlen =
SHRPX_QUIC_SECRET_RESERVEDLEN + SHRPX_QUIC_SECRETLEN + SHRPX_QUIC_SALTLEN;
@@ -352,8 +352,8 @@ FILE *open_file_for_write(const char *filename) {
namespace {
// Read passwd from |filename|
std::string read_passwd_from_file(const std::string_view &opt,
const std::string_view &filename) {
std::string read_passwd_from_file(std::string_view opt,
std::string_view filename) {
std::string line;
if (!is_secure(filename)) {
@@ -374,7 +374,7 @@ std::string read_passwd_from_file(const std::string_view &opt,
} // namespace
HeaderRefs::value_type parse_header(BlockAllocator &balloc,
const std::string_view &optarg) {
std::string_view optarg) {
auto colon = std::ranges::find(optarg, ':');
if (colon == std::ranges::end(optarg) ||
@@ -408,8 +408,7 @@ HeaderRefs::value_type parse_header(BlockAllocator &balloc,
}
template <typename T>
int parse_uint(T *dest, const std::string_view &opt,
const std::string_view &optarg) {
int parse_uint(T *dest, std::string_view opt, std::string_view optarg) {
auto val = util::parse_uint(optarg);
if (!val) {
LOG(ERROR) << opt << ": bad value. Specify an integer >= 0.";
@@ -423,8 +422,8 @@ int parse_uint(T *dest, const std::string_view &opt,
namespace {
template <typename T>
int parse_uint_with_unit(T *dest, const std::string_view &opt,
const std::string_view &optarg) {
int parse_uint_with_unit(T *dest, std::string_view opt,
std::string_view optarg) {
auto n = util::parse_uint_with_unit(optarg);
if (!n) {
LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
@@ -448,8 +447,8 @@ int parse_uint_with_unit(T *dest, const std::string_view &opt,
} // namespace
namespace {
int parse_altsvc(AltSvc &altsvc, const std::string_view &opt,
const std::string_view &optarg) {
int parse_altsvc(AltSvc &altsvc, std::string_view opt,
std::string_view optarg) {
// PROTOID, PORT, HOST, ORIGIN, PARAMS.
auto tokens = util::split_str(optarg, ',', 5);
@@ -500,7 +499,7 @@ int parse_altsvc(AltSvc &altsvc, const std::string_view &opt,
namespace {
// generated by gennghttpxfun.py
LogFragmentType log_var_lookup_token(const std::string_view &name) {
LogFragmentType log_var_lookup_token(std::string_view name) {
switch (name.size()) {
case 3:
switch (name[2]) {
@@ -721,7 +720,7 @@ bool var_token(char c) {
} // namespace
std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
const std::string_view &optarg) {
std::string_view optarg) {
auto literal_start = std::ranges::begin(optarg);
auto p = literal_start;
auto eop = std::ranges::end(optarg);
@@ -817,8 +816,8 @@ std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
}
namespace {
int parse_address_family(int *dest, const std::string_view &opt,
const std::string_view &optarg) {
int parse_address_family(int *dest, std::string_view opt,
std::string_view optarg) {
if (util::strieq("auto"sv, optarg)) {
*dest = AF_UNSPEC;
return 0;
@@ -838,8 +837,8 @@ int parse_address_family(int *dest, const std::string_view &opt,
} // namespace
namespace {
int parse_duration(ev_tstamp *dest, const std::string_view &opt,
const std::string_view &optarg) {
int parse_duration(ev_tstamp *dest, std::string_view opt,
std::string_view optarg) {
auto t = util::parse_duration_with_unit(optarg);
if (!t) {
LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
@@ -853,8 +852,8 @@ int parse_duration(ev_tstamp *dest, const std::string_view &opt,
} // namespace
namespace {
int parse_tls_proto_version(int &dest, const std::string_view &opt,
const std::string_view &optarg) {
int parse_tls_proto_version(int &dest, std::string_view opt,
std::string_view optarg) {
auto v = tls::proto_version_from_string(optarg);
if (v == -1) {
LOG(ERROR) << opt << ": invalid TLS protocol version: " << optarg;
@@ -876,8 +875,8 @@ namespace {
// and stores parsed results into |out|. This function returns 0 if
// it succeeds, or -1.
int parse_memcached_connection_params(MemcachedConnectionParams &out,
const std::string_view &src_params,
const std::string_view &opt) {
std::string_view src_params,
std::string_view opt) {
auto last = std::ranges::end(src_params);
for (auto first = std::ranges::begin(src_params); first != last;) {
auto end = std::ranges::find(first, last, ';');
@@ -915,8 +914,7 @@ namespace {
// Parses upstream configuration parameter |src_params|, and stores
// parsed results into |out|. This function returns 0 if it succeeds,
// or -1.
int parse_upstream_params(UpstreamParams &out,
const std::string_view &src_params) {
int parse_upstream_params(UpstreamParams &out, std::string_view src_params) {
auto last = std::ranges::end(src_params);
for (auto first = std::ranges::begin(src_params); first != last;) {
auto end = std::ranges::find(first, last, ';');
@@ -990,9 +988,8 @@ namespace {
// Parses |value| of parameter named |name| as duration. This
// function returns 0 if it succeeds and the parsed value is assigned
// to |dest|, or -1.
int parse_downstream_param_duration(ev_tstamp &dest,
const std::string_view &name,
const std::string_view &value) {
int parse_downstream_param_duration(ev_tstamp &dest, std::string_view name,
std::string_view value) {
auto t = util::parse_duration_with_unit(value);
if (!t) {
LOG(ERROR) << "backend: " << name << ": bad value: '" << value << "'";
@@ -1008,7 +1005,7 @@ namespace {
// parsed results into |out|. This function returns 0 if it succeeds,
// or -1.
int parse_downstream_params(DownstreamParams &out,
const std::string_view &src_params) {
std::string_view src_params) {
auto last = std::ranges::end(src_params);
for (auto first = std::ranges::begin(src_params); first != last;) {
auto end = std::ranges::find(first, last, ';');
@@ -1202,7 +1199,7 @@ namespace {
int parse_mapping(
Config *config, DownstreamAddrConfig &addr,
std::unordered_map<std::string_view, size_t> &pattern_addr_indexer,
const std::string_view &src_pattern, const std::string_view &src_params) {
std::string_view src_pattern, std::string_view src_params) {
// This returns at least 1 element (it could be empty string). We
// will append '/' to all patterns, so it becomes catch-all pattern.
auto mapping = util::split_str(src_pattern, ':');
@@ -1378,7 +1375,7 @@ int parse_mapping(
} // namespace
namespace {
ForwardedNode parse_forwarded_node_type(const std::string_view &optarg) {
ForwardedNode parse_forwarded_node_type(std::string_view optarg) {
if (util::strieq("obfuscated"sv, optarg)) {
return ForwardedNode::OBFUSCATED;
}
@@ -1403,9 +1400,8 @@ ForwardedNode parse_forwarded_node_type(const std::string_view &optarg) {
} // namespace
namespace {
int parse_error_page(std::vector<ErrorPage> &error_pages,
const std::string_view &opt,
const std::string_view &optarg) {
int parse_error_page(std::vector<ErrorPage> &error_pages, std::string_view opt,
std::string_view optarg) {
std::array<char, STRERROR_BUFSIZE> errbuf;
auto eq = std::ranges::find(optarg, '=');
@@ -1441,7 +1437,7 @@ int parse_error_page(std::vector<ErrorPage> &error_pages,
return -1;
}
auto fd_closer = defer(close, fd);
auto fd_closer = defer([fd] { close(fd); });
std::array<uint8_t, 4096> buf;
for (;;) {
@@ -1477,8 +1473,7 @@ struct SubcertParams {
namespace {
// Parses subcert parameter |src_params|, and stores parsed results
// into |out|. This function returns 0 if it succeeds, or -1.
int parse_subcert_params(SubcertParams &out,
const std::string_view &src_params) {
int parse_subcert_params(SubcertParams &out, std::string_view src_params) {
auto last = std::ranges::end(src_params);
for (auto first = std::ranges::begin(src_params); first != last;) {
auto end = std::ranges::find(first, last, ';');
@@ -1519,9 +1514,8 @@ int parse_subcert_params(SubcertParams &out,
namespace {
// Reads *.sct files from directory denoted by |dir_path|. |dir_path|
// must be NULL-terminated string.
int read_tls_sct_from_dir(std::vector<uint8_t> &dst,
const std::string_view &opt,
const std::string_view &dir_path) {
int read_tls_sct_from_dir(std::vector<uint8_t> &dst, std::string_view opt,
std::string_view dir_path) {
std::array<char, STRERROR_BUFSIZE> errbuf;
auto dir = opendir(dir_path.data());
@@ -1532,7 +1526,7 @@ int read_tls_sct_from_dir(std::vector<uint8_t> &dst,
return -1;
}
auto closer = defer(closedir, dir);
auto closer = defer([dir] { closedir(dir); });
// 2 bytes total length field
auto len_idx = dst.size();
@@ -1574,7 +1568,7 @@ int read_tls_sct_from_dir(std::vector<uint8_t> &dst,
return -1;
}
auto closer = defer(close, fd);
auto closer = defer([fd] { close(fd); });
// 2 bytes length field for this SCT.
auto len_idx = dst.size();
@@ -1638,7 +1632,7 @@ namespace {
// Reads PSK secrets from path, and parses each line. The result is
// directly stored into config->tls.psk_secrets. This function
// returns 0 if it succeeds, or -1.
int parse_psk_secrets(Config *config, const std::string_view &path) {
int parse_psk_secrets(Config *config, std::string_view path) {
auto &tlsconf = config->tls;
std::ifstream f(path.data(), std::ios::binary);
@@ -1704,7 +1698,7 @@ namespace {
// Reads PSK secrets from path, and parses each line. The result is
// directly stored into config->tls.client.psk. This function returns
// 0 if it succeeds, or -1.
int parse_client_psk_secrets(Config *config, const std::string_view &path) {
int parse_client_psk_secrets(Config *config, std::string_view path) {
auto &tlsconf = config->tls;
std::ifstream f(path.data(), std::ios::binary);
@@ -1762,7 +1756,7 @@ int parse_client_psk_secrets(Config *config, const std::string_view &path) {
#endif // !defined(OPENSSL_NO_PSK)
// generated by gennghttpxfun.py
int option_lookup_token(const std::string_view &name) {
int option_lookup_token(std::string_view name) {
switch (name.size()) {
case 4:
switch (name[3]) {
@@ -2838,7 +2832,7 @@ int option_lookup_token(const std::string_view &name) {
}
int parse_config(
Config *config, const std::string_view &opt, const std::string_view &optarg,
Config *config, std::string_view opt, std::string_view optarg,
std::unordered_set<std::string_view> &included_set,
std::unordered_map<std::string_view, size_t> &pattern_addr_indexer) {
auto optid = option_lookup_token(opt);
@@ -2847,8 +2841,7 @@ int parse_config(
}
int parse_config(
Config *config, int optid, const std::string_view &opt,
const std::string_view &optarg,
Config *config, int optid, std::string_view opt, std::string_view optarg,
std::unordered_set<std::string_view> &included_set,
std::unordered_map<std::string_view, size_t> &pattern_addr_indexer) {
std::array<char, STRERROR_BUFSIZE> errbuf;
@@ -3615,9 +3608,9 @@ int parse_config(
}
case SHRPX_OPTID_TLS_TICKET_KEY_CIPHER:
if (util::strieq("aes-128-cbc"sv, optarg)) {
config->tls.ticket.cipher = EVP_aes_128_cbc();
config->tls.ticket.cipher = nghttp2::tls::aes_128_cbc();
} else if (util::strieq("aes-256-cbc"sv, optarg)) {
config->tls.ticket.cipher = EVP_aes_256_cbc();
config->tls.ticket.cipher = nghttp2::tls::aes_256_cbc();
} else {
LOG(ERROR) << opt
<< ": unsupported cipher for ticket encryption: " << optarg;
@@ -4327,7 +4320,7 @@ std::string_view str_syslog_facility(int facility) {
}
}
int int_syslog_facility(const std::string_view &strfacility) {
int int_syslog_facility(std::string_view strfacility) {
if (util::strieq("auth"sv, strfacility)) {
return LOG_AUTH;
}
@@ -4444,7 +4437,7 @@ namespace {
// and do lower bound search in the array. The returned index is the
// backend to use.
int compute_affinity_hash(std::vector<AffinityHash> &res, size_t idx,
const std::string_view &s) {
std::string_view s) {
int rv;
std::array<uint8_t, 32> buf;
@@ -4657,10 +4650,11 @@ int configure_downstream_group(Config *config, bool http2_proxy,
auto path = addr.host.data();
auto pathlen = addr.host.size();
auto &unaddr = addr.addr.skaddr.emplace<sockaddr_un>();
if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) {
if (pathlen + 1 > sizeof(unaddr.sun_path)) {
LOG(FATAL) << "UNIX domain socket path " << path << " is too long > "
<< sizeof(addr.addr.su.un.sun_path);
<< sizeof(unaddr.sun_path);
return -1;
}
@@ -4669,11 +4663,9 @@ int configure_downstream_group(Config *config, bool http2_proxy,
<< " for backend connection";
}
addr.addr.su.un.sun_family = AF_UNIX;
unaddr.sun_family = AF_UNIX;
// copy path including terminal NULL
std::ranges::copy_n(path, as_signed(pathlen + 1),
addr.addr.su.un.sun_path);
addr.addr.len = sizeof(addr.addr.su.un);
std::ranges::copy_n(path, as_signed(pathlen + 1), unaddr.sun_path);
continue;
}
@@ -4723,8 +4715,9 @@ int configure_downstream_group(Config *config, bool http2_proxy,
key = addr.hostport;
}
} else {
key = std::string_view{reinterpret_cast<char *>(&addr.addr.su),
addr.addr.len};
key = std::string_view{
reinterpret_cast<const char *>(addr.addr.as_sockaddr()),
addr.addr.size()};
}
rv = compute_affinity_hash(g.affinity_hash, idx, key);
if (rv != 0) {
@@ -4788,7 +4781,7 @@ int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
return -1;
}
auto res_d = defer(freeaddrinfo, res);
auto res_d = defer([res] { freeaddrinfo(res); });
std::array<char, NI_MAXHOST> host;
rv = getnameinfo(res->ai_addr, res->ai_addrlen, host.data(), host.size(),
@@ -4805,8 +4798,7 @@ int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
<< " succeeded: " << host.data();
}
memcpy(&addr->su, res->ai_addr, res->ai_addrlen);
addr->len = res->ai_addrlen;
addr->set(res->ai_addr);
return 0;
}

View File

@@ -482,7 +482,7 @@ struct UpstreamAddr {
std::string_view hostport;
// Binary representation of this address. Only filled if quic is
// true.
sockaddr_union sockaddr;
Address sockaddr;
// frontend port. 0 if |host_unix| is true.
uint16_t port;
// For TCP socket, this is either AF_INET or AF_INET6. For UNIX
@@ -555,7 +555,7 @@ struct AffinityHash {
};
struct DownstreamAddrGroupConfig {
DownstreamAddrGroupConfig(const std::string_view &pattern)
DownstreamAddrGroupConfig(std::string_view pattern)
: pattern(pattern),
affinity{SessionAffinity::NONE},
redirect_if_not_tls(false),
@@ -920,7 +920,7 @@ struct RateLimitConfig {
// field. router includes all path patterns sharing the same wildcard
// host.
struct WildcardPattern {
WildcardPattern(const std::string_view &host) : host(host) {}
WildcardPattern(std::string_view host) : host(host) {}
// This might not be NULL terminated. Currently it is only used for
// comparison.
@@ -1343,7 +1343,7 @@ enum {
};
// Looks up token for given option name |name|.
int option_lookup_token(const std::string_view &name);
int option_lookup_token(std::string_view name);
// Parses option name |opt| and value |optarg|. The results are
// stored into the object pointed by |config|. This function returns 0
@@ -1354,15 +1354,14 @@ int option_lookup_token(const std::string_view &name);
// It is introduced to speed up loading configuration file with lots
// of backends.
int parse_config(
Config *config, const std::string_view &opt, const std::string_view &optarg,
Config *config, std::string_view opt, std::string_view optarg,
std::unordered_set<std::string_view> &included_set,
std::unordered_map<std::string_view, size_t> &pattern_addr_indexer);
// Similar to parse_config() above, but additional |optid| which
// should be the return value of option_lookup_token(opt).
int parse_config(
Config *config, int optid, const std::string_view &opt,
const std::string_view &optarg,
Config *config, int optid, std::string_view opt, std::string_view optarg,
std::unordered_set<std::string_view> &included_set,
std::unordered_map<std::string_view, size_t> &pattern_addr_indexer);
@@ -1379,16 +1378,16 @@ int load_config(
// is allowed at the start of the NAME, but NAME == ":" is not
// allowed. This function returns pair of NAME and VALUE.
HeaderRefs::value_type parse_header(BlockAllocator &balloc,
const std::string_view &optarg);
std::string_view optarg);
std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
const std::string_view &optarg);
std::string_view optarg);
// Returns string for syslog |facility|.
std::string_view str_syslog_facility(int facility);
// Returns integer value of syslog |facility| string.
int int_syslog_facility(const std::string_view &strfacility);
int int_syslog_facility(std::string_view strfacility);
FILE *open_file_for_write(const char *filename);
@@ -1402,7 +1401,7 @@ read_tls_ticket_key_file(const std::vector<std::string_view> &files,
#ifdef ENABLE_HTTP3
std::shared_ptr<QUICKeyingMaterials>
read_quic_secret_file(const std::string_view &path);
read_quic_secret_file(std::string_view path);
#endif // defined(ENABLE_HTTP3)
// Returns string representation of |proto|.

View File

@@ -189,9 +189,9 @@ void test_shrpx_config_read_tls_ticket_key_file(void) {
close(fd1);
close(fd2);
auto ticket_keys =
read_tls_ticket_key_file({std::string_view{file1}, std::string_view{file2}},
EVP_aes_128_cbc(), EVP_sha256());
auto ticket_keys = read_tls_ticket_key_file(
{std::string_view{file1}, std::string_view{file2}},
nghttp2::tls::aes_128_cbc(), nghttp2::tls::sha256());
unlink(file1);
unlink(file2);
assert_not_null(ticket_keys.get());
@@ -233,9 +233,9 @@ void test_shrpx_config_read_tls_ticket_key_file_aes_256(void) {
close(fd1);
close(fd2);
auto ticket_keys =
read_tls_ticket_key_file({std::string_view{file1}, std::string_view{file2}},
EVP_aes_256_cbc(), EVP_sha256());
auto ticket_keys = read_tls_ticket_key_file(
{std::string_view{file1}, std::string_view{file2}},
nghttp2::tls::aes_256_cbc(), nghttp2::tls::sha256());
unlink(file1);
unlink(file2);
assert_not_null(ticket_keys.get());

View File

@@ -482,7 +482,7 @@ void Connection::start_tls_write_idle() {
}
}
nghttp2_ssize Connection::write_tls(const void *data, size_t len) {
nghttp2_ssize Connection::write_tls(std::span<const uint8_t> data) {
// SSL_write requires the same arguments (buf pointer and its
// length) on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
// get_write_limit() may return smaller length than previously
@@ -490,13 +490,13 @@ nghttp2_ssize Connection::write_tls(const void *data, size_t len) {
// this, we keep last length passed to SSL_write to
// tls.last_writelen if SSL_write indicated I/O blocking.
if (tls.last_writelen == 0) {
len = std::min(len, wlimit.avail());
len = std::min(len, get_tls_write_limit());
if (len == 0) {
data = data.first(
std::ranges::min({data.size(), wlimit.avail(), get_tls_write_limit()}));
if (data.empty()) {
return 0;
}
} else {
len = tls.last_writelen;
data = data.first(tls.last_writelen);
tls.last_writelen = 0;
}
@@ -507,17 +507,17 @@ nghttp2_ssize Connection::write_tls(const void *data, size_t len) {
#ifdef NGHTTP2_GENUINE_OPENSSL
int rv;
if (SSL_is_init_finished(tls.ssl)) {
rv = SSL_write(tls.ssl, data, static_cast<int>(len));
rv = SSL_write(tls.ssl, data.data(), static_cast<int>(data.size()));
} else {
size_t nwrite;
rv = SSL_write_early_data(tls.ssl, data, len, &nwrite);
rv = SSL_write_early_data(tls.ssl, data.data(), data.size(), &nwrite);
// Use the same semantics with SSL_write.
if (rv == 1) {
rv = static_cast<int>(nwrite);
}
}
#else // !defined(NGHTTP2_GENUINE_OPENSSL)
auto rv = SSL_write(tls.ssl, data, static_cast<int>(len));
auto rv = SSL_write(tls.ssl, data.data(), static_cast<int>(data.size()));
#endif // !defined(NGHTTP2_GENUINE_OPENSSL)
if (rv <= 0) {
@@ -529,7 +529,7 @@ nghttp2_ssize Connection::write_tls(const void *data, size_t len) {
}
return SHRPX_ERR_NETWORK;
case SSL_ERROR_WANT_WRITE:
tls.last_writelen = len;
tls.last_writelen = data.size();
wlimit.startw();
ev_timer_again(loop, &wt);
@@ -559,13 +559,13 @@ nghttp2_ssize Connection::write_tls(const void *data, size_t len) {
return rv;
}
nghttp2_ssize Connection::read_tls(void *data, size_t len) {
nghttp2_ssize Connection::read_tls(std::span<uint8_t> data) {
ERR_clear_error();
#if defined(NGHTTP2_GENUINE_OPENSSL) || \
defined(NGHTTP2_OPENSSL_IS_BORINGSSL) || defined(NGHTTP2_OPENSSL_IS_WOLFSSL)
if (tls.earlybuf.rleft()) {
return as_signed(tls.earlybuf.remove(data, len));
return as_signed(tls.earlybuf.remove(data));
}
#endif // defined(NGHTTP2_GENUINE_OPENSSL) ||
// defined(NGHTTP2_OPENSSL_IS_BORINGSSL) ||
@@ -579,12 +579,12 @@ nghttp2_ssize Connection::read_tls(void *data, size_t len) {
// to SSL_read to tls_last_readlen_ if SSL_read indicated I/O
// blocking.
if (tls.last_readlen == 0) {
len = std::min(len, rlimit.avail());
if (len == 0) {
data = data.first(std::min(data.size(), rlimit.avail()));
if (data.empty()) {
return 0;
}
} else {
len = tls.last_readlen;
data = data.first(tls.last_readlen);
tls.last_readlen = 0;
}
@@ -592,12 +592,12 @@ nghttp2_ssize Connection::read_tls(void *data, size_t len) {
if (!tls.early_data_finish) {
// TLSv1.3 handshake is still going on.
size_t nread;
auto rv = SSL_read_early_data(tls.ssl, data, len, &nread);
auto rv = SSL_read_early_data(tls.ssl, data.data(), data.size(), &nread);
if (rv == SSL_READ_EARLY_DATA_ERROR) {
auto err = SSL_get_error(tls.ssl, rv);
switch (err) {
case SSL_ERROR_WANT_READ:
tls.last_readlen = len;
tls.last_readlen = data.size();
return 0;
case SSL_ERROR_SSL:
if (LOG_ENABLED(INFO)) {
@@ -636,12 +636,12 @@ nghttp2_ssize Connection::read_tls(void *data, size_t len) {
if (!tls.early_data_finish) {
// TLSv1.3 handshake is still going on.
size_t nread = 0;
auto rv = SSL_read_early_data(tls.ssl, data, len, &nread);
auto rv = SSL_read_early_data(tls.ssl, data.data(), data.size(), &nread);
if (rv < 0) {
auto err = SSL_get_error(tls.ssl, rv);
switch (err) {
case SSL_ERROR_WANT_READ:
tls.last_readlen = len;
tls.last_readlen = data.size();
return 0;
case SSL_ERROR_SSL:
if (LOG_ENABLED(INFO)) {
@@ -677,13 +677,13 @@ nghttp2_ssize Connection::read_tls(void *data, size_t len) {
#endif // defined(NGHTTP2_OPENSSL_IS_WOLFSSL) &&
// defined(WOLFSSL_EARLY_DATA)
auto rv = SSL_read(tls.ssl, data, static_cast<int>(len));
auto rv = SSL_read(tls.ssl, data.data(), static_cast<int>(data.size()));
if (rv <= 0) {
auto err = SSL_get_error(tls.ssl, rv);
switch (err) {
case SSL_ERROR_WANT_READ:
tls.last_readlen = len;
tls.last_readlen = data.size();
return 0;
case SSL_ERROR_WANT_WRITE:
if (LOG_ENABLED(INFO)) {
@@ -710,14 +710,14 @@ nghttp2_ssize Connection::read_tls(void *data, size_t len) {
return rv;
}
nghttp2_ssize Connection::write_clear(const void *data, size_t len) {
len = std::min(len, wlimit.avail());
if (len == 0) {
nghttp2_ssize Connection::write_clear(std::span<const uint8_t> data) {
data = data.first(std::min(data.size(), wlimit.avail()));
if (data.empty()) {
return 0;
}
ssize_t nwrite;
while ((nwrite = write(fd, data, len)) == -1 && errno == EINTR)
while ((nwrite = write(fd, data.data(), data.size())) == -1 && errno == EINTR)
;
if (nwrite == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
@@ -764,14 +764,14 @@ nghttp2_ssize Connection::writev_clear(struct iovec *iov, int iovcnt) {
return nwrite;
}
nghttp2_ssize Connection::read_clear(void *data, size_t len) {
len = std::min(len, rlimit.avail());
if (len == 0) {
nghttp2_ssize Connection::read_clear(std::span<uint8_t> data) {
data = data.first(std::min(data.size(), rlimit.avail()));
if (data.empty()) {
return 0;
}
ssize_t nread;
while ((nread = read(fd, data, len)) == -1 && errno == EINTR)
while ((nread = read(fd, data.data(), data.size())) == -1 && errno == EINTR)
;
if (nread == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
@@ -789,9 +789,9 @@ nghttp2_ssize Connection::read_clear(void *data, size_t len) {
return nread;
}
nghttp2_ssize Connection::read_nolim_clear(void *data, size_t len) {
nghttp2_ssize Connection::read_nolim_clear(std::span<uint8_t> data) {
ssize_t nread;
while ((nread = read(fd, data, len)) == -1 && errno == EINTR)
while ((nread = read(fd, data.data(), data.size())) == -1 && errno == EINTR)
;
if (nread == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
@@ -807,9 +807,10 @@ nghttp2_ssize Connection::read_nolim_clear(void *data, size_t len) {
return nread;
}
nghttp2_ssize Connection::peek_clear(void *data, size_t len) {
nghttp2_ssize Connection::peek_clear(std::span<uint8_t> data) {
ssize_t nread;
while ((nread = recv(fd, data, len, MSG_PEEK)) == -1 && errno == EINTR)
while ((nread = recv(fd, data.data(), data.size(), MSG_PEEK)) == -1 &&
errno == EINTR)
;
if (nread == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {

View File

@@ -121,8 +121,8 @@ struct Connection {
// underlying connection blocks), return 0. SHRPX_ERR_EOF is
// returned in case of EOF and no data was read. Otherwise
// SHRPX_ERR_NETWORK is return in case of error.
nghttp2_ssize write_tls(const void *data, size_t len);
nghttp2_ssize read_tls(void *data, size_t len);
nghttp2_ssize write_tls(std::span<const uint8_t> data);
nghttp2_ssize read_tls(std::span<uint8_t> data);
size_t get_tls_write_limit();
// Updates the number of bytes written in warm up period.
@@ -131,13 +131,13 @@ struct Connection {
// determine fallback to short record size mode.
void start_tls_write_idle();
nghttp2_ssize write_clear(const void *data, size_t len);
nghttp2_ssize write_clear(std::span<const uint8_t> data);
nghttp2_ssize writev_clear(struct iovec *iov, int iovcnt);
nghttp2_ssize read_clear(void *data, size_t len);
nghttp2_ssize read_clear(std::span<uint8_t> data);
// Read at most |len| bytes of data from socket without rate limit.
nghttp2_ssize read_nolim_clear(void *data, size_t len);
nghttp2_ssize read_nolim_clear(std::span<uint8_t> data);
// Peek at most |len| bytes of data from socket without rate limit.
nghttp2_ssize peek_clear(void *data, size_t len);
nghttp2_ssize peek_clear(std::span<uint8_t> data);
void handle_tls_pending_read();

View File

@@ -352,7 +352,7 @@ void ConnectionHandler::graceful_shutdown_worker() {
#ifndef NOTHREADS
ev_async_start(loop_, &thread_join_asyncev_);
thread_join_fut_ = std::async(std::launch::async, [this]() {
thread_join_fut_ = std::async(std::launch::async, [this] {
(void)reopen_log_files(get_config()->logging);
join_worker();
ev_async_send(get_loop(), &thread_join_asyncev_);
@@ -677,19 +677,23 @@ int ConnectionHandler::forward_quic_packet_to_lingering_worker_process(
std::array<uint8_t, 512> header;
assert(header.size() >= 1 + 1 + 1 + 1 + sizeof(sockaddr_storage) * 2);
assert(remote_addr.len > 0);
assert(local_addr.len > 0);
assert(!remote_addr.empty());
assert(!local_addr.empty());
auto p = header.data();
*p++ = static_cast<uint8_t>(QUICIPCType::DGRAM_FORWARD);
*p++ = static_cast<uint8_t>(remote_addr.len - 1);
p = std::ranges::copy_n(reinterpret_cast<const uint8_t *>(&remote_addr.su),
as_signed(remote_addr.len), p)
auto remote_addrlen = remote_addr.size();
*p++ = static_cast<uint8_t>(remote_addrlen - 1);
p = std::ranges::copy_n(
reinterpret_cast<const uint8_t *>(remote_addr.as_sockaddr()),
as_signed(remote_addrlen), p)
.out;
*p++ = static_cast<uint8_t>(local_addr.len - 1);
p = std::ranges::copy_n(reinterpret_cast<const uint8_t *>(&local_addr.su),
as_signed(local_addr.len), p)
auto local_addrlen = local_addr.size();
*p++ = static_cast<uint8_t>(local_addrlen - 1);
p = std::ranges::copy_n(
reinterpret_cast<const uint8_t *>(local_addr.as_sockaddr()),
as_signed(local_addrlen), p)
.out;
*p++ = pi.ecn;
@@ -790,8 +794,9 @@ int ConnectionHandler::quic_ipc_read() {
return -1;
}
pkt->remote_addr.len = remote_addrlen;
memcpy(&pkt->remote_addr.su, p, remote_addrlen);
sockaddr_storage ss;
memcpy(&ss, p, remote_addrlen);
pkt->remote_addr.set(reinterpret_cast<const sockaddr *>(&ss));
p += remote_addrlen;
@@ -810,8 +815,8 @@ int ConnectionHandler::quic_ipc_read() {
return -1;
}
pkt->local_addr.len = local_addrlen;
memcpy(&pkt->local_addr.su, p, local_addrlen);
memcpy(&ss, p, local_addrlen);
pkt->local_addr.set(reinterpret_cast<const sockaddr *>(&ss));
p += local_addrlen;
@@ -859,9 +864,10 @@ int ConnectionHandler::quic_ipc_read() {
ConnectionID decrypted_dcid;
if (decrypt_quic_connection_id(decrypted_dcid,
vc.dcid + SHRPX_QUIC_CID_WORKER_ID_OFFSET,
qkm.cid_decryption_ctx) != 0) {
if (decrypt_quic_connection_id(
decrypted_dcid,
std::span{vc.dcid, vc.dcidlen}.subspan(SHRPX_QUIC_CID_WORKER_ID_OFFSET),
qkm.cid_decryption_ctx) != 0) {
return -1;
}

View File

@@ -137,7 +137,7 @@ DNSResolver::~DNSResolver() {
ev_timer_stop(loop_, &timer_);
}
int DNSResolver::resolve(const std::string_view &name, int family) {
int DNSResolver::resolve(std::string_view name, int family) {
if (status_ != DNSResolverStatus::IDLE) {
return -1;
}
@@ -238,7 +238,7 @@ DNSResolverStatus DNSResolver::get_status(Address *result) const {
}
if (result) {
memcpy(result, &result_, sizeof(result_));
*result = result_;
}
return status_;
@@ -311,24 +311,32 @@ void DNSResolver::on_result(int status, ares_addrinfo *ai) {
for (; ap; ap = ap->ai_next) {
switch (ap->ai_family) {
case AF_INET:
case AF_INET: {
status_ = DNSResolverStatus::OK;
result_.len = sizeof(result_.su.in);
assert(sizeof(result_.su.in) == ap->ai_addrlen);
sockaddr_in sa;
memcpy(&result_.su.in, ap->ai_addr, sizeof(result_.su.in));
assert(sizeof(sa) == ap->ai_addrlen);
memcpy(&sa, ap->ai_addr, sizeof(sa));
result_.skaddr.emplace<sockaddr_in>(sa);
break;
case AF_INET6:
}
case AF_INET6: {
status_ = DNSResolverStatus::OK;
result_.len = sizeof(result_.su.in6);
assert(sizeof(result_.su.in6) == ap->ai_addrlen);
sockaddr_in6 sa;
memcpy(&result_.su.in6, ap->ai_addr, sizeof(result_.su.in6));
assert(sizeof(sa) == ap->ai_addrlen);
memcpy(&sa, ap->ai_addr, sizeof(sa));
result_.skaddr.emplace<sockaddr_in6>(sa);
break;
}
default:
continue;
}
@@ -348,7 +356,7 @@ void DNSResolver::on_result(int status, ares_addrinfo *ai) {
if (status_ == DNSResolverStatus::OK) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded: " << name_ << " -> "
<< util::numeric_name(&result_.su.sa, result_.len);
<< util::numeric_name(result_.as_sockaddr(), result_.size());
}
return;
}

View File

@@ -74,7 +74,7 @@ public:
~DNSResolver();
// Starts resolving hostname |name|.
int resolve(const std::string_view &name, int family);
int resolve(std::string_view name, int family);
// Returns status. If status_ is DNSResolverStatus::SUCCESS &&
// |result| is not nullptr, |*result| is filled.
DNSResolverStatus get_status(Address *result) const;

View File

@@ -148,7 +148,7 @@ DNSResolverStatus DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
case DNSResolverStatus::OK:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
<< util::numeric_name(&result->su.sa, result->len);
<< util::numeric_name(result->as_sockaddr(), result->size());
}
ents_.emplace(host, make_entry(nullptr, std::move(host_copy),
@@ -210,7 +210,7 @@ DNSResolverStatus DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
case DNSResolverStatus::OK:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded: " << host << " -> "
<< util::numeric_name(&result->su.sa, result->len);
<< util::numeric_name(result->as_sockaddr(), result->size());
}
update_entry(ent, nullptr, DNSResolverStatus::OK, result);
@@ -242,10 +242,11 @@ DNSResolverStatus DNSTracker::resolve(Address *result, DNSQuery *dnsq) {
case DNSResolverStatus::OK:
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Name lookup succeeded (cached): " << dnsq->host << " -> "
<< util::numeric_name(&ent.result.su.sa, ent.result.len);
<< util::numeric_name(ent.result.as_sockaddr(),
ent.result.size());
}
if (result) {
memcpy(result, &ent.result, sizeof(*result));
*result = ent.result;
}
return DNSResolverStatus::OK;
default:

View File

@@ -321,7 +321,7 @@ void Downstream::force_resume_read() {
namespace {
const HeaderRefs::value_type *
search_header_linear_backwards(const HeaderRefs &headers,
const std::string_view &name) {
std::string_view name) {
for (auto it = headers.rbegin(); it != headers.rend(); ++it) {
auto &kv = *it;
if (kv.name == name) {
@@ -374,7 +374,7 @@ std::string_view Downstream::assemble_request_cookie() {
return as_string_view(std::ranges::begin(iov), p);
}
uint32_t Downstream::find_affinity_cookie(const std::string_view &name) {
uint32_t Downstream::find_affinity_cookie(std::string_view name) {
for (auto &kv : req_.fs.headers()) {
if (kv.token != http2::HD_COOKIE) {
continue;
@@ -467,8 +467,8 @@ void Downstream::crumble_request_cookie(std::vector<nghttp2_nv> &nva) {
}
namespace {
void add_header(size_t &sum, HeaderRefs &headers, const std::string_view &name,
const std::string_view &value, bool no_index, int32_t token) {
void add_header(size_t &sum, HeaderRefs &headers, std::string_view name,
std::string_view value, bool no_index, int32_t token) {
sum += name.size() + value.size();
headers.emplace_back(name, value, no_index, token);
}
@@ -476,7 +476,7 @@ void add_header(size_t &sum, HeaderRefs &headers, const std::string_view &name,
namespace {
std::string_view alloc_header_name(BlockAllocator &balloc,
const std::string_view &name) {
std::string_view name) {
auto iov = make_byte_ref(balloc, name.size() + 1);
auto p = util::tolower(name, std::ranges::begin(iov));
*p = '\0';
@@ -487,7 +487,7 @@ std::string_view alloc_header_name(BlockAllocator &balloc,
namespace {
void append_last_header_key(BlockAllocator &balloc, bool &key_prev, size_t &sum,
HeaderRefs &headers, const std::string_view &data) {
HeaderRefs &headers, std::string_view data) {
assert(key_prev);
sum += data.size();
auto &item = headers.back();
@@ -502,7 +502,7 @@ void append_last_header_key(BlockAllocator &balloc, bool &key_prev, size_t &sum,
namespace {
void append_last_header_value(BlockAllocator &balloc, bool &key_prev,
size_t &sum, HeaderRefs &headers,
const std::string_view &data) {
std::string_view data) {
key_prev = false;
sum += data.size();
auto &item = headers.back();
@@ -550,30 +550,28 @@ HeaderRefs::value_type *FieldStore::header(int32_t token) {
return nullptr;
}
const HeaderRefs::value_type *
FieldStore::header(const std::string_view &name) const {
const HeaderRefs::value_type *FieldStore::header(std::string_view name) const {
return search_header_linear_backwards(headers_, name);
}
void FieldStore::add_header_token(const std::string_view &name,
const std::string_view &value, bool no_index,
int32_t token) {
void FieldStore::add_header_token(std::string_view name, std::string_view value,
bool no_index, int32_t token) {
shrpx::add_header(buffer_size_, headers_, name, value, no_index, token);
}
void FieldStore::alloc_add_header_name(const std::string_view &name) {
void FieldStore::alloc_add_header_name(std::string_view name) {
auto name_ref = alloc_header_name(balloc_, name);
auto token = http2::lookup_token(name_ref);
add_header_token(name_ref, ""sv, false, token);
header_key_prev_ = true;
}
void FieldStore::append_last_header_key(const std::string_view &data) {
void FieldStore::append_last_header_key(std::string_view data) {
shrpx::append_last_header_key(balloc_, header_key_prev_, buffer_size_,
headers_, data);
}
void FieldStore::append_last_header_value(const std::string_view &data) {
void FieldStore::append_last_header_value(std::string_view data) {
shrpx::append_last_header_value(balloc_, header_key_prev_, buffer_size_,
headers_, data);
}
@@ -583,27 +581,27 @@ void FieldStore::clear_headers() {
header_key_prev_ = false;
}
void FieldStore::add_trailer_token(const std::string_view &name,
const std::string_view &value, bool no_index,
void FieldStore::add_trailer_token(std::string_view name,
std::string_view value, bool no_index,
int32_t token) {
// Header size limit should be applied to all header and trailer
// fields combined.
shrpx::add_header(buffer_size_, trailers_, name, value, no_index, token);
}
void FieldStore::alloc_add_trailer_name(const std::string_view &name) {
void FieldStore::alloc_add_trailer_name(std::string_view name) {
auto name_ref = alloc_header_name(balloc_, name);
auto token = http2::lookup_token(name_ref);
add_trailer_token(name_ref, ""sv, false, token);
trailer_key_prev_ = true;
}
void FieldStore::append_last_trailer_key(const std::string_view &data) {
void FieldStore::append_last_trailer_key(std::string_view data) {
shrpx::append_last_header_key(balloc_, trailer_key_prev_, buffer_size_,
trailers_, data);
}
void FieldStore::append_last_trailer_value(const std::string_view &data) {
void FieldStore::append_last_trailer_value(std::string_view data) {
shrpx::append_last_header_value(balloc_, trailer_key_prev_, buffer_size_,
trailers_, data);
}
@@ -684,12 +682,12 @@ int Downstream::push_request_headers() {
return dconn_->push_request_headers();
}
int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) {
req_.recv_body_length += datalen;
int Downstream::push_upload_data_chunk(std::span<const uint8_t> data) {
req_.recv_body_length += data.size();
if (!dconn_ && !request_header_sent_) {
blocked_request_buf_.append(data, datalen);
req_.unconsumed_body_length += datalen;
blocked_request_buf_.append(data);
req_.unconsumed_body_length += data.size();
return 0;
}
@@ -699,11 +697,11 @@ int Downstream::push_upload_data_chunk(const uint8_t *data, size_t datalen) {
DLOG(INFO, this) << "dconn_ is NULL";
return -1;
}
if (dconn_->push_upload_data_chunk(data, datalen) != 0) {
if (dconn_->push_upload_data_chunk(data) != 0) {
return -1;
}
req_.unconsumed_body_length += datalen;
req_.unconsumed_body_length += data.size();
return 0;
}
@@ -721,7 +719,7 @@ int Downstream::end_upload_data() {
}
void Downstream::rewrite_location_response_header(
const std::string_view &upstream_scheme) {
std::string_view upstream_scheme) {
auto hd = resp_.fs.header(http2::HD_LOCATION);
if (!hd) {
return;
@@ -1104,7 +1102,7 @@ void Downstream::add_retry() { ++num_retry_; }
bool Downstream::no_more_retry() const { return num_retry_ > 50; }
void Downstream::set_request_downstream_host(const std::string_view &host) {
void Downstream::set_request_downstream_host(std::string_view host) {
request_downstream_host_ = host;
}
@@ -1210,7 +1208,7 @@ void Downstream::set_blocked_request_data_eof(bool f) {
blocked_request_data_eof_ = f;
}
void Downstream::set_ws_key(const std::string_view &key) { ws_key_ = key; }
void Downstream::set_ws_key(std::string_view key) { ws_key_ = key; }
bool Downstream::get_expect_100_continue() const {
return expect_100_continue_;

View File

@@ -89,19 +89,18 @@ public:
HeaderRefs::value_type *header(int32_t token);
// Returns pointer to the header field with the name |name|. If no
// such header is found, returns nullptr.
const HeaderRefs::value_type *header(const std::string_view &name) const;
const HeaderRefs::value_type *header(std::string_view name) const;
void add_header_token(const std::string_view &name,
const std::string_view &value, bool no_index,
int32_t token);
void add_header_token(std::string_view name, std::string_view value,
bool no_index, int32_t token);
// Adds header field name |name|. First, the copy of header field
// name pointed by name.c_str() of length name.size() is made, and
// stored.
void alloc_add_header_name(const std::string_view &name);
void alloc_add_header_name(std::string_view name);
void append_last_header_key(const std::string_view &data);
void append_last_header_value(const std::string_view &data);
void append_last_header_key(std::string_view data);
void append_last_header_value(std::string_view data);
bool header_key_prev() const { return header_key_prev_; }
@@ -112,17 +111,16 @@ public:
// Empties headers.
void clear_headers();
void add_trailer_token(const std::string_view &name,
const std::string_view &value, bool no_index,
int32_t token);
void add_trailer_token(std::string_view name, std::string_view value,
bool no_index, int32_t token);
// Adds trailer field name |name|. First, the copy of trailer field
// name pointed by name.c_str() of length name.size() is made, and
// stored.
void alloc_add_trailer_name(const std::string_view &name);
void alloc_add_trailer_name(std::string_view name);
void append_last_trailer_key(const std::string_view &data);
void append_last_trailer_value(const std::string_view &data);
void append_last_trailer_key(std::string_view data);
void append_last_trailer_value(std::string_view data);
bool trailer_key_prev() const { return trailer_key_prev_; }
@@ -253,9 +251,8 @@ struct Response {
// returns true if a resource denoted by scheme, authority, and path
// has already been pushed.
bool is_resource_pushed(const std::string_view &scheme,
const std::string_view &authority,
const std::string_view &path) const {
bool is_resource_pushed(std::string_view scheme, std::string_view authority,
std::string_view path) const {
if (!pushed_resources) {
return false;
}
@@ -266,9 +263,8 @@ struct Response {
// remember that a resource denoted by scheme, authority, and path
// is pushed.
void resource_pushed(const std::string_view &scheme,
const std::string_view &authority,
const std::string_view &path) {
void resource_pushed(std::string_view scheme, std::string_view authority,
std::string_view path) {
if (!pushed_resources) {
pushed_resources = std::make_unique<std::vector<
std::tuple<std::string_view, std::string_view, std::string_view>>>();
@@ -392,12 +388,12 @@ public:
int push_request_headers();
bool get_chunked_request() const;
void set_chunked_request(bool f);
int push_upload_data_chunk(const uint8_t *data, size_t datalen);
int push_upload_data_chunk(std::span<const uint8_t> data);
int end_upload_data();
// Validates that received request body length and content-length
// matches.
bool validate_request_recv_body_length() const;
void set_request_downstream_host(const std::string_view &host);
void set_request_downstream_host(std::string_view host);
bool expect_response_body() const;
bool expect_response_trailer() const;
void set_request_state(DownstreamState state);
@@ -422,8 +418,7 @@ public:
Response &response() { return resp_; }
// Rewrites the location response header field.
void
rewrite_location_response_header(const std::string_view &upstream_scheme);
void rewrite_location_response_header(std::string_view upstream_scheme);
bool get_chunked_response() const;
void set_chunked_response(bool f);
@@ -518,7 +513,7 @@ public:
// cookie is given in |name|. If an affinity cookie is found, it is
// assigned to a member function, and is returned. If it is not
// found, or is malformed, returns 0.
uint32_t find_affinity_cookie(const std::string_view &name);
uint32_t find_affinity_cookie(std::string_view name);
// Set |h| as affinity cookie.
void renew_affinity_cookie(uint32_t h);
// Returns affinity cookie to send. If it does not need to be sent,
@@ -526,7 +521,7 @@ public:
// field, returns 0.
uint32_t get_affinity_cookie_to_send() const;
void set_ws_key(const std::string_view &key);
void set_ws_key(std::string_view key);
bool get_expect_100_continue() const;

View File

@@ -28,6 +28,7 @@
#include "shrpx.h"
#include <memory>
#include <span>
#include "shrpx_io_control.h"
@@ -47,7 +48,7 @@ public:
virtual void detach_downstream(Downstream *downstream) = 0;
virtual int push_request_headers() = 0;
virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen) = 0;
virtual int push_upload_data_chunk(std::span<const uint8_t> data) = 0;
virtual int end_upload_data() = 0;
virtual void pause_read(IOCtrlReason reason) = 0;

View File

@@ -58,7 +58,7 @@ void DownstreamQueue::mark_failure(Downstream *downstream) {
}
DownstreamQueue::HostEntry &
DownstreamQueue::find_host_entry(const std::string_view &host) {
DownstreamQueue::find_host_entry(std::string_view host) {
auto itr = host_entries_.find(host);
if (itr == std::ranges::end(host_entries_)) {
auto key = ImmutableString{host};
@@ -69,8 +69,7 @@ DownstreamQueue::find_host_entry(const std::string_view &host) {
return (*itr).second;
}
std::string_view
DownstreamQueue::make_host_key(const std::string_view &host) const {
std::string_view DownstreamQueue::make_host_key(std::string_view host) const {
return unified_host_ ? ""sv : host;
}
@@ -95,7 +94,7 @@ void DownstreamQueue::mark_blocked(Downstream *downstream) {
ent.blocked.append(link);
}
bool DownstreamQueue::can_activate(const std::string_view &host) const {
bool DownstreamQueue::can_activate(std::string_view host) const {
auto itr = host_entries_.find(make_host_key(host));
if (itr == std::ranges::end(host_entries_)) {
return true;
@@ -107,7 +106,7 @@ bool DownstreamQueue::can_activate(const std::string_view &host) const {
namespace {
bool remove_host_entry_if_empty(const DownstreamQueue::HostEntry &ent,
DownstreamQueue::HostEntryMap &host_entries,
const std::string_view &host) {
std::string_view host) {
if (ent.blocked.empty() && ent.num_active == 0) {
host_entries.erase(host);
return true;

View File

@@ -85,7 +85,7 @@ public:
void mark_blocked(Downstream *downstream);
// Returns true if we can make downstream connection to given
// |host|.
bool can_activate(const std::string_view &host) const;
bool can_activate(std::string_view host) const;
// Removes and frees |downstream| object. If |downstream| is in
// DispatchState::ACTIVE, and |next_blocked| is true, this function
// may return Downstream object with the same target host in
@@ -94,8 +94,8 @@ public:
Downstream *remove_and_get_blocked(Downstream *downstream,
bool next_blocked = true);
Downstream *get_downstreams() const;
HostEntry &find_host_entry(const std::string_view &host);
std::string_view make_host_key(const std::string_view &host) const;
HostEntry &find_host_entry(std::string_view host);
std::string_view make_host_key(std::string_view host) const;
std::string_view make_host_key(Downstream *downstream) const;
private:

View File

@@ -52,7 +52,7 @@ DualDNSResolver::DualDNSResolver(struct ev_loop *loop, int family)
}
}
int DualDNSResolver::resolve(const std::string_view &host) {
int DualDNSResolver::resolve(std::string_view host) {
int rv4 = 0, rv6 = 0;
if (family_ == AF_UNSPEC || family_ == AF_INET) {
rv4 = resolv4_.resolve(host, AF_INET);

View File

@@ -49,7 +49,7 @@ public:
DualDNSResolver(struct ev_loop *loop, int family);
// Resolves |host|. |host| must be NULL-terminated string.
int resolve(const std::string_view &host);
int resolve(std::string_view host);
CompleteCb get_complete_cb() const;
void set_complete_cb(CompleteCb cb);
DNSResolverStatus get_status(Address *result) const;

View File

@@ -64,7 +64,7 @@ int HealthMonitorDownstreamConnection::push_request_headers() {
}
int HealthMonitorDownstreamConnection::push_upload_data_chunk(
const uint8_t *data, size_t datalen) {
std::span<const uint8_t> data) {
return 0;
}
@@ -77,7 +77,7 @@ int HealthMonitorDownstreamConnection::end_upload_data() {
resp.fs.add_header_token("content-length"sv, "0"sv, false,
http2::HD_CONTENT_LENGTH);
if (upstream->send_reply(downstream_, nullptr, 0) != 0) {
if (upstream->send_reply(downstream_, {}) != 0) {
return -1;
}

View File

@@ -34,29 +34,29 @@ class Worker;
class HealthMonitorDownstreamConnection : public DownstreamConnection {
public:
HealthMonitorDownstreamConnection();
virtual ~HealthMonitorDownstreamConnection();
virtual int attach_downstream(Downstream *downstream);
virtual void detach_downstream(Downstream *downstream);
~HealthMonitorDownstreamConnection() override;
int attach_downstream(Downstream *downstream) override;
void detach_downstream(Downstream *downstream) override;
virtual int push_request_headers();
virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);
virtual int end_upload_data();
int push_request_headers() override;
int push_upload_data_chunk(std::span<const uint8_t> data) override;
int end_upload_data() override;
virtual void pause_read(IOCtrlReason reason);
virtual int resume_read(IOCtrlReason reason, size_t consumed);
virtual void force_resume_read();
void pause_read(IOCtrlReason reason) override;
int resume_read(IOCtrlReason reason, size_t consumed) override;
void force_resume_read() override;
virtual int on_read();
virtual int on_write();
int on_read() override;
int on_write() override;
virtual void on_upstream_change(Upstream *upstream);
void on_upstream_change(Upstream *upstream) override;
// true if this object is poolable.
virtual bool poolable() const;
bool poolable() const override;
virtual const std::shared_ptr<DownstreamAddrGroup> &
get_downstream_addr_group() const;
virtual DownstreamAddr *get_addr() const;
const std::shared_ptr<DownstreamAddrGroup> &
get_downstream_addr_group() const override;
DownstreamAddr *get_addr() const override;
};
} // namespace shrpx

View File

@@ -56,10 +56,10 @@ std::string_view create_error_html(BlockAllocator &balloc,
}
std::string_view create_forwarded(BlockAllocator &balloc, uint32_t params,
const std::string_view &node_by,
const std::string_view &node_for,
const std::string_view &host,
const std::string_view &proto) {
std::string_view node_by,
std::string_view node_for,
std::string_view host,
std::string_view proto) {
size_t len = 0;
if ((params & FORWARDED_BY) && !node_by.empty()) {
len += str_size("by=\"") + node_by.size() + str_size("\";");
@@ -128,7 +128,7 @@ std::string_view create_forwarded(BlockAllocator &balloc, uint32_t params,
return as_string_view(std::ranges::begin(iov), p);
}
std::string colorize_headers(const std::string_view &hdrs) {
std::string colorize_headers(std::string_view hdrs) {
std::string nhdrs;
auto p = std::ranges::find(hdrs, '\n');
if (p == std::ranges::end(hdrs)) {
@@ -180,10 +180,9 @@ nghttp2_ssize select_padding_callback(nghttp2_session *session,
}
std::string_view create_affinity_cookie(BlockAllocator &balloc,
const std::string_view &name,
std::string_view name,
uint32_t affinity_cookie,
const std::string_view &path,
bool secure) {
std::string_view path, bool secure) {
static constexpr auto PATH_PREFIX = "; Path="sv;
static constexpr auto SECURE = "; Secure"sv;
// <name>=<value>[; Path=<path>][; Secure]
@@ -213,7 +212,7 @@ std::string_view create_affinity_cookie(BlockAllocator &balloc,
}
bool require_cookie_secure_attribute(SessionAffinityCookieSecure secure,
const std::string_view &scheme) {
std::string_view scheme) {
switch (secure) {
case SessionAffinityCookieSecure::AUTO:
return scheme == "https"sv;
@@ -276,7 +275,7 @@ create_altsvc_header_value(BlockAllocator &balloc,
return as_string_view(std::ranges::begin(iov), p);
}
bool check_http_scheme(const std::string_view &scheme, bool encrypted) {
bool check_http_scheme(std::string_view scheme, bool encrypted) {
return encrypted ? scheme == "https"sv : scheme == "http"sv;
}

View File

@@ -72,13 +72,13 @@ OutputIt create_via_header_value(OutputIt dst, int major, int minor) {
// |params| is bitwise-OR of zero or more of shrpx_forwarded_param
// defined in shrpx_config.h.
std::string_view create_forwarded(BlockAllocator &balloc, uint32_t params,
const std::string_view &node_by,
const std::string_view &node_for,
const std::string_view &host,
const std::string_view &proto);
std::string_view node_by,
std::string_view node_for,
std::string_view host,
std::string_view proto);
// Adds ANSI color codes to HTTP headers |hdrs|.
std::string colorize_headers(const std::string_view &hdrs);
std::string colorize_headers(std::string_view hdrs);
nghttp2_ssize select_padding_callback(nghttp2_session *session,
const nghttp2_frame *frame,
@@ -88,15 +88,14 @@ nghttp2_ssize select_padding_callback(nghttp2_session *session,
// not empty, "; <path>" is added. If |secure| is true, "; Secure" is
// added.
std::string_view create_affinity_cookie(BlockAllocator &balloc,
const std::string_view &name,
std::string_view name,
uint32_t affinity_cookie,
const std::string_view &path,
bool secure);
std::string_view path, bool secure);
// Returns true if |secure| indicates that Secure attribute should be
// set.
bool require_cookie_secure_attribute(SessionAffinityCookieSecure secure,
const std::string_view &scheme);
std::string_view scheme);
// Returns RFC 7838 alt-svc header field value.
std::string_view create_altsvc_header_value(BlockAllocator &balloc,
@@ -106,7 +105,7 @@ std::string_view create_altsvc_header_value(BlockAllocator &balloc,
// - scheme is https and encrypted is true
// - scheme is http and encrypted is false
// Otherwise returns false.
bool check_http_scheme(const std::string_view &scheme, bool encrypted);
bool check_http_scheme(std::string_view scheme, bool encrypted);
} // namespace http

View File

@@ -147,19 +147,19 @@ int Http2DownstreamConnection::submit_rst_stream(Downstream *downstream,
switch (downstream->get_response_state()) {
case DownstreamState::MSG_RESET:
case DownstreamState::MSG_BAD_HEADER:
case DownstreamState::MSG_COMPLETE:
break;
return rv;
default:
if (LOG_ENABLED(INFO)) {
DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream
<< ", stream_id="
<< downstream->get_downstream_stream_id()
<< ", error_code=" << error_code;
}
rv = http2session_->submit_rst_stream(
static_cast<int32_t>(downstream->get_downstream_stream_id()),
error_code);
break;
}
if (LOG_ENABLED(INFO)) {
DCLOG(INFO, this) << "Submit RST_STREAM for DOWNSTREAM:" << downstream
<< ", stream_id="
<< downstream->get_downstream_stream_id()
<< ", error_code=" << error_code;
}
rv = http2session_->submit_rst_stream(
static_cast<int32_t>(downstream->get_downstream_stream_id()), error_code);
}
return rv;
}
@@ -506,19 +506,19 @@ int Http2DownstreamConnection::push_request_headers() {
return 0;
}
int Http2DownstreamConnection::push_upload_data_chunk(const uint8_t *data,
size_t datalen) {
int Http2DownstreamConnection::push_upload_data_chunk(
std::span<const uint8_t> data) {
if (!downstream_->get_request_header_sent()) {
auto output = downstream_->get_blocked_request_buf();
auto &req = downstream_->request();
output->append(data, datalen);
req.unconsumed_body_length += datalen;
output->append(data);
req.unconsumed_body_length += data.size();
return 0;
}
int rv;
auto output = downstream_->get_request_buf();
output->append(data, datalen);
output->append(data);
if (downstream_->get_downstream_stream_id() != -1) {
rv = http2session_->resume_data(this);
if (rv != 0) {

View File

@@ -49,31 +49,31 @@ class DownstreamConnectionPool;
class Http2DownstreamConnection : public DownstreamConnection {
public:
Http2DownstreamConnection(Http2Session *http2session);
virtual ~Http2DownstreamConnection();
virtual int attach_downstream(Downstream *downstream);
virtual void detach_downstream(Downstream *downstream);
~Http2DownstreamConnection() override;
int attach_downstream(Downstream *downstream) override;
void detach_downstream(Downstream *downstream) override;
virtual int push_request_headers();
virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen);
virtual int end_upload_data();
int push_request_headers() override;
int push_upload_data_chunk(std::span<const uint8_t> data) override;
int end_upload_data() override;
virtual void pause_read(IOCtrlReason reason) {}
virtual int resume_read(IOCtrlReason reason, size_t consumed);
virtual void force_resume_read() {}
void pause_read(IOCtrlReason reason) override {}
int resume_read(IOCtrlReason reason, size_t consumed) override;
void force_resume_read() override {}
virtual int on_read();
virtual int on_write();
virtual int on_timeout();
int on_read() override;
int on_write() override;
int on_timeout() override;
virtual void on_upstream_change(Upstream *upstream) {}
void on_upstream_change(Upstream *upstream) override {}
// This object is not poolable because we don't have facility to
// migrate to another Http2Session object.
virtual bool poolable() const { return false; }
bool poolable() const override { return false; }
virtual const std::shared_ptr<DownstreamAddrGroup> &
get_downstream_addr_group() const;
virtual DownstreamAddr *get_addr() const;
const std::shared_ptr<DownstreamAddrGroup> &
get_downstream_addr_group() const override;
DownstreamAddr *get_addr() const override;
int send();

Some files were not shown because too many files have changed in this diff Show More