Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/sirupsen/logrus v1.9.2
github.com/stretchr/testify v1.8.3
github.com/urfave/cli/v2 v2.24.4
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a
go.uber.org/goleak v1.2.1
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
golang.org/x/net v0.10.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ github.com/urfave/cli/v2 v2.24.4/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6f
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a h1:DxppxFKRqJ8WD6oJ3+ZXKDY0iMONQDl5UTg2aTyHh8k=
gitlab.com/c0b/go-ordered-json v0.0.0-20201030195603-febf46534d5a/go.mod h1:NREvu3a57BaK0R1+ztrEzHWiZAihohNLQ6trPxlIqZI=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
Expand Down
76 changes: 69 additions & 7 deletions header_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,50 @@ package proton
import (
"encoding/json"
"errors"
"gitlab.com/c0b/go-ordered-json"
)

var ErrBadHeader = errors.New("bad header")

type Headers map[string][]string
type Headers struct {
Values map[string][]string
Order []string
}

func (h *Headers) UnmarshalJSON(b []byte) error {
type rawHeaders map[string]any

raw := make(rawHeaders)

if err := json.Unmarshal(b, &raw); err != nil {
// Need to use a different type to deserialize, because there still is no official way for json to decode an object
// with the fields in order https://github.com/golang/go/issues/27179.
orderedMap := ordered.NewOrderedMap()
if err := orderedMap.UnmarshalJSON(b); err != nil {
return err
}

header := make(Headers)
header := Headers{
Values: make(map[string][]string, len(raw)),
Order: make([]string, 0, len(raw)),
}

iter := orderedMap.EntriesIter()

for key, val := range raw {
switch val := val.(type) {
for {
entry, ok := iter()
if !ok {
break
}

switch val := entry.Value.(type) {
case string:
header[key] = []string{val}
header.Values[entry.Key] = []string{val}

case []any:
for _, val := range val {
switch val := val.(type) {
case string:
header[key] = append(header[key], val)
header.Values[entry.Key] = append(header.Values[entry.Key], val)

default:
return ErrBadHeader
Expand All @@ -39,9 +56,54 @@ func (h *Headers) UnmarshalJSON(b []byte) error {
default:
return ErrBadHeader
}

header.Order = append(header.Order, entry.Key)
}

*h = header

return nil
}

func (h Headers) MarshalJSON() ([]byte, error) {
// Manually Serialize to preserve oder
if len(h.Values) == 0 {
return []byte{'{', '}'}, nil
}

out := make([]byte, 0, 64)

out = append(out, '{')

for _, k := range h.Order {
v := h.Values[k]

if len(v) == 0 {
continue
}

key, err := json.Marshal(k)
if err != nil {
return nil, err
}

var val []byte
if len(v) == 1 {
val, err = json.Marshal(v[0])
} else {
val, err = json.Marshal(v)
}
if err != nil {
return nil, err
}

out = append(out, key...)
out = append(out, ':')
out = append(out, val...)
out = append(out, ',')
}

out[len(out)-1] = '}'

return out, nil
}
24 changes: 24 additions & 0 deletions header_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package proton

import (
"encoding/json"
"github.com/stretchr/testify/require"
"testing"
)

func TestHeaders_MarshalInOrder(t *testing.T) {
jsonBytes := []byte(`{"zz":"v1","foo":["a","b"],"bar":"30"}`)

var h Headers

err := json.Unmarshal(jsonBytes, &h)
require.NoError(t, err)

expectedKeyOrder := []string{"zz", "foo", "bar"}

require.Equal(t, expectedKeyOrder, h.Order)

serializedJson, err := json.Marshal(h)
require.NoError(t, err)
require.Equal(t, jsonBytes, serializedJson)
}
4 changes: 2 additions & 2 deletions message_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,8 @@ func getTextPartHeader(body []byte, mimeType rfc822.MIMEType) message.Header {
func getAttachmentPartHeader(att Attachment) message.Header {
var header message.Header

for key, val := range att.Headers {
for _, val := range val {
for _, key := range att.Headers.Order {
for _, val := range att.Headers.Values[key] {
header.Add(key, val)
}
}
Expand Down
7 changes: 5 additions & 2 deletions server/backend/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,13 @@ func (msg *message) getParsedHeaders() proton.Headers {
panic(err)
}

parsed := make(proton.Headers)
parsed := proton.Headers{
Values: make(map[string][]string),
}

header.Entries(func(key, value string) {
parsed[key] = append(parsed[key], value)
parsed.Order = append(parsed.Order, key)
parsed.Values[key] = append(parsed.Values[key], value)
})

return parsed
Expand Down