-
Notifications
You must be signed in to change notification settings - Fork 0
/
encoder.go
124 lines (107 loc) · 3.6 KB
/
encoder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package quack
import (
"errors"
"sync/atomic"
"github.com/renthraysk/quack/ascii"
"github.com/renthraysk/quack/internal/field"
"github.com/renthraysk/quack/varint"
)
type Encoder struct {
// Dynamic table
dt field.DT
// fieldEncoder is the encoder state required to encode headers.
// It is immutable once created by the dynamic table.
// If nil then will only encode using the static table. Will be updated
// when receive the increment decoder instruction from peer.
fieldEncoder atomic.Pointer[field.Encoder]
}
func NewEncoder() *Encoder {
return &Encoder{}
}
func allEqual[T comparable](s []T, one T) bool {
for _, v := range s {
if v != one {
return false
}
}
return true
}
// AppendRequest https://www.rfc-editor.org/rfc/rfc9114.html#name-request-pseudo-header-field
func (e *Encoder) AppendRequest(p []byte, method, scheme, authority, path string, header map[string][]string) ([]byte, error) {
if len(scheme) <= len("https") {
switch ascii.Lower(scheme) {
case "https", "http":
// Clients that generate HTTP/3 requests directly SHOULD use the
// :authority pseudo-header field instead of the Host header field.
if authority == "" {
return p, errors.New("empty :authority")
} else if hosts, ok := header["Host"]; ok && !allEqual(hosts, authority) {
return p, errors.New(":authority and Host header are inconsistent")
}
// This pseudo-header field MUST NOT be empty for "http" or "https" URIs;
// "http" or "https" URIs that do not contain a path component MUST
// include a value of / (ASCII 0x2f).
if path == "" {
path = "/"
// An OPTIONS request that does not include a path component includes
// the value * (ASCII 0x2a) for the :path pseudo-header field
if method == "OPTIONS" {
path = "*"
}
}
}
}
fe := e.fieldEncoder.Load()
p = fe.AppendRequest(p, method, scheme, authority, path, header)
return p, nil
}
// AppendConnect https://www.rfc-editor.org/rfc/rfc9114.html#name-the-connect-method
func (e *Encoder) AppendConnect(p []byte, authority string, header map[string][]string) ([]byte, error) {
fe := e.fieldEncoder.Load()
p = fe.AppendConnect(p, authority, header)
return p, nil
}
// AppendResponse https://www.rfc-editor.org/rfc/rfc9114.html#name-response-pseudo-header-fiel
func (e *Encoder) AppendResponse(p []byte, statusCode int, header map[string][]string) ([]byte, error) {
fe := e.fieldEncoder.Load()
p = fe.AppendResponse(p, statusCode, header)
return p, nil
}
// readDecoderInstructions https://www.rfc-editor.org/rfc/rfc9204.html#name-decoder-instructions
func (e *Encoder) readDecoderInstructions(p []byte) error {
var streamID, increment uint64
var err error
for len(p) > 0 {
switch p[0] >> 6 {
case 0b00:
// https://www.rfc-editor.org/rfc/rfc9204.html#name-insert-count-increment
increment, p, err = varint.Read(p, 0b0011_1111)
if err != nil {
return err
}
if err := e.increment(increment); err != nil {
return err
}
case 0b01:
// https://www.rfc-editor.org/rfc/rfc9204.html#name-stream-cancellation
streamID, p, err = varint.Read(p, 0b0011_1111)
if err != nil {
return err
}
e.streamInstruction(streamID, (*stream).streamCancellation, true)
case 0b10, 0b11:
// https://www.rfc-editor.org/rfc/rfc9204.html#name-section-acknowledgment
streamID, p, err = varint.Read(p, 0b0111_1111)
if err != nil {
return err
}
e.streamInstruction(streamID, (*stream).sectionAcknowledgement, false)
}
}
return nil
}
func (e *Encoder) streamInstruction(streamID uint64, f func(s *stream), remove bool) {
}
func (e *Encoder) increment(increment uint64) error {
return nil
}