Skip to content

Commit a397109

Browse files
authored
Merge pull request #1 from projectsesame/feat/conv
feat: convert between xml and json
2 parents 284c294 + 27f7aa9 commit a397109

File tree

9 files changed

+177
-130
lines changed

9 files changed

+177
-130
lines changed

Diff for: Dockerfile

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
FROM golang:1.21.6-bullseye
1+
FROM m.daocloud.io/docker.io/library/golang:1.21.6-bullseye
22

33
SHELL ["/bin/bash", "-c"]
44

5+
RUN sed -i 's/htt[p|ps]:\/\/archive.ubuntu.com\/ubuntu\//mirror:\/\/mirrors.ubuntu.com\/mirrors.txt/g' /etc/apt/sources.list
6+
57
RUN apt-get update && apt-get -y upgrade \
68
&& apt-get autoremove -y \
79
&& rm -rf /var/lib/apt/lists/* \
@@ -10,19 +12,21 @@ RUN apt-get update && apt-get -y upgrade \
1012
WORKDIR /build
1113

1214
COPY . .
15+
16+
RUN go env -w GOPROXY=https://goproxy.cn,direct
1317
RUN go mod tidy \
1418
&& go mod download \
1519
&& go build -o /extproc
1620

1721

18-
FROM busybox
22+
FROM m.daocloud.io/docker.io/library/busybox:latest
1923

2024
COPY --from=0 /extproc /bin/extproc
2125
RUN chmod +x /bin/extproc
2226

23-
ARG EXAMPLE=payload-limit
27+
ARG EXAMPLE=xml-json-conv
2428

2529
EXPOSE 50051
2630

2731
ENTRYPOINT [ "/bin/extproc" ]
28-
CMD [ "payload-limit", "--log-stream", "--log-phases", "payload-limit", "32" ]
32+
CMD [ "conv", "--log-stream", "--log-phases" ]

Diff for: README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# envoy-extproc-payloadlimit-demo-go
1+
# envoy-extproc-xml-json-conv-demo-go
22

3-
This repository contains a demo application written in Go that demonstrates the usage of Envoy's External Processor (ExtProc) filter to do `payload limit` for POST request.
3+
This repository contains a demo application written in Go that demonstrates the usage of Envoy's External Processor (ExtProc) filter to convert the `data formats` for POST request.
44

55
## Overview
66

@@ -19,7 +19,7 @@ To get started with the demo application, follow these steps:
1919

2020
1. Clone the repository:
2121
```
22-
git clone https://github.com/projectsesame/envoy-extproc-payloadlimit-demo-go.git
22+
git clone https://github.com/projectsesame/envoy-extproc-xml-json-conv-demo-go.git
2323
```
2424
2525
2. Build the Go application:
@@ -29,7 +29,7 @@ To get started with the demo application, follow these steps:
2929
3030
3. Run the application:
3131
```
32-
./envoy-extproc-payloadlimit-demo-go payload-limit --log-stream --log-phases payload-limit 32
32+
./envoy-extproc-xml-json-conv-demo-go conv --log-stream --log-phases
3333
```
3434
3535

Diff for: conv.go

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"encoding/xml"
7+
"errors"
8+
"io"
9+
"log"
10+
11+
mxj "github.com/clbanning/mxj/v2"
12+
typev3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
13+
ep "github.com/wrossmorrow/envoy-extproc-sdk-go"
14+
)
15+
16+
type convRequestProcessor struct {
17+
opts *ep.ProcessingOptions
18+
}
19+
20+
func (s *convRequestProcessor) GetName() string {
21+
return "xml-json-conv"
22+
}
23+
24+
func (s *convRequestProcessor) GetOptions() *ep.ProcessingOptions {
25+
return s.opts
26+
}
27+
28+
func (s *convRequestProcessor) ProcessRequestHeaders(ctx *ep.RequestContext, headers ep.AllHeaders) error {
29+
return ctx.ContinueRequest()
30+
}
31+
32+
const (
33+
kXML = "xml"
34+
kJSON = "json"
35+
)
36+
37+
// detectFormat attempts to determine whether the data is JSON or XML.
38+
func detectFormat(data []byte) (string, error) {
39+
if json.Valid(data) {
40+
return kJSON, nil
41+
}
42+
43+
if isValidXML(data) {
44+
return kXML, nil
45+
}
46+
47+
return "", errors.New("unknown format")
48+
}
49+
50+
func isValidXML(input []byte) bool {
51+
decoder := xml.NewDecoder(bytes.NewReader(input))
52+
for {
53+
err := decoder.Decode(new(any))
54+
if err != nil {
55+
return err == io.EOF
56+
}
57+
}
58+
}
59+
60+
func (s *convRequestProcessor) ProcessRequestBody(ctx *ep.RequestContext, body []byte) error {
61+
cancel := func(code int32) error {
62+
return ctx.CancelRequest(code, map[string]ep.HeaderValue{}, typev3.StatusCode_name[code])
63+
}
64+
65+
from, err := detectFormat(body)
66+
if err != nil {
67+
log.Println(err)
68+
return cancel(400)
69+
}
70+
71+
var data []byte
72+
73+
switch from {
74+
case kJSON:
75+
data, err = jsonToXML(body)
76+
case kXML:
77+
data, err = xmlToJSON(body)
78+
default:
79+
panic("never happen")
80+
}
81+
82+
if err != nil {
83+
log.Printf("convert data format is failed: %v", err)
84+
return cancel(400)
85+
}
86+
87+
return ctx.CancelRequest(200, map[string]ep.HeaderValue{}, string(data))
88+
}
89+
90+
func (s *convRequestProcessor) ProcessRequestTrailers(ctx *ep.RequestContext, trailers ep.AllHeaders) error {
91+
return ctx.ContinueRequest()
92+
}
93+
94+
func (s *convRequestProcessor) ProcessResponseHeaders(ctx *ep.RequestContext, headers ep.AllHeaders) error {
95+
return ctx.ContinueRequest()
96+
}
97+
98+
func (s *convRequestProcessor) ProcessResponseBody(ctx *ep.RequestContext, body []byte) error {
99+
return ctx.ContinueRequest()
100+
}
101+
102+
func (s *convRequestProcessor) ProcessResponseTrailers(ctx *ep.RequestContext, trailers ep.AllHeaders) error {
103+
return ctx.ContinueRequest()
104+
}
105+
106+
func (s *convRequestProcessor) Init(opts *ep.ProcessingOptions, nonFlagArgs []string) error {
107+
s.opts = opts
108+
return nil
109+
}
110+
111+
func (s *convRequestProcessor) Finish() {}
112+
113+
// jsonToXML converts a JSON byte array to XML byte array.
114+
func jsonToXML(data []byte) ([]byte, error) {
115+
mxj.XMLEscapeChars(true)
116+
m, err := mxj.NewMapJsonReader(bytes.NewReader(data))
117+
if err != nil {
118+
return nil, err
119+
}
120+
121+
buf := &bytes.Buffer{}
122+
err = m.XmlIndentWriter(buf, "", "\t")
123+
if err != nil {
124+
return nil, err
125+
}
126+
return buf.Bytes(), nil
127+
}
128+
129+
// xmlToJSON converts XML data to JSON.
130+
func xmlToJSON(data []byte) ([]byte, error) {
131+
mxj.XMLEscapeChars(true)
132+
m, err := mxj.NewMapXmlReader(bytes.NewReader(data))
133+
if err != nil {
134+
return nil, err
135+
}
136+
137+
buf := &bytes.Buffer{}
138+
err = m.JsonIndentWriter(buf, "", "\t")
139+
if err != nil {
140+
return nil, err
141+
}
142+
return buf.Bytes(), nil
143+
}

Diff for: deployment.yaml

+9-9
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,24 @@
22
apiVersion: apps/v1
33
kind: Deployment
44
metadata:
5-
name: payload-limit-demo
6-
namespace: payload-limit-demo
5+
name: xml-json-conv-demo
6+
namespace: xml-json-conv-demo
77
annotations:
88
spec:
99
replicas: 1
1010
selector:
1111
matchLabels:
12-
app: payload-limit-demo
12+
app: xml-json-conv-demo
1313
template:
1414
metadata:
1515
creationTimestamp: null
1616
labels:
17-
app: payload-limit-demo
17+
app: xml-json-conv-demo
1818
spec:
1919
containers:
20-
- name: payload-limit-demo-container
20+
- name: xml-json-conv-demo-container
2121
image: >-
22-
release.daocloud.io/skoala/envoy-extproc-payloadlimit-demo-go@sha256:dcc5ba7aa2790e87b0cd46799363367b54202585794ebfe87248264f111e63d8
22+
release.daocloud.io/skoala/envoy-extproc-xml-json-conv-demo-go:v0.0.1
2323
ports:
2424
- containerPort: 50051
2525
protocol: TCP
@@ -44,8 +44,8 @@ spec:
4444
apiVersion: v1
4545
kind: Service
4646
metadata:
47-
name: payload-limit-demo
48-
namespace: payload-limit-demo
47+
name: xml-json-conv-demo
48+
namespace: xml-json-conv-demo
4949
annotations:
5050
spec:
5151
ports:
@@ -54,7 +54,7 @@ spec:
5454
targetPort: 50051
5555
nodePort: 31928
5656
selector:
57-
app: payload-limit-demo
57+
app: xml-json-conv-demo
5858
type: NodePort
5959
sessionAffinity: None
6060
externalTrafficPolicy: Cluster

Diff for: envoy.yaml

+8-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ static_resources:
22
listeners:
33
- name: listener
44
address:
5-
socket_address: {address: 0.0.0.0, port_value: 8000}
5+
socket_address: {address: 0.0.0.0, port_value: 8080}
66
filter_chains:
77
- filters:
88
- name: envoy.filters.network.http_connection_manager
@@ -35,7 +35,7 @@ static_resources:
3535
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor
3636
grpc_service:
3737
envoy_grpc:
38-
cluster_name: payload-limit
38+
cluster_name: xml-json-conv
3939
timeout: 30s
4040
failure_mode_allow: true
4141
message_timeout: 0.2s
@@ -68,21 +68,21 @@ static_resources:
6868
hostname: upstream
6969
address:
7070
socket_address:
71-
address: upstream
72-
port_value: 8000
73-
- name: payload-limit
71+
address: 127.0.0.1 # if in a k8s cluster, use the service name
72+
port_value: 5678
73+
- name: xml-json-conv
7474
dns_lookup_family: V4_ONLY
7575
lb_policy: LEAST_REQUEST
7676
load_assignment:
77-
cluster_name: payload-limit
77+
cluster_name: xml-json-conv
7878
endpoints:
7979
- lb_endpoints:
8080
- endpoint:
8181
address:
8282
socket_address:
83-
address: payload-limit
83+
address: 127.0.0.1 # if in a k8s cluster, use the service name
8484
port_value: 50051
85-
hostname: payload-limit
85+
hostname: xml-json-conv
8686
type: LOGICAL_DNS
8787
typed_extension_protocol_options:
8888
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:

Diff for: go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
module github.com/projectsesame/envoy-extproc-payloadlimit-demo-go
1+
module github.com/projectsesame/envoy-extproc-xml-json-conv-demo-go
22

33
go 1.21
44

55
require (
6+
github.com/clbanning/mxj/v2 v2.7.0
67
github.com/envoyproxy/go-control-plane v0.12.0
78
github.com/wrossmorrow/envoy-extproc-sdk-go v0.0.21
89
)

Diff for: go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
2+
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
13
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ=
24
github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM=
35
github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI=

Diff for: main.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type processor interface {
1616
}
1717

1818
var processors = map[string]processor{
19-
"payload-limit": &payloadLimitRequestProcessor{},
19+
"conv": &convRequestProcessor{},
2020
}
2121

2222
func parseArgs(args []string) (port *int, opts *ep.ProcessingOptions, nonFlagArgs []string) {
@@ -36,7 +36,6 @@ func parseArgs(args []string) (port *int, opts *ep.ProcessingOptions, nonFlagArg
3636
}
3737

3838
func main() {
39-
4039
// cmd subCmd arg, arg2,...
4140
args := os.Args
4241
if len(args) < 2 {

0 commit comments

Comments
 (0)