@@ -4,16 +4,21 @@ import (
44 "bytes"
55 "crypto/sha256"
66 "encoding/binary"
7+ "errors"
78 "fmt"
89 "io"
10+ "net/http"
911 "net/url"
1012 "strconv"
1113 "strings"
1214
1315 "github.com/WICG/webpackage/go/bundle/version"
1416 "github.com/WICG/webpackage/go/signedexchange/cbor"
17+ "github.com/WICG/webpackage/go/signedexchange/structuredheader"
1518)
1619
20+ const maxNumVariantsForSingleURL = 10000
21+
1722func normalizeHeaderValues (values []string ) string {
1823 // RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1
1924 // 4.2 Message Headers
@@ -66,13 +71,15 @@ func (r Response) HeaderSha256() ([]byte, error) {
6671
6772var _ = io .WriterTo (& Bundle {})
6873
69- type requestEntry struct {
74+ type indexEntry struct {
7075 Request
71- Offset uint64 // Offset within the responses section
72- Length uint64
76+ Variants string
77+ VariantKey string
78+ Offset uint64 // Offset within the responses section
79+ Length uint64
7380}
7481
75- func (r requestEntry ) String () string {
82+ func (r indexEntry ) String () string {
7683 return fmt .Sprintf ("{URL: %v, Header: %v, Offset: %d, Length: %d}" , r .URL , r .Header , r .Offset , r .Length )
7784}
7885
@@ -84,15 +91,19 @@ type section interface {
8491
8592// staging area for writing index section
8693type indexSection struct {
87- es []requestEntry
94+ es []* indexEntry
8895 bytes []byte
8996}
9097
91- func (is * indexSection ) addRequest (r Request , offset , length int ) error {
92- ent := requestEntry {
93- Request : r ,
94- Offset : uint64 (offset ),
95- Length : uint64 (length ),
98+ func (is * indexSection ) addExchange (e * Exchange , offset , length int ) error {
99+ variants := normalizeHeaderValues (e .Response .Header [http .CanonicalHeaderKey ("variants" )])
100+ variantKey := normalizeHeaderValues (e .Response .Header [http .CanonicalHeaderKey ("variant-key" )])
101+ ent := & indexEntry {
102+ Request : e .Request ,
103+ Variants : variants ,
104+ VariantKey : variantKey ,
105+ Offset : uint64 (offset ),
106+ Length : uint64 (length ),
96107 }
97108 is .es = append (is .es , ent )
98109 return nil
@@ -111,25 +122,42 @@ func (is *indexSection) Finalize(ver version.Version) error {
111122 // index = {* whatwg-url => [ variants-value, +location-in-responses ] }
112123 // variants-value = bstr
113124 // location-in-responses = (offset: uint, length: uint)
114- mes := [] * cbor. MapEntryEncoder {}
125+ m := make ( map [ string ][] * indexEntry )
115126 for _ , e := range is .es {
116- me := cbor .GenerateMapEntry (func (keyE * cbor.Encoder , valueE * cbor.Encoder ) {
117- if err := keyE .EncodeTextString (e .URL .String ()); err != nil {
118- panic (err )
127+ url := e .URL .String ()
128+ m [url ] = append (m [url ], e )
129+ }
130+
131+ mes := []* cbor.MapEntryEncoder {}
132+ for url , es := range m {
133+ var variantsValue []byte
134+ if len (es ) > 1 {
135+ variantsValue = []byte (es [0 ].Variants )
136+ var err error
137+ es , err = entriesInPossibleKeyOrder (es )
138+ if err != nil {
139+ return fmt .Errorf ("bundle: cannot construct index entry for %s: %v" , url , err )
119140 }
120- // Currently, this encoder does not support variants. So, the
121- // map value is always a three-element array ['', offset, length].
122- if err := valueE .EncodeArrayHeader (3 ); err != nil {
141+ }
142+
143+ me := cbor .GenerateMapEntry (func (keyE * cbor.Encoder , valueE * cbor.Encoder ) {
144+ if err := keyE .EncodeTextString (url ); err != nil {
123145 panic (err )
124146 }
125- if err := valueE .EncodeByteString (nil ); err != nil {
147+
148+ if err := valueE .EncodeArrayHeader (1 + len (es )* 2 ); err != nil {
126149 panic (err )
127150 }
128- if err := valueE .EncodeUint ( e . Offset ); err != nil {
151+ if err := valueE .EncodeByteString ( variantsValue ); err != nil {
129152 panic (err )
130153 }
131- if err := valueE .EncodeUint (e .Length ); err != nil {
132- panic (err )
154+ for _ , e := range es {
155+ if err := valueE .EncodeUint (e .Offset ); err != nil {
156+ panic (err )
157+ }
158+ if err := valueE .EncodeUint (e .Length ); err != nil {
159+ panic (err )
160+ }
133161 }
134162 })
135163 mes = append (mes , me )
@@ -190,6 +218,155 @@ func (is *indexSection) Finalize(ver version.Version) error {
190218 return nil
191219}
192220
221+ // entriesInPossibleKeyOrder reorders es by VariantKey, in the order they should
222+ // appear in the index section; the row-major order of possible keys for
223+ // Variants. All entries in es must have the same Variants value.
224+ //
225+ // For example, if the Variants value is
226+ // "Accept-Language;en;fr, Accept-Encoding:gzip;br", the result would satisfy
227+ // this:
228+ // result[0].VariantKey == "en;gzip"
229+ // result[1].VariantKey == "en;br"
230+ // result[2].VariantKey == "fr;gzip"
231+ // result[3].VariantKey == "fr;br"
232+ //
233+ // Note that VariantKey can have multiple keys (e.g. "en;gzip, fr;gzip"). Such
234+ // entrys will appear multiple times in the result. e.g.:
235+ // result[0].VariantKey == "en;gzip, fr;gzip"
236+ // result[1].VariantKey == "en;br"
237+ // result[2] == result[0]
238+ // result[3].VariantKey == "fr;br"
239+ //
240+ // If entries in es do not cover all combination of possible keys or two entries
241+ // have overwrapping possible keys, this returns an error.
242+ func entriesInPossibleKeyOrder (es []* indexEntry ) ([]* indexEntry , error ) {
243+ if es [0 ].Variants == "" {
244+ return nil , errors .New ("no Variants header" )
245+ }
246+ variants , err := parseVariants (es [0 ].Variants )
247+ if err != nil {
248+ return nil , fmt .Errorf ("cannot parse Variants header value %q: %v" , es [0 ].Variants , err )
249+ }
250+ numPossibleKeys , err := variants .numberOfPossibleKeys ()
251+ if err != nil {
252+ return nil , fmt .Errorf ("invalid Variants header value %q: %v" , es [0 ].Variants , err )
253+ }
254+
255+ result := make ([]* indexEntry , numPossibleKeys )
256+ for _ , e := range es {
257+ // TODO: Compare Variants values as lists
258+ // (e.g. "Accept;foo;bar" == "Accept; foo; bar").
259+ if e .Variants != es [0 ].Variants {
260+ return nil , fmt .Errorf ("inconsistent Variants value. %q != %q" , e .Variants , es [0 ].Variants )
261+ }
262+ vks , err := parseListOfStringLists (e .VariantKey )
263+ if err != nil {
264+ return nil , fmt .Errorf ("cannot parse Variant-Key header %q: %v" , e .VariantKey , err )
265+ }
266+ for _ , vk := range vks {
267+ i := variants .indexInPossibleKeys (vk )
268+ if i == - 1 {
269+ return nil , fmt .Errorf ("Variant-Key %q is not covered by variants %q" , e .VariantKey , e .Variants )
270+ }
271+ if result [i ] != nil {
272+ return nil , fmt .Errorf ("duplicated entries with Variant-Key %q" , vk )
273+ }
274+ result [i ] = e
275+ }
276+ }
277+ for i , e := range result {
278+ if e == nil {
279+ return nil , fmt .Errorf ("no entry for Variant-Key %v" , variants .possibleKeyAt (i ))
280+ }
281+ }
282+ return result , nil
283+ }
284+
285+ func parseListOfStringLists (s string ) ([][]string , error ) {
286+ ll , err := structuredheader .ParseListOfLists (s )
287+ if err != nil {
288+ return nil , err
289+ }
290+ // Convert [][]structuredheader.Item to [][]string.
291+ var result [][]string
292+ for _ , l := range ll {
293+ var sl []string
294+ for _ , item := range l {
295+ switch v := item .(type ) {
296+ case string :
297+ sl = append (sl , v )
298+ case structuredheader.Token :
299+ sl = append (sl , string (v ))
300+ default :
301+ return nil , fmt .Errorf ("unexpected value of type %T" , v )
302+ }
303+ }
304+ result = append (result , sl )
305+ }
306+ return result , nil
307+ }
308+
309+ // Variants represents a Variants: header value.
310+ type Variants [][]string
311+
312+ func parseVariants (s string ) (Variants , error ) {
313+ vs , err := parseListOfStringLists (s )
314+ return Variants (vs ), err
315+ }
316+
317+ func (v Variants ) numberOfPossibleKeys () (int , error ) {
318+ n := 1
319+ for _ , vals := range v {
320+ // vals is [header-name, possible-value1, possible-value2, ...]
321+ if len (vals ) <= 1 {
322+ return 0 , errors .New ("no possible key" )
323+ }
324+ n *= len (vals ) - 1
325+ if n > maxNumVariantsForSingleURL {
326+ return 0 , errors .New ("too many possible keys" )
327+ }
328+ }
329+ return n , nil
330+ }
331+
332+ // indexInPossibleKeys returns the index of variantKey within the all possible
333+ // key combinations for v. If variantKey is not a possible key for v, this
334+ // returns -1.
335+ func (v Variants ) indexInPossibleKeys (variantKey []string ) int {
336+ if len (v ) != len (variantKey ) {
337+ return - 1
338+ }
339+
340+ index := 0
341+ OuterLoop:
342+ for i , vals := range v {
343+ vals = vals [1 :] // Drop header-name
344+ for indexInAxis , val := range vals {
345+ if val == variantKey [i ] {
346+ index = index * len (vals ) + indexInAxis
347+ continue OuterLoop
348+ }
349+ }
350+ return - 1
351+ }
352+ return index
353+ }
354+
355+ // possibleKeyAt returns a variant key at given index within the all possible
356+ // key combinations for v, or nil if index is out of range.
357+ func (v Variants ) possibleKeyAt (index int ) []string {
358+ keys := make ([]string , len (v ))
359+ for i := len (v ) - 1 ; i >= 0 ; i -- {
360+ vals := v [i ][1 :] // Drop header-name
361+ keys [i ] = vals [index % len (vals )]
362+ index /= len (vals )
363+ }
364+ if index != 0 {
365+ return nil // index out of range
366+ }
367+ return keys
368+ }
369+
193370func (is * indexSection ) Name () string {
194371 return "index"
195372}
@@ -318,7 +495,7 @@ func addExchange(is *indexSection, rs *responsesSection, e *Exchange) error {
318495 return err
319496 }
320497
321- if err := is .addRequest ( e . Request , offset , length ); err != nil {
498+ if err := is .addExchange ( e , offset , length ); err != nil {
322499 return err
323500 }
324501 return nil
0 commit comments