mirror of
https://github.com/go-micro/go-micro.git
synced 2025-05-31 21:59:42 +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