diff options
| author | Shulhan <ms@kilabit.info> | 2023-04-08 17:36:22 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2023-04-08 17:36:22 +0700 |
| commit | 700b4bb063f0a75f19d4663b9ae705db5bda856a (patch) | |
| tree | b467ab186800599c02c5281f81b720a0239e6922 /lib | |
| parent | 27662a36cd679529e98023206ff7a1826b78ac92 (diff) | |
| download | pakakeh.go-700b4bb063f0a75f19d4663b9ae705db5bda856a.tar.xz | |
lib/io: refactoring ParseContentType using lib/bytes#Parser
The libio.Reader will be deprecated and replaced with libbytes.Parser
in the future.
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/email/contenttype.go | 61 | ||||
| -rw-r--r-- | lib/email/contenttype_test.go | 52 |
2 files changed, 63 insertions, 50 deletions
diff --git a/lib/email/contenttype.go b/lib/email/contenttype.go index 951a993f..1fbe5a78 100644 --- a/lib/email/contenttype.go +++ b/lib/email/contenttype.go @@ -6,11 +6,10 @@ package email import ( "bytes" - "errors" "fmt" "strings" - libio "github.com/shuLhan/share/lib/io" + libbytes "github.com/shuLhan/share/lib/bytes" ) var ( @@ -41,63 +40,71 @@ func ParseContentType(raw []byte) (ct *ContentType, err error) { return ct, nil } - r := &libio.Reader{} - r.Init(raw) - var c byte + var ( + logp = `ParseContentType` + parser = libbytes.NewParser(raw, []byte{'/', ';'}) + c byte + ) - ct.Top, _, c = r.ReadUntil([]byte{'/'}, nil) - if c == 0 { - return nil, errors.New("ParseContentType: missing subtype") + ct.Top, c = parser.Read() + if c != '/' { + return nil, fmt.Errorf(`%s: missing subtype`, logp) } if !isValidToken(ct.Top, false) { - return nil, fmt.Errorf("ParseContentType: invalid type: '%s'", ct.Top) + return nil, fmt.Errorf(`%s: invalid type '%s'`, logp, ct.Top) } - ct.Sub, _, c = r.ReadUntil([]byte{';'}, nil) + ct.Sub, c = parser.Read() if !isValidToken(ct.Sub, false) { - return nil, fmt.Errorf("ParseContentType: invalid subtype: '%s'", ct.Sub) + return nil, fmt.Errorf(`%s: invalid subtype '%s'`, logp, ct.Sub) } if c == 0 { return ct, nil } - c = r.SkipSpaces() - ksep := []byte{'='} - qsep := []byte{'"'} - vsep := []byte{' '} + c = parser.SkipSpaces() + parser.SetDelimiters([]byte{'=', '"'}) for c != 0 { param := Param{} - param.Key, _, c = r.ReadUntil(ksep, nil) + param.Key, c = parser.ReadNoSpace() if c == 0 { // Ignore key without value break } + if c != '=' { + return nil, fmt.Errorf(`%s: expecting '=', got '%c'`, logp, c) + } if !isValidToken(param.Key, false) { - err = fmt.Errorf("ParseContentType: invalid parameter key: '%s'", param.Key) - return nil, err + return nil, fmt.Errorf(`%s: invalid parameter key '%s'`, logp, param.Key) } - c = r.Current() + param.Value, c = parser.ReadNoSpace() if c == '"' { - r.SkipN(1) - param.Value, _, c = r.ReadUntil(qsep, nil) + if len(param.Value) != 0 { + return nil, fmt.Errorf(`%s: invalid parameter value '%s'`, logp, param.Value) + } + + // The param value may contain '=', remove it + // temporarily. + parser.RemoveDelimiters([]byte{'='}) + + param.Value, c = parser.ReadNoSpace() if c != '"' { - return nil, errors.New("ParseContentType: missing closing quote") + return nil, fmt.Errorf(`%s: missing closing quote`, logp) } param.Quoted = true - } else { - param.Value, _, _ = r.ReadUntil(vsep, nil) + + parser.AddDelimiters([]byte{'='}) } if !isValidToken(param.Value, param.Quoted) { - err = fmt.Errorf("ParseContentType: invalid parameter value: '%s'", param.Value) - return nil, err + return nil, fmt.Errorf(`%s: invalid parameter value '%s'`, logp, param.Value) } param.Key = bytes.ToLower(param.Key) ct.Params = append(ct.Params, param) - c = r.SkipSpaces() + c = parser.SkipSpaces() } return ct, nil diff --git a/lib/email/contenttype_test.go b/lib/email/contenttype_test.go index 2dd36a58..1b786631 100644 --- a/lib/email/contenttype_test.go +++ b/lib/email/contenttype_test.go @@ -12,47 +12,53 @@ import ( func TestParseContentType(t *testing.T) { cases := []struct { + in string expErr string exp string - in []byte }{{ - exp: "text/plain; charset=us-ascii", + exp: `text/plain; charset=us-ascii`, }, { - in: []byte("text/;"), - expErr: "ParseContentType: invalid subtype: ''", + in: `text/;`, + expErr: `ParseContentType: invalid subtype ''`, }, { - in: []byte("text/plain"), - exp: "text/plain;", + in: `text/plain`, + exp: `text/plain;`, }, { - in: []byte("text ;"), - expErr: "ParseContentType: missing subtype", + in: `text ;`, + expErr: `ParseContentType: missing subtype`, }, { - in: []byte("text /plain;"), - expErr: "ParseContentType: invalid type: 'text '", + in: `text /plain;`, + expErr: `ParseContentType: invalid type 'text '`, }, { - in: []byte("text/ plain;"), - expErr: "ParseContentType: invalid subtype: ' plain'", + in: `text/ plain;`, + expErr: `ParseContentType: invalid subtype ' plain'`, }, { - in: []byte("text/plain; key"), - exp: "text/plain;", + in: `text/plain; key`, + exp: `text/plain;`, }, { - in: []byte("text/plain; ke(y)=value"), - expErr: "ParseContentType: invalid parameter key: 'ke(y)'", + in: `text/plain; ke(y)=value`, + expErr: `ParseContentType: invalid parameter key 'ke(y)'`, }, { - in: []byte("text/plain; key=value?"), - expErr: "ParseContentType: invalid parameter value: 'value?'", + in: `text/plain; key=value?`, + expErr: `ParseContentType: invalid parameter value 'value?'`, }, { - in: []byte(`text/plain; key="value?`), - expErr: "ParseContentType: missing closing quote", + in: `text/plain; key"value"`, + expErr: `ParseContentType: expecting '=', got '"'`, }, { - in: []byte(`text/plain; key="value ?"`), + in: `text/plain; key=val "value"`, + expErr: `ParseContentType: invalid parameter value 'val'`, + }, { + in: `text/plain; key="value?`, + expErr: `ParseContentType: missing closing quote`, + }, { + in: `text/plain; key="value ?"`, exp: `text/plain; key="value ?"`, }} for _, c := range cases { - t.Logf("%s", c.in) + t.Log(c.in) - got, err := ParseContentType(c.in) + got, err := ParseContentType([]byte(c.in)) if err != nil { test.Assert(t, "error", c.expErr, err.Error()) continue |
