[tor-commits] [goptlib/master] encodeSmethodArgs.

dcf at torproject.org dcf at torproject.org
Mon Dec 9 02:49:51 UTC 2013


commit 9a9f9cb455d17bf82d02292953b945dcbb4a5029
Author: David Fifield <david at bamsoftware.com>
Date:   Sun Dec 8 02:53:38 2013 -0800

    encodeSmethodArgs.
---
 args.go      |   44 ++++++++++++++++++++++++++++++++++++++++++++
 args_test.go |   35 +++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+)

diff --git a/args.go b/args.go
index a0a3bd5..9eacee5 100644
--- a/args.go
+++ b/args.go
@@ -4,6 +4,8 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
+	"sort"
+	"strings"
 )
 
 // Key–value mappings for the representation of client and server options.
@@ -173,3 +175,45 @@ func parseServerTransportOptions(s string) (opts map[string]Args, err error) {
 	}
 	return opts, nil
 }
+
+// Escape backslashes and all the bytes that are in set.
+func backslashEscape(s string, set []byte) string {
+	var buf bytes.Buffer
+	for _, b := range []byte(s) {
+		if b == '\\' || bytes.IndexByte(set, b) != -1 {
+			buf.WriteByte('\\')
+		}
+		buf.WriteByte(b)
+	}
+	return buf.String()
+}
+
+// Encode a name–value mapping so that it is suitable to go in the ARGS option
+// of an SMETHOD line. The output is sorted by key. The "ARGS:" prefix is not
+// added.
+//
+// "Equal signs and commas [and backslashes] must be escaped with a backslash."
+func encodeSmethodArgs(args Args) string {
+	if args == nil {
+		return ""
+	}
+
+	keys := make([]string, 0, len(args))
+	for key, _ := range args {
+		keys = append(keys, key)
+	}
+	sort.Strings(keys)
+
+	escape := func(s string) string {
+		return backslashEscape(s, []byte{'=', ','})
+	}
+
+	var pairs []string
+	for _, key := range keys {
+		for _, value := range args[key] {
+			pairs = append(pairs, escape(key)+"="+escape(value))
+		}
+	}
+
+	return strings.Join(pairs, ",")
+}
diff --git a/args_test.go b/args_test.go
index ece7a50..d62968c 100644
--- a/args_test.go
+++ b/args_test.go
@@ -299,3 +299,38 @@ func TestParseServerTransportOptions(t *testing.T) {
 		}
 	}
 }
+
+func TestEncodeSmethodArgs(t *testing.T) {
+	tests := [...]struct {
+		args     Args
+		expected string
+	}{
+		{
+			Args{},
+			"",
+		},
+		{
+			Args{"j": []string{"v1", "v2", "v3"}, "k": []string{"v1", "v2", "v3"}},
+			"j=v1,j=v2,j=v3,k=v1,k=v2,k=v3",
+		},
+		{
+			Args{"=,\\": []string{"=", ",", "\\"}},
+			"\\=\\,\\\\=\\=,\\=\\,\\\\=\\,,\\=\\,\\\\=\\\\",
+		},
+		{
+			Args{"secret": []string{"yes"}},
+			"secret=yes",
+		},
+		{
+			Args{"secret": []string{"nou"}, "cache": []string{"/tmp/cache"}},
+			"cache=/tmp/cache,secret=nou",
+		},
+	}
+
+	for _, test := range tests {
+		encoded := encodeSmethodArgs(test.args)
+		if encoded != test.expected {
+			t.Errorf("%q → %q (expected %q)", test.args, encoded, test.expected)
+		}
+	}
+}





More information about the tor-commits mailing list