├── .gitignore
├── LICENSE
├── README.md
├── chat_server
├── index.html.go
├── main.go
└── session_manager.go
├── file_server
└── main.go
├── go.mod
├── go.sum
├── https_server
├── cert.pem
├── key.pem
└── main.go
├── redis_cli
├── README.md
├── main.go
├── redis_codec.go
├── redis_console.go
└── redisgo
│ ├── LICENSE
│ ├── README.md
│ ├── decode.go
│ ├── decode_test.go
│ ├── encode.go
│ ├── encode_test.go
│ └── resp.go
└── tcp_server
└── main.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-netty-samples
2 |
3 | An open source samples written by [go-netty](https://github.com/go-netty/go-netty)
4 |
5 | ## Samples
6 |
7 | 1. [chat_server](./chat_server) - A simple web-based chatroom
8 | 2. [file_server](./file_server) - A simple web-based file browser
9 | 3. [tcp_server](./tcp_server) - A simple echo server & client
10 | 4. [redis_cli](./redis_cli) - A simple redis cli
11 | 5. [https_server](./https_server) - A simple https server
12 |
--------------------------------------------------------------------------------
/chat_server/index.html.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the go-netty project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | var indexHtml = []byte(`
20 |
21 |
22 |
23 |
24 | WebSocket Chat
25 |
26 |
27 |
62 |
71 |
72 |
73 |
74 |
75 | `)
76 |
--------------------------------------------------------------------------------
/chat_server/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the go-netty project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "net/http"
22 |
23 | "github.com/go-netty/go-netty"
24 | "github.com/go-netty/go-netty-transport/websocket"
25 | "github.com/go-netty/go-netty/codec/format"
26 | "github.com/go-netty/go-netty/codec/frame"
27 | )
28 |
29 | var ManagerInst = NewManager()
30 |
31 | func main() {
32 |
33 | // index page.
34 | websocket.DefaultOptions.ServeMux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
35 | writer.Write(indexHtml)
36 | })
37 |
38 | // child pipeline initializer.
39 | setupCodec := func(channel netty.Channel) {
40 | channel.Pipeline().
41 | // read websocket message
42 | AddLast(frame.PacketCodec(128)).
43 | // decode bytes to map[string]interface{}
44 | AddLast(format.JSONCodec(true, false)).
45 | // session recorder.
46 | AddLast(ManagerInst).
47 | // chat handler.
48 | AddLast(chatHandler{})
49 | }
50 |
51 | // setup bootstrap & startup server.
52 | netty.NewBootstrap(netty.WithChildInitializer(setupCodec), netty.WithTransport(websocket.New())).
53 | Listen("0.0.0.0:8080/chat").Sync()
54 | }
55 |
56 | type chatHandler struct{}
57 |
58 | func (chatHandler) HandleActive(ctx netty.ActiveContext) {
59 | type wsTransport interface {
60 | Route() string
61 | Header() http.Header
62 | }
63 |
64 | if wst, ok := ctx.Channel().Transport().(wsTransport); ok {
65 | fmt.Printf("child connection from: %s, route: %s, Websocket-Key: %s, User-Agent: %s\n",
66 | ctx.Channel().RemoteAddr(), wst.Route(), wst.Header().Get("Sec-Websocket-Key"), wst.Header().Get("User-Agent"))
67 | }
68 |
69 | ctx.HandleActive()
70 | }
71 |
72 | func (chatHandler) HandleRead(ctx netty.InboundContext, message netty.Message) {
73 |
74 | fmt.Printf("received child message from: %s, %v\n", ctx.Channel().RemoteAddr(), message)
75 |
76 | if cmd, ok := message.(map[string]interface{}); ok {
77 | cmd["id"] = ctx.Channel().ID()
78 | }
79 |
80 | ManagerInst.Broadcast(message)
81 | }
82 |
83 | func (chatHandler) HandleInactive(ctx netty.InactiveContext, ex netty.Exception) {
84 | fmt.Printf("child connection closed: %s %s\n", ctx.Channel().RemoteAddr(), ex.Error())
85 | ctx.HandleInactive(ex)
86 | }
87 |
--------------------------------------------------------------------------------
/chat_server/session_manager.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the go-netty project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "sync"
21 |
22 | "github.com/go-netty/go-netty"
23 | )
24 |
25 | type Manager interface {
26 | netty.ActiveHandler
27 | netty.InactiveHandler
28 | Size() int
29 | Context(id int64) netty.HandlerContext
30 | ForEach(func(netty.HandlerContext) bool)
31 | Broadcast(message netty.Message)
32 | BroadcastIf(message netty.Message, fn func(netty.HandlerContext) bool)
33 | }
34 |
35 | func NewManager() Manager {
36 | return &sessionManager{
37 | _sessions: make(map[int64]netty.HandlerContext, 64),
38 | }
39 | }
40 |
41 | type sessionManager struct {
42 | _sessions map[int64]netty.HandlerContext
43 | _mutex sync.RWMutex
44 | }
45 |
46 | func (s *sessionManager) Size() int {
47 | s._mutex.RLock()
48 | size := len(s._sessions)
49 | s._mutex.RUnlock()
50 | return size
51 | }
52 |
53 | func (s *sessionManager) Context(id int64) netty.HandlerContext {
54 | s._mutex.RLock()
55 | ctx, _ := s._sessions[id]
56 | s._mutex.RUnlock()
57 | return ctx
58 | }
59 |
60 | func (s *sessionManager) ForEach(fn func(netty.HandlerContext) bool) {
61 | s._mutex.RLock()
62 | defer s._mutex.RUnlock()
63 |
64 | for _, ctx := range s._sessions {
65 | fn(ctx)
66 | }
67 | }
68 |
69 | func (s *sessionManager) Broadcast(message netty.Message) {
70 | s.ForEach(func(ctx netty.HandlerContext) bool {
71 | ctx.Write(message)
72 | return true
73 | })
74 | }
75 |
76 | func (s *sessionManager) BroadcastIf(message netty.Message, fn func(netty.HandlerContext) bool) {
77 | s.ForEach(func(ctx netty.HandlerContext) bool {
78 | if fn(ctx) {
79 | ctx.Write(message)
80 | }
81 | return true
82 | })
83 | }
84 |
85 | func (s *sessionManager) HandleActive(ctx netty.ActiveContext) {
86 |
87 | s._mutex.Lock()
88 | s._sessions[ctx.Channel().ID()] = ctx
89 | s._mutex.Unlock()
90 |
91 | ctx.HandleActive()
92 | }
93 |
94 | func (s *sessionManager) HandleInactive(ctx netty.InactiveContext, ex netty.Exception) {
95 | s._mutex.Lock()
96 | delete(s._sessions, ctx.Channel().ID())
97 | s._mutex.Unlock()
98 |
99 | ctx.HandleInactive(ex)
100 | }
101 |
--------------------------------------------------------------------------------
/file_server/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the go-netty project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "net/http"
22 |
23 | "github.com/go-netty/go-netty"
24 | "github.com/go-netty/go-netty/codec/xhttp"
25 | )
26 |
27 | func main() {
28 |
29 | // http file server handler.
30 | httpMux := http.NewServeMux()
31 | httpMux.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir("./"))))
32 |
33 | // channel pipeline initializer.
34 | setupCodec := func(channel netty.Channel) {
35 | channel.Pipeline().
36 | // decode http request from channel
37 | AddLast(xhttp.ServerCodec()).
38 | // print http access log
39 | AddLast(new(httpStateHandler)).
40 | // compatible with http.Handler
41 | AddLast(xhttp.Handler(httpMux))
42 | }
43 |
44 | // setup bootstrap & startup server.
45 | netty.NewBootstrap(netty.WithChildInitializer(setupCodec)).
46 | Listen("0.0.0.0:8080").Sync()
47 | }
48 |
49 | type httpStateHandler struct{}
50 |
51 | func (*httpStateHandler) HandleActive(ctx netty.ActiveContext) {
52 | fmt.Printf("http client active: %s\n", ctx.Channel().RemoteAddr())
53 | ctx.HandleActive()
54 | }
55 |
56 | func (*httpStateHandler) HandleRead(ctx netty.InboundContext, message netty.Message) {
57 | if request, ok := message.(*http.Request); ok {
58 | fmt.Printf("[%d]%s: %s %s\n", ctx.Channel().ID(), ctx.Channel().RemoteAddr(), request.Method, request.URL.Path)
59 | }
60 | ctx.HandleRead(message)
61 | }
62 |
63 | func (*httpStateHandler) HandleInactive(ctx netty.InactiveContext, ex netty.Exception) {
64 | fmt.Printf("http client inactive: %s %v\n", ctx.Channel().RemoteAddr(), ex)
65 | ctx.HandleInactive(ex)
66 | }
67 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/go-netty/go-netty-samples
2 |
3 | go 1.22
4 |
5 | require (
6 | github.com/go-netty/go-netty v1.6.7
7 | github.com/go-netty/go-netty-transport v1.7.13
8 | )
9 |
10 | require (
11 | github.com/gobwas/httphead v0.1.0 // indirect
12 | github.com/gobwas/pool v0.2.1 // indirect
13 | github.com/gobwas/ws v1.4.0 // indirect
14 | golang.org/x/sys v0.28.0 // indirect
15 | )
16 |
17 | //replace github.com/go-netty/go-netty => ../go-netty
18 | //replace github.com/go-netty/go-netty-transport => ../go-netty-transport
19 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/go-netty/go-netty v1.6.7 h1:heWNYCzAjiHnNZvLaoLyemHgZIWb0FSvZERYI3LnJqQ=
2 | github.com/go-netty/go-netty v1.6.7/go.mod h1:vSbL7RzFTO5bHXhxzZsAW0iStVz1qnenR90UVF6e1HA=
3 | github.com/go-netty/go-netty-transport v1.7.13 h1:46aR9f+Iosm3FHwNlz2u9/nWYoaV12EapDkEmfvlX6U=
4 | github.com/go-netty/go-netty-transport v1.7.13/go.mod h1:B0v+jKzmtQOiA1dagrm4Z5mn1CddkKPzoUDfQlSsjkk=
5 | github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
6 | github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
7 | github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
8 | github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
9 | github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
10 | github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
11 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
12 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
13 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
14 |
--------------------------------------------------------------------------------
/https_server/cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDazCCAlOgAwIBAgIUS0gnR4GQRAax+E+PaFiV2+ktXN8wDQYJKoZIhvcNAQEL
3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDAzMzAxNjMzMzRaFw0zMDAz
5 | MjgxNjMzMzRaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
7 | AQUAA4IBDwAwggEKAoIBAQCmJtTK0ujv9jDEhmhrOsSELj9i1+ZZHKDdZQrexZXZ
8 | /ipBzpk3HXiXeLOxwpmiQTgW7l+ocx/RoWmjr8/95ZCDgMYk2skMmJw5HbldQtaG
9 | c35Bu1w0xjo83vQRfLW/DhEgc6bN++4HWssmoex+PX5mNc5ZfdC+0apeGLuDv8Bl
10 | /B9BhF1gApbs3oKNy2NuU3T1z/jjymPRFHNbbfVNAN7Zzt5l9FH4yZosr1K5ZVHF
11 | yK1Aj76pdVE+P48TK8otOKZusUBGGeg7+Uyq3A8YrUJChr55MxZ8JolIOPBj13mS
12 | pQmnjNRpFrgwpfLqPKsK/gkGP8mpB0XVjQ54REHXytCdAgMBAAGjUzBRMB0GA1Ud
13 | DgQWBBSTjBPnKG4iDEN8VQ4QuT+lMxTR2jAfBgNVHSMEGDAWgBSTjBPnKG4iDEN8
14 | VQ4QuT+lMxTR2jAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAj
15 | R75wzk+44AY9dn+Qg9LxglT9ReZQ15qxUnAb30SEQNsrLY9i73acuLQlOQYenvAU
16 | 1mgeHinX4EFYOSp21azGZ3h9t4gy+MrnYtu26KasWc7KRrJRm0NfS8anRAESFMbK
17 | 0azUTcI+RTLZWAZXgUAtWacjUsxcoGmHIw/9aQ9fDtJ1+72WL/VNb2nlzUtB8j1r
18 | ZIct/CfEX6MRPhhpF38zOfE2i/5QcD9FJcZFHHbRU4ebJdcUKu+RnewYdDUsOPkv
19 | xZCjL/K15HTF++e5HNHnB3qDp6m9R9P79IlUck2+u18r/mBEhl5o/Xvp4yitvMc5
20 | OQfMbtCPeum72OkMnMDj
21 | -----END CERTIFICATE-----
22 |
--------------------------------------------------------------------------------
/https_server/key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEpAIBAAKCAQEApibUytLo7/YwxIZoazrEhC4/YtfmWRyg3WUK3sWV2f4qQc6Z
3 | Nx14l3izscKZokE4Fu5fqHMf0aFpo6/P/eWQg4DGJNrJDJicOR25XULWhnN+Qbtc
4 | NMY6PN70EXy1vw4RIHOmzfvuB1rLJqHsfj1+ZjXOWX3QvtGqXhi7g7/AZfwfQYRd
5 | YAKW7N6CjctjblN09c/448pj0RRzW231TQDe2c7eZfRR+MmaLK9SuWVRxcitQI++
6 | qXVRPj+PEyvKLTimbrFARhnoO/lMqtwPGK1CQoa+eTMWfCaJSDjwY9d5kqUJp4zU
7 | aRa4MKXy6jyrCv4JBj/JqQdF1Y0OeERB18rQnQIDAQABAoIBAFuQqNKB+xdLMMMN
8 | Y3w8rdI+fnYlj29qpIh+R8hPUW8KcBVGGw/RsQSg4z7ChNJzaMrSYWs+zrlDYCly
9 | GYjNu5+NFyV2YTgM3oNniyEHVZxRB7KH45+94phrMkx00uBQi0W8FqAMB7iL48sN
10 | j3xevUrsuiR9fs26VE55CNXplcldjiS7tcEIcCj557mwbmf4aJgwAtK7fMyxhGmM
11 | yThrSyG5GazZJ0nFCPcph46+gLjdXFoCqjLHlk98uSr4Ax+GYyFChY5V5MeY69BH
12 | 3TsCbhTsPB4tofOpO7goDdExitnl/chIfadNSnRtEPhQNNQiieYGQgAYoJxSMxI9
13 | 3lvLBmECgYEA02voNgwcSm1NQml8bXIeC+cHROMaessp0KEqRm4J4l8i1X62vPdu
14 | qbOR664LRJytcEOK8vFxf/DwiOtia2O1S4yJeDwYiktO0K0ozS7hUKK1gcMMZ6pg
15 | 5J1g0/NFhTLob0W5hRRx+byd+ZDQPiSf2v94Vzi5B+DXIUtWHJLAEDkCgYEAyS9a
16 | ZrQ7de6oh6gCxw8teY331rjOBhGaP8Ge0rwzg7ZjLHNJt0Mzfv+r3iLgbH/p2NTL
17 | NfV0s53ZqYJmNuuGcNZyyBjH4cDom8BR3RuJPMFgiyb+ufsmoj2P1YzVt10hIq8R
18 | v8qf+4TeZH8+h3MK1LSmAVwxO4vbrL3lX5Xve4UCgYBeMuXzrtGsF6ckv/tAiGf6
19 | xaTnfIh/zaWD30sGtmCRC/JpTVv+NgCIfAm4CIPcvjJIos92Dz2S07dYSgg/8N0G
20 | HEfj2mLUu1kSgRR81zZsxxI4Dr+oMLGbKsPJq8p93oDRky9lrAZcTz3TxEIEbi0L
21 | Vmg39H38lKFFpFtpv7jzgQKBgQCRp87zfJlwEJHDZUhdhgeOcdJ1iubeTfA3KJ8s
22 | Q0Wonmu+ytkb7XNjBwgPk4CYDSPo8GcPcd5EDIyBxP7r1ClJmHkRlZ9hOqEt4ldi
23 | lYUOj5KuLFVPoR2faKKyIXy5fJt42PcovDKHjvLUH6vNtOSqiv+FDJI4YlFxBysB
24 | wTPZYQKBgQCYnB1d1rHaEpDlgWx6WZnRvjK7S0cd1s8sX0zmexnhu8DK/iQpVFWb
25 | wXTnAFi0ljmHRjM+od6PCRVZ/V9iZIcsRmMhbaGcBMuRIQ5ASq1LVnTbXoQwbO+C
26 | nsHwLvd8nHfA9LyBSek9d7llK1oNeNRRQQW6b5X3sJWbBFAS6VCthQ==
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/https_server/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 the go-netty project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "net/http"
22 |
23 | "github.com/go-netty/go-netty"
24 | "github.com/go-netty/go-netty-transport/tls"
25 | "github.com/go-netty/go-netty/codec/xhttp"
26 | )
27 |
28 | func main() {
29 |
30 | httpMux := http.NewServeMux()
31 | httpMux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
32 | writer.Write([]byte("Hello, go-netty!"))
33 | })
34 |
35 | // child pipeline initializer.
36 | setupCodec := func(channel netty.Channel) {
37 | channel.Pipeline().
38 | // decode http request from channel
39 | AddLast(xhttp.ServerCodec()).
40 | // print http access log
41 | AddLast(new(httpStateHandler)).
42 | // compatible with http.Handler
43 | AddLast(xhttp.Handler(httpMux))
44 | }
45 |
46 | // setup bootstrap & startup server.
47 | netty.NewBootstrap(netty.WithChildInitializer(setupCodec), netty.WithTransport(tls.New())).
48 | Listen("0.0.0.0:8080", tls.WithOptions(&tls.Options{CertFile: "cert.pem", KeyFile: "key.pem"})).Sync()
49 | }
50 |
51 | type httpStateHandler struct{}
52 |
53 | func (*httpStateHandler) HandleActive(ctx netty.ActiveContext) {
54 | fmt.Printf("http client active: %s\n", ctx.Channel().RemoteAddr())
55 | ctx.HandleActive()
56 | }
57 |
58 | func (*httpStateHandler) HandleRead(ctx netty.InboundContext, message netty.Message) {
59 | if request, ok := message.(*http.Request); ok {
60 | fmt.Printf("[%d]%s: %s %s\n", ctx.Channel().ID(), ctx.Channel().RemoteAddr(), request.Method, request.URL.Path)
61 | }
62 | ctx.HandleRead(message)
63 | }
64 |
65 | func (*httpStateHandler) HandleInactive(ctx netty.InactiveContext, ex netty.Exception) {
66 | fmt.Printf("http client inactive: %s %v\n", ctx.Channel().RemoteAddr(), ex)
67 | ctx.HandleInactive(ex)
68 | }
69 |
--------------------------------------------------------------------------------
/redis_cli/README.md:
--------------------------------------------------------------------------------
1 | # redis_cli
2 | A simple redis cli written by [go-netty](https://github.com/go-netty/go-netty)
3 |
4 | ### Preview
5 | ```bash
6 | connecting redis server ...
7 | connected
8 | 192.168.212.212:6379>get name
9 | (empty)
10 | 192.168.212.212:6379>set name go-netty
11 | OK
12 | 192.168.212.212:6379>get name
13 | go-netty
14 | 192.168.212.212:6379>exit
15 | exited
16 | ```
17 |
--------------------------------------------------------------------------------
/redis_cli/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the go-netty project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 |
22 | "github.com/go-netty/go-netty"
23 | "github.com/go-netty/go-netty/utils"
24 | )
25 |
26 | func main() {
27 |
28 | // setup client pipeline initializer.
29 | setupCodec := func(channel netty.Channel) {
30 | channel.Pipeline().
31 | AddLast(&simpleRedisCodec{}, &simpleRedisConsole{})
32 | }
33 |
34 | // new bootstrap
35 | var bootstrap = netty.NewBootstrap(netty.WithClientInitializer(setupCodec))
36 |
37 | // connect to redis server
38 | fmt.Println("connecting redis server ...")
39 |
40 | ch, err := bootstrap.Connect("127.0.0.1:6379")
41 | utils.Assert(err)
42 |
43 | select {
44 | case <-ch.Context().Done():
45 | case <-bootstrap.Context().Done():
46 | }
47 |
48 | fmt.Println("exited")
49 | }
50 |
--------------------------------------------------------------------------------
/redis_cli/redis_codec.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the go-netty project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "bytes"
21 | "fmt"
22 | "github.com/go-netty/go-netty"
23 | "github.com/go-netty/go-netty-samples/redis_cli/redisgo"
24 | "github.com/go-netty/go-netty/utils"
25 | "io"
26 | )
27 |
28 | type simpleRedisCodec struct {
29 | decoder *redisgo.Decoder
30 | }
31 |
32 | func (s *simpleRedisCodec) CodecName() string {
33 | return "simple-redis-codec"
34 | }
35 |
36 | func (s *simpleRedisCodec) HandleRead(ctx netty.InboundContext, message netty.Message) {
37 |
38 | // init decoder.
39 | if nil == s.decoder {
40 | s.decoder = redisgo.NewDecoder(message.(io.Reader), 10240)
41 | }
42 |
43 | // decode redis response.
44 | resp := &redisgo.Resp{}
45 | utils.Assert(s.decoder.Decode(resp))
46 |
47 | // fmt.Println("->", resp.String())
48 | // post response.
49 | ctx.HandleRead(resp)
50 | }
51 |
52 | func (s *simpleRedisCodec) HandleWrite(ctx netty.OutboundContext, message netty.Message) {
53 |
54 | switch v := message.(type) {
55 | case []redisgo.Value:
56 | // encode request.
57 | buffer := bytes.NewBuffer(nil)
58 | utils.Assert(redisgo.EncodeMulti(buffer, v...))
59 |
60 | // fmt.Println("<-", buffer.String())
61 | // post request.
62 | ctx.HandleWrite(buffer)
63 | default:
64 | utils.Assert(fmt.Errorf("%T is invalid message", message))
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/redis_cli/redis_console.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the go-netty project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "bufio"
21 | "fmt"
22 | "os"
23 | "strings"
24 |
25 | "github.com/go-netty/go-netty"
26 | "github.com/go-netty/go-netty-samples/redis_cli/redisgo"
27 | )
28 |
29 | type simpleRedisConsole struct {
30 | respChan chan *redisgo.Resp
31 | }
32 |
33 | func (s *simpleRedisConsole) HandleActive(ctx netty.ActiveContext) {
34 | fmt.Println("connected")
35 |
36 | go s.attachConsole(ctx)
37 | ctx.HandleActive()
38 | }
39 |
40 | func (s *simpleRedisConsole) HandleRead(ctx netty.InboundContext, message netty.Message) {
41 | s.respChan <- message.(*redisgo.Resp)
42 | }
43 |
44 | func (s *simpleRedisConsole) HandleInactive(ctx netty.InactiveContext, ex netty.Exception) {
45 | fmt.Println("disconnected", ex)
46 |
47 | ctx.HandleInactive(ex)
48 | }
49 |
50 | func (s *simpleRedisConsole) attachConsole(ctx netty.HandlerContext) {
51 |
52 | s.respChan = make(chan *redisgo.Resp, 1)
53 |
54 | fmt.Print(ctx.Channel().RemoteAddr(), ">")
55 |
56 | stdin := bufio.NewScanner(os.Stdin)
57 | for stdin.Scan() {
58 |
59 | if text := strings.TrimSpace(stdin.Text()); len(text) > 0 {
60 |
61 | switch text {
62 | case "exit":
63 | ctx.Close(fmt.Errorf("user exit"))
64 | return
65 | case "help":
66 | fmt.Println("help information")
67 | default:
68 | inputs := strings.Split(text, " ")
69 |
70 | // build command.
71 | var cmds = make([]redisgo.Value, 0, len(inputs))
72 | for _, v := range inputs {
73 | cmds = append(cmds, redisgo.BlukString(v))
74 | }
75 |
76 | // send redis command.
77 | ctx.Write(cmds)
78 |
79 | // print response
80 | resp := <-s.respChan
81 |
82 | switch {
83 | case resp.Null:
84 | fmt.Println("(empty)")
85 | case len(resp.Data) > 0:
86 | fmt.Println(resp.Data)
87 | case len(resp.Array) > 0:
88 | for index, rsp := range resp.Array {
89 | fmt.Println(fmt.Sprintf(`%d) "%s"`, index+1, rsp.Data))
90 | }
91 | }
92 | }
93 | } else {
94 | fmt.Println("please input redis command")
95 | }
96 |
97 | fmt.Print(ctx.Channel().RemoteAddr(), ">")
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/redis_cli/redisgo/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 vizee
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/redis_cli/redisgo/README.md:
--------------------------------------------------------------------------------
1 | # redisgo
2 |
3 | yet another redis protocol codec
4 |
5 | COPY FROM [https://github.com/rokumoe/redisgo](https://github.com/rokumoe/redisgo)
6 |
7 | * fix decode issue
--------------------------------------------------------------------------------
/redis_cli/redisgo/decode.go:
--------------------------------------------------------------------------------
1 | package redisgo
2 |
3 | import (
4 | "bufio"
5 | "fmt"
6 | "io"
7 | "strconv"
8 | "unsafe"
9 | )
10 |
11 | const (
12 | maxBlukSize = 512 * 1024 * 1024
13 | )
14 |
15 | type Decoder struct {
16 | r *bufio.Reader
17 | }
18 |
19 | func (d *Decoder) readLine() ([]byte, error) {
20 | ln, err := d.r.ReadSlice('\n')
21 | if err != nil {
22 | return nil, err
23 | }
24 | if len(ln) < 2 || ln[len(ln)-2] != '\r' {
25 | return nil, fmt.Errorf("expect terminated with CRLF")
26 | }
27 | return ln[:len(ln)-2], nil
28 | }
29 |
30 | func (d *Decoder) Decode(r *Resp) error {
31 | ch, err := d.r.ReadByte()
32 | if err != nil {
33 | return err
34 | }
35 | switch RespKind(ch) {
36 | case SimpleKind, ErrorKind, IntegerKind:
37 | ln, err := d.readLine()
38 | if err != nil {
39 | return err
40 | }
41 | *r = Resp{
42 | Kind: RespKind(ch),
43 | Data: string(ln),
44 | }
45 | case BlukKind:
46 | ln, err := d.readLine()
47 | if err != nil {
48 | return err
49 | }
50 | n, err := strconv.Atoi(*(*string)(unsafe.Pointer(&ln)))
51 | if err != nil {
52 | return err
53 | }
54 | if n < -1 || n > maxBlukSize {
55 | return fmt.Errorf("invalid bluk length: %d", n)
56 | }
57 | if n == -1 {
58 | *r = Resp{
59 | Kind: RespKind(ch),
60 | Null: true,
61 | }
62 | } else {
63 | data := make([]byte, n+2)
64 | _, err := io.ReadFull(d.r, data)
65 | if err != nil {
66 | return err
67 | }
68 | if data[len(data)-2] != '\r' || data[len(data)-1] != '\n' {
69 | return fmt.Errorf("expect terminated with CRLF")
70 | }
71 | *r = Resp{
72 | Kind: RespKind(ch),
73 | Data: string(data[:len(data)-2]),
74 | }
75 | }
76 | case ArrayKind:
77 | ln, err := d.readLine()
78 | if err != nil {
79 | return err
80 | }
81 | n, err := strconv.Atoi(*(*string)(unsafe.Pointer(&ln)))
82 | if err != nil {
83 | return err
84 | }
85 | if n < 0 {
86 | return fmt.Errorf("invalid array length: %d", n)
87 | }
88 | array := make([]Resp, n)
89 | for i := 0; i < n; i++ {
90 | err = d.Decode(&array[i])
91 | if err != nil {
92 | return err
93 | }
94 | }
95 | *r = Resp{
96 | Kind: RespKind(ch),
97 | Array: array,
98 | }
99 | default:
100 | return fmt.Errorf("unrecognized kind: %c", ch)
101 | }
102 | return nil
103 | }
104 |
105 | func NewDecoder(r io.Reader, maxLineSize int) *Decoder {
106 | br, ok := r.(*bufio.Reader)
107 | if !ok {
108 | br = bufio.NewReaderSize(r, maxLineSize)
109 | }
110 | return &Decoder{
111 | r: br,
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/redis_cli/redisgo/decode_test.go:
--------------------------------------------------------------------------------
1 | package redisgo
2 |
3 | import (
4 | "strconv"
5 | "strings"
6 | "testing"
7 | )
8 |
9 | func TestDecoder_Decode(t *testing.T) {
10 | type args struct {
11 | p *Resp
12 | }
13 | tests := []struct {
14 | name string
15 | input string
16 | args args
17 | wantErr bool
18 | }{
19 | {"simple", "+OK\r\n", args{&Resp{}}, false},
20 | {"error", "-ERR unknown command 'foobar'\r\n", args{&Resp{}}, false},
21 | {"integer", ":1000\r\n", args{&Resp{}}, false},
22 | {"bluk", "$7\r\nfoo\nbar\r\n", args{&Resp{}}, false},
23 | {"array", "*3\r\n$3\r\nfoo\r\n$-1\r\n$3\r\nbar\r\n", args{&Resp{}}, false},
24 | {"array-in-array", "*2\r\n*3\r\n:1\r\n:2\r\n:3\r\n*2\r\n+Foo\r\n-Bar\r\n", args{&Resp{}}, false},
25 | }
26 | for _, tt := range tests {
27 | t.Run(tt.name, func(t *testing.T) {
28 | d := NewDecoder(strings.NewReader(tt.input), 1024)
29 | if err := d.Decode(tt.args.p); (err != nil) != tt.wantErr {
30 | t.Errorf("Decoder.Decode() error = %v, wantErr %v", err, tt.wantErr)
31 | } else {
32 | t.Logf("%s", strconv.Quote(tt.args.p.String()))
33 | }
34 | })
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/redis_cli/redisgo/encode.go:
--------------------------------------------------------------------------------
1 | package redisgo
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "strconv"
7 | "unsafe"
8 | )
9 |
10 | const (
11 | blukchar = '$'
12 | integerchar = ':'
13 | arraychar = '*'
14 | )
15 |
16 | var (
17 | crlf = []byte{'\r', '\n'}
18 | simplechar = []byte{'+'}
19 | errorchar = []byte{'-'}
20 | nullbluk = []byte("$-1\r\n")
21 | )
22 |
23 | type kind uint
24 |
25 | const (
26 | nullKind kind = iota
27 | simpleKind
28 | errorKind
29 | blukKind
30 | intKind
31 | int8Kind
32 | int16Kind
33 | int32Kind
34 | int64Kind
35 | uintKind
36 | uint8Kind
37 | uint16Kind
38 | uint32Kind
39 | uint64Kind
40 | )
41 |
42 | type Value struct {
43 | kind kind
44 | u64 uint64
45 | s string
46 | }
47 |
48 | func Null() Value {
49 | return Value{kind: nullKind}
50 | }
51 |
52 | func Simple(s string) Value {
53 | return Value{kind: simpleKind, s: s}
54 | }
55 |
56 | func Error(err string) Value {
57 | return Value{kind: errorKind, s: err}
58 | }
59 |
60 | func BlukString(s string) Value {
61 | return Value{kind: blukKind, s: s}
62 | }
63 |
64 | func Bluk(p []byte) Value {
65 | return BlukString(*(*string)(unsafe.Pointer(&p)))
66 | }
67 |
68 | func Int(v int) Value {
69 | return Value{kind: intKind, u64: uint64(v)}
70 | }
71 |
72 | func Int8(v int8) Value {
73 | return Value{kind: int8Kind, u64: uint64(v)}
74 | }
75 |
76 | func Int16(v int16) Value {
77 | return Value{kind: int16Kind, u64: uint64(v)}
78 | }
79 |
80 | func Int32(v int32) Value {
81 | return Value{kind: int32Kind, u64: uint64(v)}
82 | }
83 |
84 | func Int64(v int64) Value {
85 | return Value{kind: int64Kind, u64: uint64(v)}
86 | }
87 |
88 | func Uint(v uint) Value {
89 | return Value{kind: uintKind, u64: uint64(v)}
90 | }
91 |
92 | func Uint8(v uint8) Value {
93 | return Value{kind: uint8Kind, u64: uint64(v)}
94 | }
95 |
96 | func Uint16(v uint16) Value {
97 | return Value{kind: uint16Kind, u64: uint64(v)}
98 | }
99 |
100 | func Uint32(v uint32) Value {
101 | return Value{kind: uint32Kind, u64: uint64(v)}
102 | }
103 |
104 | func Uint64(v uint64) Value {
105 | return Value{kind: uint64Kind, u64: uint64(v)}
106 | }
107 |
108 | type slice struct {
109 | data uintptr
110 | n1 int
111 | n2 int
112 | }
113 |
114 | func sb(s string) []byte {
115 | b := slice{
116 | data: *(*uintptr)(unsafe.Pointer(&s)),
117 | n1: len(s),
118 | n2: len(s),
119 | }
120 | return *(*[]byte)(unsafe.Pointer(&b))
121 | }
122 |
123 | func Encode(w io.Writer, v Value) (err error) {
124 | var buf [32]byte
125 | b := crlf
126 | switch v.kind {
127 | case nullKind:
128 | b = nullbluk
129 | case simpleKind:
130 | _, err = w.Write(simplechar)
131 | if err != nil {
132 | return
133 | }
134 | _, err = w.Write(sb(v.s))
135 | if err != nil {
136 | return
137 | }
138 | case errorKind:
139 | _, err = w.Write(errorchar)
140 | if err != nil {
141 | return
142 | }
143 | _, err = w.Write(sb(v.s))
144 | if err != nil {
145 | return
146 | }
147 | case blukKind:
148 | t := append(buf[:0], blukchar)
149 | t = strconv.AppendInt(t, int64(len(v.s)), 10)
150 | t = append(t, '\r', '\n')
151 | _, err = w.Write(t)
152 | if err != nil {
153 | return
154 | }
155 | _, err = w.Write(sb(v.s))
156 | if err != nil {
157 | return
158 | }
159 | case intKind:
160 | b = append(buf[:0], integerchar)
161 | b = strconv.AppendInt(b, int64(int(v.u64)), 10)
162 | b = append(b, '\r', '\n')
163 | case int8Kind:
164 | b = append(buf[:0], integerchar)
165 | b = strconv.AppendInt(b, int64(int8(v.u64)), 10)
166 | b = append(b, '\r', '\n')
167 | case int16Kind:
168 | b = append(buf[:0], integerchar)
169 | b = strconv.AppendInt(b, int64(int16(v.u64)), 10)
170 | b = append(b, '\r', '\n')
171 | case int32Kind:
172 | b = append(buf[:0], integerchar)
173 | b = strconv.AppendInt(b, int64(int32(v.u64)), 10)
174 | b = append(b, '\r', '\n')
175 | case int64Kind:
176 | b = append(buf[:0], integerchar)
177 | b = strconv.AppendInt(b, int64(int64(v.u64)), 10)
178 | b = append(b, '\r', '\n')
179 | case uintKind:
180 | b = append(buf[:0], integerchar)
181 | b = strconv.AppendUint(b, uint64(uint(v.u64)), 10)
182 | b = append(b, '\r', '\n')
183 | case uint8Kind:
184 | b = append(buf[:0], integerchar)
185 | b = strconv.AppendUint(b, uint64(uint8(v.u64)), 10)
186 | b = append(b, '\r', '\n')
187 | case uint16Kind:
188 | b = append(buf[:0], integerchar)
189 | b = strconv.AppendUint(b, uint64(uint16(v.u64)), 10)
190 | b = append(b, '\r', '\n')
191 | case uint32Kind:
192 | b = append(buf[:0], integerchar)
193 | b = strconv.AppendUint(b, uint64(uint32(v.u64)), 10)
194 | b = append(b, '\r', '\n')
195 | case uint64Kind:
196 | b = append(buf[:0], integerchar)
197 | b = strconv.AppendUint(b, uint64(uint64(v.u64)), 10)
198 | b = append(b, '\r', '\n')
199 | default:
200 | panic("never get here")
201 | }
202 | _, err = w.Write(b)
203 | return
204 | }
205 |
206 | func EncodeMulti(w io.Writer, vals ...Value) error {
207 | var buf [32]byte
208 | t := append(buf[:0], arraychar)
209 | t = strconv.AppendInt(t, int64(len(vals)), 10)
210 | t = append(t, '\r', '\n')
211 | _, err := w.Write(t)
212 | if err != nil {
213 | return err
214 | }
215 | for _, v := range vals {
216 | err = Encode(w, v)
217 | if err != nil {
218 | return err
219 | }
220 | }
221 | return nil
222 | }
223 |
224 | func EncodeResp(w io.Writer, r *Resp) (err error) {
225 | var buf [32]byte
226 | b := crlf
227 | switch r.Kind {
228 | case SimpleKind, ErrorKind, IntegerKind:
229 | if 3+len(r.Data) <= 32 {
230 | t := append(buf[:0], byte(r.Kind))
231 | t = append(t, r.Data...)
232 | b = append(t, '\r', '\n')
233 | } else {
234 | _, err = w.Write([]byte{byte(r.Kind)})
235 | if err != nil {
236 | return
237 | }
238 | _, err = w.Write(sb(r.Data))
239 | if err != nil {
240 | return
241 | }
242 | }
243 | case BlukKind:
244 | if r.Null {
245 | b = nullbluk
246 | } else {
247 | t := append(buf[:0], '$')
248 | t = strconv.AppendInt(t, int64(len(r.Data)), 10)
249 | t = append(t, '\r', '\n')
250 | _, err = w.Write(t)
251 | if err != nil {
252 | return
253 | }
254 | _, err = w.Write(sb(r.Data))
255 | if err != nil {
256 | return
257 | }
258 | }
259 | case ArrayKind:
260 | t := append(buf[:0], '*')
261 | t = strconv.AppendInt(t, int64(len(r.Array)), 10)
262 | t = append(t, '\r', '\n')
263 | _, err = w.Write(t)
264 | if err != nil {
265 | return
266 | }
267 | for i := range r.Array {
268 | err = EncodeResp(w, &r.Array[i])
269 | if err != nil {
270 | return
271 | }
272 | }
273 | return
274 | default:
275 | return fmt.Errorf("unrecognized kind: %c", r.Kind)
276 | }
277 | _, err = w.Write(b)
278 | return
279 | }
280 |
--------------------------------------------------------------------------------
/redis_cli/redisgo/encode_test.go:
--------------------------------------------------------------------------------
1 | package redisgo
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 | )
7 |
8 | func TestEncode(t *testing.T) {
9 | type args struct {
10 | v Value
11 | }
12 | tests := []struct {
13 | name string
14 | args args
15 | wantW string
16 | wantErr bool
17 | }{
18 | {"null", args{Null()}, "$-1\r\n", false},
19 | {"simple", args{Simple("hello")}, "+hello\r\n", false},
20 | {"error", args{Error("ERR oops")}, "-ERR oops\r\n", false},
21 | {"bluk", args{Bluk([]byte("hello"))}, "$5\r\nhello\r\n", false},
22 | {"int", args{Int(233)}, ":233\r\n", false},
23 | }
24 | for _, tt := range tests {
25 | t.Run(tt.name, func(t *testing.T) {
26 | w := &bytes.Buffer{}
27 | if err := Encode(w, tt.args.v); (err != nil) != tt.wantErr {
28 | t.Errorf("Encode() error = %v, wantErr %v", err, tt.wantErr)
29 | return
30 | }
31 | if gotW := w.String(); gotW != tt.wantW {
32 | t.Errorf("Encode() = %v, want %v", gotW, tt.wantW)
33 | }
34 | })
35 | }
36 | }
37 |
38 | func TestEncodeMulti(t *testing.T) {
39 | type args struct {
40 | vals []Value
41 | }
42 | tests := []struct {
43 | name string
44 | args args
45 | wantW string
46 | wantErr bool
47 | }{
48 | {"multi", args{[]Value{Null(), Simple("hello"), Int(233)}}, "*3\r\n$-1\r\n+hello\r\n:233\r\n", false},
49 | }
50 | for _, tt := range tests {
51 | t.Run(tt.name, func(t *testing.T) {
52 | w := &bytes.Buffer{}
53 | if err := EncodeMulti(w, tt.args.vals...); (err != nil) != tt.wantErr {
54 | t.Errorf("EncodeMulti() error = %v, wantErr %v", err, tt.wantErr)
55 | return
56 | }
57 | if gotW := w.String(); gotW != tt.wantW {
58 | t.Errorf("EncodeMulti() = %v, want %v", gotW, tt.wantW)
59 | }
60 | })
61 | }
62 | }
63 |
64 | func TestEncodeResp(t *testing.T) {
65 | type args struct {
66 | r *Resp
67 | }
68 | tests := []struct {
69 | name string
70 | args args
71 | wantW string
72 | wantErr bool
73 | }{
74 | {"array-in-array", args{&Resp{
75 | Kind: ArrayKind,
76 | Array: []Resp{
77 | {
78 | Kind: ArrayKind,
79 | Array: []Resp{
80 | Resp{
81 | Kind: IntegerKind,
82 | Data: "1",
83 | },
84 | Resp{
85 | Kind: IntegerKind,
86 | Data: "2",
87 | },
88 | Resp{
89 | Kind: IntegerKind,
90 | Data: "3",
91 | },
92 | },
93 | },
94 | {
95 | Kind: ArrayKind,
96 | Array: []Resp{
97 | {
98 | Kind: SimpleKind,
99 | Data: "Foo",
100 | },
101 | {
102 | Kind: ErrorKind,
103 | Data: "Bar",
104 | },
105 | },
106 | },
107 | },
108 | }}, "*2\r\n*3\r\n:1\r\n:2\r\n:3\r\n*2\r\n+Foo\r\n-Bar\r\n", false},
109 | }
110 | for _, tt := range tests {
111 | t.Run(tt.name, func(t *testing.T) {
112 | w := &bytes.Buffer{}
113 | if err := EncodeResp(w, tt.args.r); (err != nil) != tt.wantErr {
114 | t.Errorf("EncodeResp() error = %v, wantErr %v", err, tt.wantErr)
115 | return
116 | }
117 | if gotW := w.String(); gotW != tt.wantW {
118 | t.Errorf("EncodeResp() = %v, want %v", gotW, tt.wantW)
119 | }
120 | })
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/redis_cli/redisgo/resp.go:
--------------------------------------------------------------------------------
1 | package redisgo
2 |
3 | import "bytes"
4 |
5 | type RespKind byte
6 |
7 | const (
8 | SimpleKind RespKind = '+'
9 | ErrorKind = '-'
10 | IntegerKind = ':'
11 | BlukKind = '$'
12 | ArrayKind = '*'
13 | )
14 |
15 | type Resp struct {
16 | Kind RespKind
17 | Null bool
18 | Data string
19 | Array []Resp
20 | }
21 |
22 | func (r *Resp) String() string {
23 | w := &bytes.Buffer{}
24 | EncodeResp(w, r)
25 | return w.String()
26 | }
27 |
--------------------------------------------------------------------------------
/tcp_server/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 the go-netty project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package main
18 |
19 | import (
20 | "encoding/binary"
21 | "fmt"
22 | "time"
23 |
24 | "github.com/go-netty/go-netty"
25 | "github.com/go-netty/go-netty/codec/format"
26 | "github.com/go-netty/go-netty/codec/frame"
27 | "github.com/go-netty/go-netty/utils"
28 | )
29 |
30 | func main() {
31 |
32 | // setup child pipeline initializer.
33 | childInitializer := func(channel netty.Channel) {
34 | channel.Pipeline().
35 | AddLast(frame.LengthFieldCodec(binary.LittleEndian, 1024, 0, 2, 0, 2)).
36 | AddLast(format.TextCodec()).
37 | AddLast(EchoHandler{"Server"})
38 | }
39 |
40 | // setup client pipeline initializer.
41 | clientInitializer := func(channel netty.Channel) {
42 | channel.Pipeline().
43 | AddLast(frame.LengthFieldCodec(binary.LittleEndian, 1024, 0, 2, 0, 2)).
44 | AddLast(format.TextCodec()).
45 | AddLast(EchoHandler{"Client"})
46 | }
47 |
48 | // new bootstrap
49 | var bootstrap = netty.NewBootstrap(netty.WithChildInitializer(childInitializer), netty.WithClientInitializer(clientInitializer))
50 |
51 | // connect to the server after 1 second
52 | time.AfterFunc(time.Second, func() {
53 | _, err := bootstrap.Connect("127.0.0.1:6565")
54 | utils.Assert(err)
55 | })
56 |
57 | // setup bootstrap & startup server.
58 | bootstrap.Listen("0.0.0.0:6565").Sync()
59 | }
60 |
61 | type EchoHandler struct {
62 | role string
63 | }
64 |
65 | func (l EchoHandler) HandleActive(ctx netty.ActiveContext) {
66 | fmt.Println(l.role, "->", "active:", ctx.Channel().RemoteAddr())
67 |
68 | ctx.Write("Hello I'm " + l.role)
69 | ctx.HandleActive()
70 | }
71 |
72 | func (l EchoHandler) HandleRead(ctx netty.InboundContext, message netty.Message) {
73 | fmt.Println(l.role, "->", "handle read:", message)
74 | ctx.HandleRead(message)
75 | }
76 |
77 | func (l EchoHandler) HandleInactive(ctx netty.InactiveContext, ex netty.Exception) {
78 | fmt.Println(l.role, "->", "inactive:", ctx.Channel().RemoteAddr(), ex)
79 | ctx.HandleInactive(ex)
80 | }
81 |
--------------------------------------------------------------------------------