apt/message.go (85 lines of code) (raw):
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package apt
import (
"fmt"
"regexp"
"sort"
"strings"
)
var newlineRegexp = regexp.MustCompile(`\r?\n`)
// Message represents a single RFC822 Apt message.
type Message struct {
code int
description string
fields map[string][]string
}
// Get returns the first AptMessage Field for `key`, or "".
func (m *Message) Get(key string) string {
if vals, ok := m.fields[key]; ok {
if len(vals) > 0 {
return vals[0]
}
}
return ""
}
func (m *Message) String() string {
// Map iteration is unordered. For testing convenience, write alphabetical output.
sortedKeys := make([]string, len(m.fields))
i := 0
for key := range m.fields {
sortedKeys[i] = key
i++
}
sort.Strings(sortedKeys)
message := []string{fmt.Sprintf("%d %s", m.code, m.description)}
for _, key := range sortedKeys {
for _, val := range m.fields[key] {
// Messages are allowed to contain newlines, but they must not contain double newlines,
// nor end in a newline (since this would result in a premature double newline). We'll
// encode all newlines here as a space instead for simplicity.
val = newlineRegexp.ReplaceAllString(val, " ")
message = append(message, fmt.Sprintf("%s: %s", key, val))
}
}
message = append(message, "")
message = append(message, "") // End with a newline.
return strings.Join(message, "\n")
}
func new100Message() Message {
fields := make(map[string][]string)
fields["Send-Config"] = []string{"true"}
fields["Version"] = []string{"1.0"}
return Message{code: 100, description: "Capabilities", fields: fields}
}
func new101Message(msg string) Message {
fields := make(map[string][]string)
fields["Message"] = []string{msg}
return Message{code: 101, description: "Log", fields: fields}
}
func new200Message(uri, size, lastModified string) Message {
fields := make(map[string][]string)
fields["URI"] = []string{uri}
fields["Size"] = []string{size}
if lastModified != "" {
fields["Last-Modified"] = []string{lastModified}
}
fields["Resume-Point"] = []string{"0"}
return Message{code: 200, description: "URI Start", fields: fields}
}
func new201Message(uri, size, lastModified, md5Hash, filename string, imsHit bool) Message {
fields := make(map[string][]string)
fields["URI"] = []string{uri}
fields["Last-Modified"] = []string{lastModified}
fields["Filename"] = []string{filename}
if imsHit {
fields["IMS-Hit"] = []string{"true"}
} else {
fields["Size"] = []string{size}
fields["MD5-Hash"] = []string{md5Hash}
}
return Message{code: 201, description: "URI Done", fields: fields}
}
func new400Message(uri, msg string) Message {
fields := make(map[string][]string)
fields["URI"] = []string{uri}
fields["Message"] = []string{msg}
return Message{code: 400, description: "URI Failure", fields: fields}
}
func new401Message(msg string) Message {
fields := make(map[string][]string)
fields["Message"] = []string{msg}
return Message{code: 401, description: "General Failure", fields: fields}
}