mirror of
https://github.com/go-micro/go-micro.git
synced 2025-05-25 21:53:14 +02:00
unused packages
This commit is contained in:
parent
72df27b7d1
commit
dd0145fa18
@ -1,18 +0,0 @@
|
||||
package ctx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"go-micro.dev/v5/metadata"
|
||||
)
|
||||
|
||||
func FromRequest(r *http.Request) context.Context {
|
||||
ctx := context.Background()
|
||||
md := make(metadata.Metadata)
|
||||
for k, v := range r.Header {
|
||||
md[k] = strings.Join(v, ",")
|
||||
}
|
||||
return metadata.NewContext(ctx, md)
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package ctx
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"go-micro.dev/v5/metadata"
|
||||
)
|
||||
|
||||
func TestRequestToContext(t *testing.T) {
|
||||
testData := []struct {
|
||||
request *http.Request
|
||||
expect metadata.Metadata
|
||||
}{
|
||||
{
|
||||
&http.Request{
|
||||
Header: http.Header{
|
||||
"Foo1": []string{"bar"},
|
||||
"Foo2": []string{"bar", "baz"},
|
||||
},
|
||||
},
|
||||
metadata.Metadata{
|
||||
"Foo1": "bar",
|
||||
"Foo2": "bar,baz",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, d := range testData {
|
||||
ctx := FromRequest(d.request)
|
||||
md, ok := metadata.FromContext(ctx)
|
||||
if !ok {
|
||||
t.Fatalf("Expected metadata for request %+v", d.request)
|
||||
}
|
||||
for k, v := range d.expect {
|
||||
if val := md[k]; val != v {
|
||||
t.Fatalf("Expected %s for key %s for expected md %+v, got md %+v", v, k, d.expect, md)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
// Package io is for io management
|
||||
package io
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"go-micro.dev/v5/transport"
|
||||
)
|
||||
|
||||
type rwc struct {
|
||||
socket transport.Socket
|
||||
}
|
||||
|
||||
func (r *rwc) Read(p []byte) (n int, err error) {
|
||||
m := new(transport.Message)
|
||||
if err := r.socket.Recv(m); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(p, m.Body)
|
||||
return len(m.Body), nil
|
||||
}
|
||||
|
||||
func (r *rwc) Write(p []byte) (n int, err error) {
|
||||
err = r.socket.Send(&transport.Message{
|
||||
Body: p,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (r *rwc) Close() error {
|
||||
return r.socket.Close()
|
||||
}
|
||||
|
||||
// NewRWC returns a new ReadWriteCloser.
|
||||
func NewRWC(sock transport.Socket) io.ReadWriteCloser {
|
||||
return &rwc{sock}
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
package pki
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"math/big"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CertOptions are passed to cert options.
|
||||
type CertOptions struct {
|
||||
NotBefore time.Time
|
||||
NotAfter time.Time
|
||||
|
||||
SerialNumber *big.Int
|
||||
|
||||
Parent *x509.Certificate
|
||||
Subject pkix.Name
|
||||
DNSNames []string
|
||||
IPAddresses []net.IP
|
||||
Pub ed25519.PublicKey
|
||||
Priv ed25519.PrivateKey
|
||||
IsCA bool
|
||||
}
|
||||
|
||||
// CertOption sets CertOptions.
|
||||
type CertOption func(c *CertOptions)
|
||||
|
||||
// Subject sets the Subject field.
|
||||
func Subject(subject pkix.Name) CertOption {
|
||||
return func(c *CertOptions) {
|
||||
c.Subject = subject
|
||||
}
|
||||
}
|
||||
|
||||
// IsCA states the cert is a CA.
|
||||
func IsCA() CertOption {
|
||||
return func(c *CertOptions) {
|
||||
c.IsCA = true
|
||||
}
|
||||
}
|
||||
|
||||
// DNSNames is a list of hosts to sign in to the certificate.
|
||||
func DNSNames(names ...string) CertOption {
|
||||
return func(c *CertOptions) {
|
||||
c.DNSNames = names
|
||||
}
|
||||
}
|
||||
|
||||
// IPAddresses is a list of IPs to sign in to the certificate.
|
||||
func IPAddresses(ips ...net.IP) CertOption {
|
||||
return func(c *CertOptions) {
|
||||
c.IPAddresses = ips
|
||||
}
|
||||
}
|
||||
|
||||
// KeyPair is the key pair to sign the certificate with.
|
||||
func KeyPair(pub ed25519.PublicKey, priv ed25519.PrivateKey) CertOption {
|
||||
return func(c *CertOptions) {
|
||||
c.Pub = pub
|
||||
c.Priv = priv
|
||||
}
|
||||
}
|
||||
|
||||
// SerialNumber is the Certificate Serial number.
|
||||
func SerialNumber(serial *big.Int) CertOption {
|
||||
return func(c *CertOptions) {
|
||||
c.SerialNumber = serial
|
||||
}
|
||||
}
|
||||
|
||||
// NotBefore is the time the certificate is not valid before.
|
||||
func NotBefore(time time.Time) CertOption {
|
||||
return func(c *CertOptions) {
|
||||
c.NotBefore = time
|
||||
}
|
||||
}
|
||||
|
||||
// NotAfter is the time the certificate is not valid after.
|
||||
func NotAfter(time time.Time) CertOption {
|
||||
return func(c *CertOptions) {
|
||||
c.NotAfter = time
|
||||
}
|
||||
}
|
164
util/pki/pki.go
164
util/pki/pki.go
@ -1,164 +0,0 @@
|
||||
// Package pki provides PKI all the PKI functions necessary to run micro over an untrusted network
|
||||
// including a CA
|
||||
package pki
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// GenerateKey returns an ed25519 key.
|
||||
func GenerateKey() (ed25519.PublicKey, ed25519.PrivateKey, error) {
|
||||
return ed25519.GenerateKey(rand.Reader)
|
||||
}
|
||||
|
||||
// CA generates a self signed CA and returns cert, key in PEM format.
|
||||
func CA(opts ...CertOption) ([]byte, []byte, error) {
|
||||
opts = append(opts, IsCA())
|
||||
options := CertOptions{}
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
template := &x509.Certificate{
|
||||
SignatureAlgorithm: x509.PureEd25519,
|
||||
Subject: options.Subject,
|
||||
DNSNames: options.DNSNames,
|
||||
IPAddresses: options.IPAddresses,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
NotBefore: options.NotBefore,
|
||||
NotAfter: options.NotAfter,
|
||||
SerialNumber: options.SerialNumber,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
if options.IsCA {
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
}
|
||||
x509Cert, err := x509.CreateCertificate(rand.Reader, template, template, options.Pub, options.Priv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cert, key := &bytes.Buffer{}, &bytes.Buffer{}
|
||||
if err := pem.Encode(cert, &pem.Block{Type: "CERTIFICATE", Bytes: x509Cert}); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
x509Key, err := x509.MarshalPKCS8PrivateKey(options.Priv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := pem.Encode(key, &pem.Block{Type: "PRIVATE KEY", Bytes: x509Key}); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return cert.Bytes(), key.Bytes(), nil
|
||||
}
|
||||
|
||||
// CSR generates a certificate request in PEM format.
|
||||
func CSR(opts ...CertOption) ([]byte, error) {
|
||||
options := CertOptions{}
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
csrTemplate := &x509.CertificateRequest{
|
||||
Subject: options.Subject,
|
||||
SignatureAlgorithm: x509.PureEd25519,
|
||||
DNSNames: options.DNSNames,
|
||||
IPAddresses: options.IPAddresses,
|
||||
}
|
||||
out := &bytes.Buffer{}
|
||||
csr, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, options.Priv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := pem.Encode(out, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csr}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
// Sign decodes a CSR and signs it with the CA.
|
||||
func Sign(CACrt, CAKey, CSR []byte, opts ...CertOption) ([]byte, error) {
|
||||
options := CertOptions{}
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
}
|
||||
asn1CACrt, err := decodePEM(CACrt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode CA Crt PEM")
|
||||
}
|
||||
if len(asn1CACrt) != 1 {
|
||||
return nil, errors.Errorf("expected 1 CA Crt, got %d", len(asn1CACrt))
|
||||
}
|
||||
caCrt, err := x509.ParseCertificate(asn1CACrt[0].Bytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ca is not a valid certificate")
|
||||
}
|
||||
asn1CAKey, err := decodePEM(CAKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode CA Key PEM")
|
||||
}
|
||||
if len(asn1CAKey) != 1 {
|
||||
return nil, errors.Errorf("expected 1 CA Key, got %d", len(asn1CACrt))
|
||||
}
|
||||
caKey, err := x509.ParsePKCS8PrivateKey(asn1CAKey[0].Bytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ca key is not a valid private key")
|
||||
}
|
||||
asn1CSR, err := decodePEM(CSR)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode CSR PEM")
|
||||
}
|
||||
if len(asn1CSR) != 1 {
|
||||
return nil, errors.Errorf("expected 1 CSR, got %d", len(asn1CSR))
|
||||
}
|
||||
csr, err := x509.ParseCertificateRequest(asn1CSR[0].Bytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "csr is invalid")
|
||||
}
|
||||
template := &x509.Certificate{
|
||||
SignatureAlgorithm: x509.PureEd25519,
|
||||
Subject: csr.Subject,
|
||||
DNSNames: csr.DNSNames,
|
||||
IPAddresses: csr.IPAddresses,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
NotBefore: options.NotBefore,
|
||||
NotAfter: options.NotAfter,
|
||||
SerialNumber: options.SerialNumber,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
x509Cert, err := x509.CreateCertificate(rand.Reader, template, caCrt, caCrt.PublicKey, caKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Couldn't sign certificate")
|
||||
}
|
||||
out := &bytes.Buffer{}
|
||||
if err := pem.Encode(out, &pem.Block{Type: "CERTIFICATE", Bytes: x509Cert}); err != nil {
|
||||
return nil, errors.Wrap(err, "couldn't encode cert")
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
func decodePEM(PEM []byte) ([]*pem.Block, error) {
|
||||
var blocks []*pem.Block
|
||||
var asn1 *pem.Block
|
||||
var rest []byte
|
||||
for {
|
||||
asn1, rest = pem.Decode(PEM)
|
||||
if asn1 == nil {
|
||||
return nil, errors.New("PEM is not valid")
|
||||
}
|
||||
blocks = append(blocks, asn1)
|
||||
if len(rest) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return blocks, nil
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
package pki
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPrivateKey(t *testing.T) {
|
||||
_, _, err := GenerateKey()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCA(t *testing.T) {
|
||||
pub, priv, err := GenerateKey()
|
||||
assert.NoError(t, err)
|
||||
|
||||
serialNumberMax := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberMax)
|
||||
assert.NoError(t, err, "Couldn't generate serial")
|
||||
|
||||
cert, key, err := CA(
|
||||
KeyPair(pub, priv),
|
||||
Subject(pkix.Name{
|
||||
Organization: []string{"test"},
|
||||
}),
|
||||
DNSNames("localhost"),
|
||||
IPAddresses(net.ParseIP("127.0.0.1")),
|
||||
SerialNumber(serialNumber),
|
||||
NotBefore(time.Now().Add(time.Minute*-1)),
|
||||
NotAfter(time.Now().Add(time.Minute)),
|
||||
)
|
||||
assert.NoError(t, err, "Couldn't sign CA")
|
||||
asn1Key, _ := pem.Decode(key)
|
||||
assert.NotNil(t, asn1Key, "Couldn't decode key")
|
||||
assert.Equal(t, "PRIVATE KEY", asn1Key.Type)
|
||||
decodedKey, err := x509.ParsePKCS8PrivateKey(asn1Key.Bytes)
|
||||
assert.NoError(t, err, "Couldn't decode ASN1 Key")
|
||||
assert.Equal(t, priv, decodedKey.(ed25519.PrivateKey))
|
||||
|
||||
pool := x509.NewCertPool()
|
||||
assert.True(t, pool.AppendCertsFromPEM(cert), "Coudn't parse cert")
|
||||
|
||||
asn1Cert, _ := pem.Decode(cert)
|
||||
assert.NotNil(t, asn1Cert, "Couldn't parse pem cert")
|
||||
x509cert, err := x509.ParseCertificate(asn1Cert.Bytes)
|
||||
assert.NoError(t, err, "Couldn't parse asn1 cert")
|
||||
chains, err := x509cert.Verify(x509.VerifyOptions{
|
||||
Roots: pool,
|
||||
})
|
||||
assert.NoError(t, err, "Cert didn't verify")
|
||||
assert.Len(t, chains, 1, "CA should have 1 cert in chain")
|
||||
}
|
||||
|
||||
func TestCSR(t *testing.T) {
|
||||
pub, priv, err := GenerateKey()
|
||||
assert.NoError(t, err)
|
||||
csr, err := CSR(
|
||||
Subject(
|
||||
pkix.Name{
|
||||
CommonName: "testnode",
|
||||
Organization: []string{"microtest"},
|
||||
OrganizationalUnit: []string{"super-testers"},
|
||||
},
|
||||
),
|
||||
DNSNames("localhost"),
|
||||
IPAddresses(net.ParseIP("127.0.0.1")),
|
||||
KeyPair(pub, priv),
|
||||
)
|
||||
assert.NoError(t, err, "CSR couldn't be encoded")
|
||||
|
||||
asn1csr, _ := pem.Decode(csr)
|
||||
assert.NotNil(t, asn1csr)
|
||||
decodedcsr, err := x509.ParseCertificateRequest(asn1csr.Bytes)
|
||||
assert.NoError(t, err)
|
||||
expected := pkix.Name{
|
||||
CommonName: "testnode",
|
||||
Organization: []string{"microtest"},
|
||||
OrganizationalUnit: []string{"super-testers"},
|
||||
}
|
||||
assert.Equal(t, decodedcsr.Subject.String(), expected.String())
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Jon Calhoun
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -1,55 +0,0 @@
|
||||
# qson
|
||||
|
||||
This is copy from https://github.com/joncalhoun/qson
|
||||
As author says he is not acrivelly maintains the repo and not plan to do that.
|
||||
|
||||
## Usage
|
||||
|
||||
You can either turn a URL query param into a JSON byte array, or unmarshal that directly into a Go object.
|
||||
|
||||
Transforming the URL query param into a JSON byte array:
|
||||
|
||||
```go
|
||||
import "github.com/joncalhoun/qson"
|
||||
|
||||
func main() {
|
||||
b, err := qson.ToJSON("bar%5Bone%5D%5Btwo%5D=2&bar[one][red]=112")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
// Should output: {"bar":{"one":{"red":112,"two":2}}}
|
||||
}
|
||||
```
|
||||
|
||||
Or unmarshalling directly into a Go object using JSON struct tags:
|
||||
|
||||
```go
|
||||
import "github.com/joncalhoun/qson"
|
||||
|
||||
type unmarshalT struct {
|
||||
A string `json:"a"`
|
||||
B unmarshalB `json:"b"`
|
||||
}
|
||||
type unmarshalB struct {
|
||||
C int `json:"c"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
var out unmarshalT
|
||||
query := "a=xyz&b[c]=456"
|
||||
err := Unmarshal(&out, query)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// out should equal
|
||||
// unmarshalT{
|
||||
// A: "xyz",
|
||||
// B: unmarshalB{
|
||||
// C: 456,
|
||||
// },
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
To get a query string like in the two previous examples you can use the `RawQuery` field on the [net/url.URL](https://golang.org/pkg/net/url/#URL) type.
|
@ -1,35 +0,0 @@
|
||||
package qson
|
||||
|
||||
// merge merges a with b if they are either both slices
|
||||
// or map[string]interface{} types. Otherwise it returns b.
|
||||
func merge(a interface{}, b interface{}) interface{} {
|
||||
switch aT := a.(type) {
|
||||
case map[string]interface{}:
|
||||
return mergeMap(aT, b.(map[string]interface{}))
|
||||
case []interface{}:
|
||||
return mergeSlice(aT, b.([]interface{}))
|
||||
default:
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
// mergeMap merges a with b, attempting to merge any nested
|
||||
// values in nested maps but eventually overwriting anything
|
||||
// in a that can't be merged with whatever is in b.
|
||||
func mergeMap(a map[string]interface{}, b map[string]interface{}) map[string]interface{} {
|
||||
for bK, bV := range b {
|
||||
if _, ok := a[bK]; ok {
|
||||
a[bK] = merge(a[bK], bV)
|
||||
} else {
|
||||
a[bK] = bV
|
||||
}
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// mergeSlice merges a with b and returns the result.
|
||||
func mergeSlice(a []interface{}, b []interface{}) []interface{} {
|
||||
a = append(a, b...)
|
||||
return a
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package qson
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMergeSlice(t *testing.T) {
|
||||
a := []interface{}{"a"}
|
||||
b := []interface{}{"b"}
|
||||
actual := mergeSlice(a, b)
|
||||
if len(actual) != 2 {
|
||||
t.Errorf("Expected size to be 2.")
|
||||
}
|
||||
if actual[0] != "a" {
|
||||
t.Errorf("Expected index 0 to have value a. Actual: %s", actual[0])
|
||||
}
|
||||
if actual[1] != "b" {
|
||||
t.Errorf("Expected index 1 to have value b. Actual: %s", actual[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeMap(t *testing.T) {
|
||||
a := map[string]interface{}{
|
||||
"a": "b",
|
||||
}
|
||||
b := map[string]interface{}{
|
||||
"b": "c",
|
||||
}
|
||||
actual := mergeMap(a, b)
|
||||
if len(actual) != 2 {
|
||||
t.Errorf("Expected size to be 2.")
|
||||
}
|
||||
if actual["a"] != "b" {
|
||||
t.Errorf("Expected key \"a\" to have value b. Actual: %s", actual["a"])
|
||||
}
|
||||
if actual["b"] != "c" {
|
||||
t.Errorf("Expected key \"b\" to have value c. Actual: %s", actual["b"])
|
||||
}
|
||||
}
|
@ -1,170 +0,0 @@
|
||||
// Package qson implmenets decoding of URL query params
|
||||
// into JSON and Go values (using JSON struct tags).
|
||||
//
|
||||
// See https://golang.org/pkg/encoding/json/ for more
|
||||
// details on JSON struct tags.
|
||||
package qson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidParam is returned when invalid data is provided to the ToJSON or Unmarshal function.
|
||||
// Specifically, this will be returned when there is no equals sign present in the URL query parameter.
|
||||
ErrInvalidParam = errors.New("qson: invalid url query param provided")
|
||||
|
||||
bracketSplitter *regexp.Regexp
|
||||
)
|
||||
|
||||
func init() {
|
||||
bracketSplitter = regexp.MustCompile(`\[|\]`)
|
||||
}
|
||||
|
||||
// Unmarshal will take a dest along with URL
|
||||
// query params and attempt to first turn the query params
|
||||
// into JSON and then unmarshal those into the dest variable
|
||||
//
|
||||
// BUG(joncalhoun): If a URL query param value is something
|
||||
// like 123 but is expected to be parsed into a string this
|
||||
// will currently result in an error because the JSON
|
||||
// transformation will assume this is intended to be an int.
|
||||
// This should only affect the Unmarshal function and
|
||||
// could likely be fixed, but someone will need to submit a
|
||||
// PR if they want that fixed.
|
||||
func Unmarshal(dst interface{}, query string) error {
|
||||
b, err := ToJSON(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(b, dst)
|
||||
}
|
||||
|
||||
// ToJSON will turn a query string like:
|
||||
//
|
||||
// cat=1&bar%5Bone%5D%5Btwo%5D=2&bar[one][red]=112
|
||||
//
|
||||
// Into a JSON object with all the data merged as nicely as
|
||||
// possible. Eg the example above would output:
|
||||
//
|
||||
// {"bar":{"one":{"two":2,"red":112}}}
|
||||
func ToJSON(query string) ([]byte, error) {
|
||||
var (
|
||||
builder interface{} = make(map[string]interface{})
|
||||
)
|
||||
|
||||
params := strings.Split(query, "&")
|
||||
|
||||
for _, part := range params {
|
||||
tempMap, err := queryToMap(part)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
builder = merge(builder, tempMap)
|
||||
}
|
||||
|
||||
return json.Marshal(builder)
|
||||
}
|
||||
|
||||
// queryToMap turns something like a[b][c]=4 into
|
||||
//
|
||||
// map[string]interface{}{
|
||||
// "a": map[string]interface{}{
|
||||
// "b": map[string]interface{}{
|
||||
// "c": 4,
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
func queryToMap(param string) (map[string]interface{}, error) {
|
||||
rawKey, rawValue, err := splitKeyAndValue(param)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawValue, err = url.QueryUnescape(rawValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawKey, err = url.QueryUnescape(rawKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pieces := bracketSplitter.Split(rawKey, -1)
|
||||
key := pieces[0]
|
||||
|
||||
// If len==1 then rawKey has no [] chars and we can just
|
||||
// decode this as key=value into {key: value}
|
||||
if len(pieces) == 1 {
|
||||
var value interface{}
|
||||
// First we try parsing it as an int, bool, null, etc
|
||||
err = json.Unmarshal([]byte(rawValue), &value)
|
||||
if err != nil {
|
||||
// If we got an error we try wrapping the value in
|
||||
// quotes and processing it as a string
|
||||
err = json.Unmarshal([]byte("\""+rawValue+"\""), &value)
|
||||
if err != nil {
|
||||
// If we can't decode as a string we return the err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
key: value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// If len > 1 then we have something like a[b][c]=2
|
||||
// so we need to turn this into {"a": {"b": {"c": 2}}}
|
||||
// To do this we break our key into two pieces:
|
||||
// a and b[c]
|
||||
// and then we set {"a": queryToMap("b[c]", value)}
|
||||
ret := make(map[string]interface{}, 0)
|
||||
ret[key], err = queryToMap(buildNewKey(rawKey) + "=" + rawValue)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// When URL params have a set of empty brackets (eg a[]=1)
|
||||
// it is assumed to be an array. This will get us the
|
||||
// correct value for the array item and return it as an
|
||||
// []interface{} so that it can be merged properly.
|
||||
if pieces[1] == "" {
|
||||
temp := ret[key].(map[string]interface{})
|
||||
ret[key] = []interface{}{temp[""]}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// buildNewKey will take something like:
|
||||
// origKey = "bar[one][two]"
|
||||
// pieces = [bar one two ]
|
||||
// and return "one[two]".
|
||||
func buildNewKey(origKey string) string {
|
||||
pieces := bracketSplitter.Split(origKey, -1)
|
||||
ret := origKey[len(pieces[0])+1:]
|
||||
ret = ret[:len(pieces[1])] + ret[len(pieces[1])+1:]
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// splitKeyAndValue splits a URL param at the last equal
|
||||
// sign and returns the two strings. If no equal sign is
|
||||
// found, the ErrInvalidParam error is returned.
|
||||
func splitKeyAndValue(param string) (string, string, error) {
|
||||
li := strings.LastIndex(param, "=")
|
||||
if li == -1 {
|
||||
return "", "", ErrInvalidParam
|
||||
}
|
||||
|
||||
return param[:li], param[li+1:], nil
|
||||
}
|
@ -1,170 +0,0 @@
|
||||
package qson
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func ExampleUnmarshal() {
|
||||
type Ex struct {
|
||||
A string `json:"a"`
|
||||
B struct {
|
||||
C int `json:"c"`
|
||||
} `json:"b"`
|
||||
}
|
||||
var ex Ex
|
||||
if err := Unmarshal(&ex, "a=xyz&b[c]=456"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("%+v\n", ex)
|
||||
// Output: {A:xyz B:{C:456}}
|
||||
}
|
||||
|
||||
type unmarshalT struct {
|
||||
A string `json:"a"`
|
||||
B unmarshalB `json:"b"`
|
||||
}
|
||||
type unmarshalB struct {
|
||||
C int `json:"c"`
|
||||
D string `json:"D"`
|
||||
}
|
||||
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
query := "a=xyz&b[c]=456"
|
||||
expected := unmarshalT{
|
||||
A: "xyz",
|
||||
B: unmarshalB{
|
||||
C: 456,
|
||||
},
|
||||
}
|
||||
var actual unmarshalT
|
||||
err := Unmarshal(&actual, query)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if expected != actual {
|
||||
t.Errorf("Expected: %+v Actual: %+v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleToJSON() {
|
||||
b, err := ToJSON("a=xyz&b[c]=456")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Print(string(b))
|
||||
// Output: {"a":"xyz","b":{"c":456}}
|
||||
}
|
||||
|
||||
func TestToJSONNested(t *testing.T) {
|
||||
query := "bar%5Bone%5D%5Btwo%5D=2&bar[one][red]=112"
|
||||
expected := `{"bar":{"one":{"red":112,"two":2}}}`
|
||||
actual, err := ToJSON(query)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
actualStr := string(actual)
|
||||
if actualStr != expected {
|
||||
t.Errorf("Expected: %s Actual: %s", expected, actualStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToJSONPlain(t *testing.T) {
|
||||
query := "cat=1&dog=2"
|
||||
expected := `{"cat":1,"dog":2}`
|
||||
actual, err := ToJSON(query)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
actualStr := string(actual)
|
||||
if actualStr != expected {
|
||||
t.Errorf("Expected: %s Actual: %s", expected, actualStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToJSONSlice(t *testing.T) {
|
||||
query := "cat[]=1&cat[]=34"
|
||||
expected := `{"cat":[1,34]}`
|
||||
actual, err := ToJSON(query)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
actualStr := string(actual)
|
||||
if actualStr != expected {
|
||||
t.Errorf("Expected: %s Actual: %s", expected, actualStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToJSONBig(t *testing.T) {
|
||||
query := "distinct_id=763_1495187301909_3495×tamp=1495187523&event=product_add_cart¶ms%5BproductRefId%5D=8284563078¶ms%5Bapps%5D%5B%5D=precommend¶ms%5Bapps%5D%5B%5D=bsales¶ms%5Bsource%5D=item¶ms%5Boptions%5D%5Bsegment%5D=cart_recommendation¶ms%5Boptions%5D%5Btype%5D=up_sell¶ms%5BtimeExpire%5D=1495187599642¶ms%5Brecommend_system_product_source%5D=item¶ms%5Bproduct_id%5D=8284563078¶ms%5Bvariant_id%5D=27661944134¶ms%5Bsku%5D=00483332%20(black)¶ms%5Bsources%5D%5B%5D=product_recommendation¶ms%5Bcart_token%5D=dc2c336a009edf2762128e65806dfb1d¶ms%5Bquantity%5D=1¶ms%5Bnew_popup_upsell_mobile%5D=false¶ms%5BclientDevice%5D=desktop¶ms%5BclientIsMobile%5D=false¶ms%5BclientIsSmallScreen%5D=false¶ms%5Bnew_popup_crossell_mobile%5D=false&api_key=14c5b7dacea9157029265b174491d340"
|
||||
expected := `{"api_key":"14c5b7dacea9157029265b174491d340","distinct_id":"763_1495187301909_3495","event":"product_add_cart","params":{"apps":["precommend","bsales"],"cart_token":"dc2c336a009edf2762128e65806dfb1d","clientDevice":"desktop","clientIsMobile":false,"clientIsSmallScreen":false,"new_popup_crossell_mobile":false,"new_popup_upsell_mobile":false,"options":{"segment":"cart_recommendation","type":"up_sell"},"productRefId":8284563078,"product_id":8284563078,"quantity":1,"recommend_system_product_source":"item","sku":"00483332 (black)","source":"item","sources":["product_recommendation"],"timeExpire":1495187599642,"variant_id":27661944134},"timestamp":1495187523}`
|
||||
actual, err := ToJSON(query)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
actualStr := string(actual)
|
||||
if actualStr != expected {
|
||||
t.Errorf("Expected: %s Actual: %s", expected, actualStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToJSONDuplicateKey(t *testing.T) {
|
||||
query := "cat=1&cat=2"
|
||||
expected := `{"cat":2}`
|
||||
actual, err := ToJSON(query)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
actualStr := string(actual)
|
||||
if actualStr != expected {
|
||||
t.Errorf("Expected: %s Actual: %s", expected, actualStr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitKeyAndValue(t *testing.T) {
|
||||
param := "a[dog][=cat]=123"
|
||||
eKey, eValue := "a[dog][=cat]", "123"
|
||||
aKey, aValue, err := splitKeyAndValue(param)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if eKey != aKey {
|
||||
t.Errorf("Keys do not match. Expected: %s Actual: %s", eKey, aKey)
|
||||
}
|
||||
if eValue != aValue {
|
||||
t.Errorf("Values do not match. Expected: %s Actual: %s", eValue, aValue)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodedAmpersand(t *testing.T) {
|
||||
query := "a=xyz&b[d]=ben%26jerry"
|
||||
expected := unmarshalT{
|
||||
A: "xyz",
|
||||
B: unmarshalB{
|
||||
D: "ben&jerry",
|
||||
},
|
||||
}
|
||||
var actual unmarshalT
|
||||
err := Unmarshal(&actual, query)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if expected != actual {
|
||||
t.Errorf("Expected: %+v Actual: %+v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodedAmpersand2(t *testing.T) {
|
||||
query := "filter=parent%3Dflow12345%26request%3Dreq12345&meta.limit=20&meta.offset=0"
|
||||
expected := map[string]interface{}{"filter": "parent=flow12345&request=req12345", "meta.limit": float64(20), "meta.offset": float64(0)}
|
||||
actual := make(map[string]interface{})
|
||||
err := Unmarshal(&actual, query)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
for k, v := range actual {
|
||||
if nv, ok := expected[k]; !ok || nv != v {
|
||||
t.Errorf("Expected: %+v Actual: %+v", expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
// Package stream encapsulates streams within streams
|
||||
package stream
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"go-micro.dev/v5/client"
|
||||
"go-micro.dev/v5/codec"
|
||||
"go-micro.dev/v5/metadata"
|
||||
"go-micro.dev/v5/server"
|
||||
)
|
||||
|
||||
type Stream interface {
|
||||
Context() context.Context
|
||||
SendMsg(interface{}) error
|
||||
RecvMsg(interface{}) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
type stream struct {
|
||||
Stream
|
||||
|
||||
err error
|
||||
request *request
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type request struct {
|
||||
client.Request
|
||||
context context.Context
|
||||
}
|
||||
|
||||
func (r *request) Codec() codec.Reader {
|
||||
return r.Request.Codec().(codec.Reader)
|
||||
}
|
||||
|
||||
func (r *request) Header() map[string]string {
|
||||
md, _ := metadata.FromContext(r.context)
|
||||
return md
|
||||
}
|
||||
|
||||
func (r *request) Read() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *stream) Request() server.Request {
|
||||
return s.request
|
||||
}
|
||||
|
||||
func (s *stream) Send(v interface{}) error {
|
||||
err := s.Stream.SendMsg(v)
|
||||
if err != nil {
|
||||
s.Lock()
|
||||
s.err = err
|
||||
s.Unlock()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *stream) Recv(v interface{}) error {
|
||||
err := s.Stream.RecvMsg(v)
|
||||
if err != nil {
|
||||
s.Lock()
|
||||
s.err = err
|
||||
s.Unlock()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *stream) Error() error {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
return s.err
|
||||
}
|
||||
|
||||
// New returns a new encapsulated stream
|
||||
// Proto stream within a server.Stream.
|
||||
func New(service, endpoint string, req interface{}, s Stream) server.Stream {
|
||||
return &stream{
|
||||
Stream: s,
|
||||
request: &request{
|
||||
context: s.Context(),
|
||||
Request: client.DefaultClient.NewRequest(service, endpoint, req),
|
||||
},
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user