├── .gitignore ├── .project ├── LICENSE ├── README.md └── src ├── agent ├── admin.go ├── agent.go ├── agent_test.go └── server.go └── main └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | /pkg/* 2 | /bin/* -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | http-agent-go 4 | 5 | 6 | 7 | 8 | 9 | com.googlecode.goclipse.goBuilder 10 | clean,full,incremental, 11 | 12 | 13 | 14 | 15 | 16 | com.googlecode.goclipse.core.goNature 17 | 18 | 19 | -------------------------------------------------------------------------------- /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 | # agent-go 2 | 3 | ## What's is agent-go? 4 | agent-go is an open source agent(proxy and adapter) that written in golang. 5 | 6 | ## Features 7 | - [X] READY 8 | - [ ] TODO 9 | 10 | ### agent features 11 | - [X] HTTP proxy(support GET PUT POST DELETE to target server) 12 | - [ ] HTTPS proxy(support GET PUT POST DELETE to target server) 13 | - [X] FTP adaptor(support GET POST DELETE to target server) 14 | - [ ] SFTP adaptor(support GET POST DELETE to target server) 15 | 16 | ### admin features 17 | - [ ] black target list(host:port) 18 | - [ ] white target list(host:port) 19 | - [ ] access list(host) 20 | - [ ] target key management 21 | - [ ] download log file 22 | - [ ] client and target mapping(host->host:port) 23 | 24 | ## Example 25 | First, put target method(m), target url(u) to agent server's url as query string. 26 | Then, write the body follow the target server's requirements. 27 | Finally, get the response from target server. 28 | 29 | ```go 30 | var m, u string 31 | 32 | u = "http://localhost:8001/test?a=1&b=2" 33 | m = base64.StdEncoding.EncodeToString([]byte("PUT")) 34 | u = base64.StdEncoding.EncodeToString([]byte(u)) 35 | client := &http.Client{} 36 | req, err := http.NewRequest(http.MethodPost, "http://localhost:8000/agent?m="+m+"&u="+u, strings.NewReader("PUT http://localhost:8001/test?a=1&b=2")) 37 | if nil != err { 38 | fmt.Printf("%v\n", err) 39 | return 40 | } 41 | resp, err := client.Do(req) 42 | if nil != err { 43 | fmt.Printf("%v\n", err) 44 | return 45 | } 46 | fmt.Println(resp.StatusCode == 200) //check the response status 47 | fmt.Printf("PUT:\n %v\n", resp) 48 | buf := new(bytes.Buffer) 49 | n, err := buf.ReadFrom(resp.Body) 50 | if nil != err { 51 | fmt.Printf("%v\n", err) 52 | return 53 | } 54 | fmt.Println(n) 55 | s := buf.String() 56 | fmt.Println(s) 57 | buf.Reset() 58 | 59 | 60 | m = base64.StdEncoding.EncodeToString([]byte(http.MethodGet)) 61 | u = "ftp://cme:CMEpassword1&@172.16.4.14:2121/a/admin.go" 62 | u = base64.StdEncoding.EncodeToString([]byte(u)) 63 | req, err = http.NewRequest(http.MethodPost, "http://localhost:8000/agent?m="+m+"&u="+u, nil) 64 | if nil != err { 65 | fmt.Printf("%v\n", err) 66 | return 67 | } 68 | resp, err = client.Do(req) 69 | if nil != err { 70 | fmt.Printf("%v\n", err) 71 | return 72 | } 73 | fmt.Println(resp.StatusCode == 200) //check the response status 74 | fmt.Printf("GET:\n %v\n", resp) 75 | buf = new(bytes.Buffer) 76 | n, err = buf.ReadFrom(resp.Body) 77 | if nil != err { 78 | fmt.Printf("%v\n", err) 79 | return 80 | } 81 | fmt.Println(n) 82 | s = buf.String() 83 | fmt.Println(s) 84 | buf.Reset() 85 | ``` 86 | 87 | -------------------------------------------------------------------------------- /src/agent/admin.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | func admin(w http.ResponseWriter, r *http.Request) { 9 | fmt.Fprintf(w, "%s %s %s\n", r.Host, r.Method, r.URL.Path) 10 | r.ParseForm() 11 | fmt.Fprintf(w, "%v\n", r.Form) 12 | } 13 | -------------------------------------------------------------------------------- /src/agent/agent.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "encoding/base64" 5 | "fmt" 6 | "github.com/jlaffaye/ftp" 7 | "io" 8 | "log" 9 | "net/http" 10 | "net/url" 11 | "strconv" 12 | "strings" 13 | ) 14 | 15 | const ( 16 | METHOD = "m" 17 | URL = "u" 18 | ) 19 | 20 | func agent(w http.ResponseWriter, r *http.Request) { 21 | 22 | if http.MethodPost != r.Method { 23 | log.Printf("accept wrong request %v\n only post was accepted", r) 24 | error := fmt.Sprintf("Only POST method was accepted but %s reveived", r.Method) 25 | http.Error(w, error, http.StatusMethodNotAllowed) 26 | return 27 | } 28 | 29 | if err := r.ParseForm(); nil != err { 30 | log.Printf("%v\n", err) 31 | error := fmt.Sprintf("%v\n", err) 32 | http.Error(w, error, http.StatusBadRequest) 33 | return 34 | } 35 | //m := r.Form.Get(METHOD) 36 | //u := r.Form.Get(URL) 37 | mbytes, err := base64.StdEncoding.DecodeString(r.Form.Get(METHOD)) 38 | if nil != err { 39 | log.Printf("%v\n", err) 40 | error := fmt.Sprintf("%v\n", err) 41 | http.Error(w, error, http.StatusBadRequest) 42 | return 43 | } 44 | ubytes, err := base64.StdEncoding.DecodeString(r.Form.Get(URL)) 45 | if nil != err { 46 | log.Printf("%v\n", err) 47 | error := fmt.Sprintf("%v\n", err) 48 | http.Error(w, error, http.StatusBadRequest) 49 | return 50 | } 51 | m := string(mbytes) 52 | u := string(ubytes) 53 | if !strings.EqualFold(m, "") && !strings.EqualFold(u, "") { 54 | switch { 55 | case (strings.EqualFold(http.MethodGet, m) || strings.EqualFold(http.MethodDelete, m)) && strings.HasPrefix(u, "http"): 56 | req, err := http.NewRequest(m, u, nil) 57 | if nil != err { 58 | log.Printf("%v\n", err) 59 | error := fmt.Sprintf("%v\n", err) 60 | http.Error(w, error, http.StatusBadRequest) 61 | return 62 | } 63 | client := &http.Client{} 64 | resp, err := client.Do(req) 65 | if nil != err { 66 | log.Printf("%v\n", err) 67 | error := fmt.Sprintf("%v\n", err) 68 | http.Error(w, error, http.StatusBadRequest) 69 | return 70 | } 71 | _, err = io.Copy(w, resp.Body) 72 | defer resp.Body.Close() 73 | if nil != err { 74 | log.Printf("%v\n", err) 75 | error := fmt.Sprintf("%v\n", err) 76 | http.Error(w, error, http.StatusBadRequest) 77 | } 78 | case (strings.EqualFold(http.MethodPut, m) || strings.EqualFold(http.MethodPost, m)) && strings.HasPrefix(u, "http"): 79 | req, err := http.NewRequest(m, u, r.Body) 80 | if nil != err { 81 | log.Printf("%v\n", err) 82 | error := fmt.Sprintf("%v\n", err) 83 | http.Error(w, error, http.StatusBadRequest) 84 | return 85 | } 86 | client := &http.Client{} 87 | resp, err := client.Do(req) 88 | if nil != err { 89 | log.Printf("%v\n", err) 90 | error := fmt.Sprintf("%v\n", err) 91 | http.Error(w, error, http.StatusBadRequest) 92 | return 93 | } 94 | _, err = io.Copy(w, resp.Body) 95 | defer resp.Body.Close() 96 | if nil != err { 97 | log.Printf("%v\n", err) 98 | error := fmt.Sprintf("%v\n", err) 99 | http.Error(w, error, http.StatusBadRequest) 100 | return 101 | } 102 | case strings.EqualFold(http.MethodGet, m) && strings.HasPrefix(u, "https"): 103 | case strings.EqualFold(http.MethodPut, m) && strings.HasPrefix(u, "https"): 104 | case strings.EqualFold(http.MethodPost, m) && strings.HasPrefix(u, "https"): 105 | case strings.EqualFold(http.MethodDelete, m) && strings.HasPrefix(u, "https"): 106 | case strings.EqualFold(http.MethodGet, m) && strings.HasPrefix(u, "ftp"): 107 | ftpUrl, err := url.Parse(u) 108 | if nil != err { 109 | log.Printf("%v\n", err) 110 | error := fmt.Sprintf("%v\n", err) 111 | http.Error(w, error, http.StatusBadRequest) 112 | return 113 | } 114 | log.Printf("%s %s %v\n", ftpUrl.Host, ftpUrl.Path, ftpUrl.User) 115 | server, err := ftp.Connect(ftpUrl.Host) 116 | defer server.Quit() 117 | if nil != err { 118 | log.Printf("%v\n", err) 119 | error := fmt.Sprintf("%v\n", err) 120 | http.Error(w, error, http.StatusBadRequest) 121 | return 122 | } 123 | var user, pass string 124 | if nil == ftpUrl.User { 125 | user = "anonymous" 126 | pass = "" 127 | } else { 128 | user = ftpUrl.User.Username() 129 | if strings.EqualFold(user, "") { 130 | user = "anonymous" 131 | } 132 | pass, _ = ftpUrl.User.Password() 133 | } 134 | 135 | err = server.Login(user, pass) 136 | defer server.Logout() 137 | if nil != err { 138 | log.Printf("%v\n", err) 139 | error := fmt.Sprintf("%v\n", err) 140 | http.Error(w, error, http.StatusBadRequest) 141 | return 142 | } 143 | 144 | resp, err := server.Retr(ftpUrl.Path) 145 | if nil != err { 146 | log.Printf("%v\n", err) 147 | error := fmt.Sprintf("%v\n", err) 148 | http.Error(w, error, http.StatusBadRequest) 149 | return 150 | } 151 | _, err = io.Copy(w, resp) 152 | defer resp.Close() 153 | if nil != err { 154 | log.Printf("%v\n", err) 155 | error := fmt.Sprintf("%v\n", err) 156 | http.Error(w, error, http.StatusBadRequest) 157 | return 158 | } 159 | 160 | case strings.EqualFold(http.MethodPut, m) && strings.HasPrefix(u, "ftp"): 161 | err = r.ParseMultipartForm(16 << 10) //16M 162 | if nil != err { 163 | log.Printf("%v\n", err) 164 | error := fmt.Sprintf("%v\n", err) 165 | http.Error(w, error, http.StatusBadRequest) 166 | return 167 | } 168 | if nil == r.MultipartForm { 169 | log.Println("Not a MultipartForm.\n") 170 | http.Error(w, "Not a MultipartForm.\n", http.StatusBadRequest) 171 | return 172 | } 173 | if nil == r.MultipartForm.File || len(r.MultipartForm.File) == 0 { 174 | log.Println("MultipartForm did not have file.\n") 175 | http.Error(w, "MultipartForm did not have file.\n", http.StatusBadRequest) 176 | return 177 | } 178 | 179 | ftpUrl, err := url.Parse(u) 180 | if nil != err { 181 | log.Printf("%v\n", err) 182 | error := fmt.Sprintf("%v\n", err) 183 | http.Error(w, error, http.StatusBadRequest) 184 | return 185 | } 186 | log.Printf("%s %s %v\n", ftpUrl.Host, ftpUrl.Path, ftpUrl.User) 187 | server, err := ftp.Connect(ftpUrl.Host) 188 | defer server.Quit() 189 | if nil != err { 190 | log.Printf("%v\n", err) 191 | error := fmt.Sprintf("%v\n", err) 192 | http.Error(w, error, http.StatusBadRequest) 193 | return 194 | } 195 | var user, pass string 196 | if nil == ftpUrl.User { 197 | user = "anonymous" 198 | pass = "" 199 | } else { 200 | user = ftpUrl.User.Username() 201 | if strings.EqualFold(user, "") { 202 | user = "anonymous" 203 | } 204 | pass, _ = ftpUrl.User.Password() 205 | } 206 | err = server.Login(user, pass) 207 | defer server.Logout() 208 | if nil != err { 209 | log.Printf("%v\n", err) 210 | error := fmt.Sprintf("%v\n", err) 211 | http.Error(w, error, http.StatusBadRequest) 212 | return 213 | } 214 | 215 | /* 216 | RFC7578 217 | For form data that represents the content of a file, a name for the 218 | file SHOULD be supplied as well, by using a "filename" parameter of 219 | the Content-Disposition header field. The file name isn't mandatory 220 | for cases where the file name isn't available or is meaningless or 221 | private; this might result, for example, when selection or drag-and- 222 | drop is used or when the form data content is streamed directly from 223 | a device. 224 | */ 225 | for k, v := range r.MultipartForm.File { 226 | for i := 0; i < len(v); i++ { 227 | f, err := v[i].Open() 228 | if nil != err { 229 | log.Printf("%v\n", err) 230 | error := fmt.Sprintf("%v\n", err) 231 | http.Error(w, error, http.StatusBadRequest) 232 | return 233 | } 234 | var filepath = v[i].Filename 235 | if strings.EqualFold(filepath, "") { 236 | filepath = k + strconv.Itoa(i) 237 | } 238 | if !strings.EqualFold(ftpUrl.Path, "") { 239 | filepath = ftpUrl.Path + "/" + filepath 240 | } 241 | err = server.Stor(filepath, f) 242 | if nil != err { 243 | log.Printf("%v\n", err) 244 | error := fmt.Sprintf("%v\n", err) 245 | http.Error(w, error, http.StatusBadRequest) 246 | return 247 | } 248 | } 249 | } 250 | 251 | case strings.EqualFold(http.MethodDelete, m) && strings.HasPrefix(u, "ftp"): 252 | ftpUrl, err := url.Parse(u) 253 | if nil != err { 254 | log.Printf("%v\n", err) 255 | error := fmt.Sprintf("%v\n", err) 256 | http.Error(w, error, http.StatusBadRequest) 257 | return 258 | } 259 | // log.Printf("%s %s %v\n", ftpUrl.Host, ftpUrl.Path, ftpUrl.User) 260 | server, err := ftp.Connect(ftpUrl.Host) 261 | defer server.Quit() 262 | if nil != err { 263 | log.Printf("%v\n", err) 264 | error := fmt.Sprintf("%v\n", err) 265 | http.Error(w, error, http.StatusBadRequest) 266 | return 267 | } 268 | var user, pass string 269 | if nil == ftpUrl.User { 270 | user = "anonymous" 271 | pass = "" 272 | } else { 273 | user = ftpUrl.User.Username() 274 | if strings.EqualFold(user, "") { 275 | user = "anonymous" 276 | } 277 | pass, _ = ftpUrl.User.Password() 278 | } 279 | err = server.Login(user, pass) 280 | defer server.Logout() 281 | if nil != err { 282 | log.Printf("%v\n", err) 283 | error := fmt.Sprintf("%v\n", err) 284 | http.Error(w, error, http.StatusBadRequest) 285 | return 286 | } 287 | err = server.Delete(ftpUrl.Path) 288 | if nil != err { 289 | log.Printf("%v\n", err) 290 | error := fmt.Sprintf("%v\n", err) 291 | http.Error(w, error, http.StatusBadRequest) 292 | return 293 | } 294 | case strings.EqualFold(http.MethodGet, m) && strings.HasPrefix(u, "ftps"): 295 | case strings.EqualFold(http.MethodPut, m) && strings.HasPrefix(u, "ftps"): 296 | case strings.EqualFold(http.MethodDelete, m) && strings.HasPrefix(u, "ftps"): 297 | default: 298 | log.Printf("%v\n", err) 299 | error := fmt.Sprintf("%v\n", err) 300 | http.Error(w, error, http.StatusBadRequest) 301 | } 302 | } else { 303 | log.Printf("%v\n", err) 304 | error := fmt.Sprintf("%v\n", err) 305 | http.Error(w, error, http.StatusBadRequest) 306 | } 307 | return 308 | // ftp, err := ftp.Connect("ftp://192.168.1.1:21") 309 | // if nil != err { 310 | // log.Printf("%v\n", err) 311 | // fmt.Fprintf(w, "%v\n", err) 312 | // } 313 | // ftp.RetrFrom(" ", 0) 314 | } 315 | 316 | //func forward(m, u string, w http.ResponseWriter, body io.ReadCloser) { 317 | // resp, err := http.NewRequest(m, u, nil) 318 | // if nil != err { 319 | // log.Printf("%v\n", err) 320 | // error := fmt.Sprintf("%v\n", err) 321 | // http.Error(w, error, StatusBadRequest) 322 | // return 323 | // } 324 | // client := &http.Client{} 325 | // resp, err := client.Do(req) 326 | // if nil != err { 327 | // log.Printf("%v\n", err) 328 | // error := fmt.Sprintf("%v\n", err) 329 | // http.Error(w, error, StatusBadRequest) 330 | // return 331 | // } 332 | // _, err = io.Copy(w, resp.Body) 333 | // if nil != err { 334 | // log.Printf("%v\n", err) 335 | // error := fmt.Sprintf("%v\n", err) 336 | // http.Error(w, error, StatusBadRequest) 337 | // } 338 | //} 339 | // 340 | //func adapt(m, u string, w io.Writer, r io.Reader) { 341 | // 342 | //} 343 | -------------------------------------------------------------------------------- /src/agent/agent_test.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "fmt" 7 | "io" 8 | "mime/multipart" 9 | "net/http" 10 | "os" 11 | "strings" 12 | "testing" 13 | ) 14 | 15 | func Test_agent(t *testing.T) { 16 | var m, u string 17 | 18 | u = "http://localhost:8001/test?a=1&b=2" 19 | m = base64.StdEncoding.EncodeToString([]byte(http.MethodPut)) 20 | u = base64.StdEncoding.EncodeToString([]byte(u)) 21 | fmt.Println(m) 22 | fmt.Println(u) 23 | client := &http.Client{} 24 | req, err := http.NewRequest(http.MethodPost, "http://localhost:8000/agent?m="+m+"&u="+u, strings.NewReader("methodPut")) 25 | if nil != err { 26 | fmt.Printf("%v\n", err) 27 | return 28 | } 29 | resp, err := client.Do(req) 30 | if nil != err { 31 | fmt.Printf("%v\n", err) 32 | return 33 | } 34 | fmt.Println(resp.StatusCode == 200) //check the response status 35 | fmt.Printf("PUT:\n %v\n", resp) 36 | buf := new(bytes.Buffer) 37 | n, err := buf.ReadFrom(resp.Body) 38 | if nil != err { 39 | fmt.Printf("%v\n", err) 40 | return 41 | } 42 | fmt.Println(n) 43 | s := buf.String() 44 | fmt.Println(s) 45 | buf.Reset() 46 | 47 | m = base64.StdEncoding.EncodeToString([]byte(http.MethodPost)) 48 | req, err = http.NewRequest(http.MethodPost, "http://localhost:8000/agent?m="+m+"&u="+u, strings.NewReader("methodPost")) 49 | if nil != err { 50 | fmt.Printf("%v\n", err) 51 | return 52 | } 53 | resp, err = client.Do(req) 54 | if nil != err { 55 | fmt.Printf("%v\n", err) 56 | return 57 | } 58 | fmt.Println(resp.StatusCode == 200) //check the response status 59 | fmt.Printf("POST:\n %v\n", resp) 60 | buf = new(bytes.Buffer) 61 | n, err = buf.ReadFrom(resp.Body) 62 | if nil != err { 63 | fmt.Printf("%v\n", err) 64 | return 65 | } 66 | fmt.Println(n) 67 | s = buf.String() 68 | fmt.Println(s) 69 | buf.Reset() 70 | 71 | m = base64.StdEncoding.EncodeToString([]byte(http.MethodGet)) 72 | req, err = http.NewRequest(http.MethodPost, "http://localhost:8000/agent?m="+m+"&u="+u, nil) 73 | if nil != err { 74 | fmt.Printf("%v\n", err) 75 | return 76 | } 77 | resp, err = client.Do(req) 78 | if nil != err { 79 | fmt.Printf("%v\n", err) 80 | return 81 | } 82 | fmt.Println(resp.StatusCode == 200) //check the response status 83 | fmt.Printf("GET:\n %v\n", resp) 84 | buf = new(bytes.Buffer) 85 | n, err = buf.ReadFrom(resp.Body) 86 | if nil != err { 87 | fmt.Printf("%v\n", err) 88 | return 89 | } 90 | fmt.Println(n) 91 | s = buf.String() 92 | fmt.Println(s) 93 | buf.Reset() 94 | 95 | m = base64.StdEncoding.EncodeToString([]byte(http.MethodDelete)) 96 | req, err = http.NewRequest(http.MethodPost, "http://localhost:8000/agent?m="+m+"&u="+u, nil) 97 | if nil != err { 98 | fmt.Printf("%v\n", err) 99 | return 100 | } 101 | resp, err = client.Do(req) 102 | if nil != err { 103 | fmt.Printf("%v\n", err) 104 | return 105 | } 106 | fmt.Println(resp.StatusCode == 200) //check the response status 107 | fmt.Printf("DELETE:\n %v\n", resp) 108 | buf = new(bytes.Buffer) 109 | n, err = buf.ReadFrom(resp.Body) 110 | if nil != err { 111 | fmt.Printf("%v\n", err) 112 | return 113 | } 114 | fmt.Println(n) 115 | s = buf.String() 116 | fmt.Println(s) 117 | buf.Reset() 118 | 119 | m = base64.StdEncoding.EncodeToString([]byte(http.MethodGet)) 120 | u = "ftp://cme:CMEpassword1&@172.16.4.14:2121/a/admin.go" 121 | u = base64.StdEncoding.EncodeToString([]byte(u)) 122 | req, err = http.NewRequest(http.MethodPost, "http://localhost:8000/agent?m="+m+"&u="+u, nil) 123 | if nil != err { 124 | fmt.Printf("%v\n", err) 125 | return 126 | } 127 | resp, err = client.Do(req) 128 | if nil != err { 129 | fmt.Printf("%v\n", err) 130 | return 131 | } 132 | fmt.Println(resp.StatusCode == 200) //check the response status 133 | fmt.Printf("GET:\n %v\n", resp) 134 | buf = new(bytes.Buffer) 135 | n, err = buf.ReadFrom(resp.Body) 136 | if nil != err { 137 | fmt.Printf("%v\n", err) 138 | return 139 | } 140 | fmt.Println(n) 141 | s = buf.String() 142 | fmt.Println(s) 143 | buf.Reset() 144 | 145 | m = base64.StdEncoding.EncodeToString([]byte(http.MethodDelete)) 146 | u = "ftp://cme:CMEpassword1&@172.16.4.14:2121/a/admin.go" 147 | u = base64.StdEncoding.EncodeToString([]byte(u)) 148 | req, err = http.NewRequest(http.MethodPost, "http://localhost:8000/agent?m="+m+"&u="+u, nil) 149 | if nil != err { 150 | fmt.Printf("%v\n", err) 151 | return 152 | } 153 | resp, err = client.Do(req) 154 | if nil != err { 155 | fmt.Printf("%v\n", err) 156 | return 157 | } 158 | fmt.Println(resp.StatusCode == 200) //check the response status 159 | fmt.Printf("DELETE:\n %v\n", resp) 160 | buf = new(bytes.Buffer) 161 | n, err = buf.ReadFrom(resp.Body) 162 | if nil != err { 163 | fmt.Printf("%v\n", err) 164 | return 165 | } 166 | fmt.Println(n) 167 | s = buf.String() 168 | fmt.Println(s) 169 | buf.Reset() 170 | 171 | m = base64.StdEncoding.EncodeToString([]byte(http.MethodPut)) 172 | u = "ftp://cme:CMEpassword1&@172.16.4.14:2121/a/" 173 | u = base64.StdEncoding.EncodeToString([]byte(u)) 174 | 175 | // Create buffer 176 | buf = new(bytes.Buffer) // caveat IMO dont use this for large files, \ 177 | // create a tmpfile and assemble your multipart from there (not tested) 178 | mw := multipart.NewWriter(buf) 179 | // Create file field 180 | fw, err := mw.CreateFormFile("file", "admin.go") //这里的file很重要,必须和服务器端的FormFile一致 181 | if err != nil { 182 | fmt.Printf("%v\n", err) 183 | return 184 | } 185 | fd, err := os.Open("admin.go") 186 | if err != nil { 187 | fmt.Printf("%v\n", err) 188 | return 189 | } 190 | defer fd.Close() 191 | // Write file field from file to upload 192 | _, err = io.Copy(fw, fd) 193 | if err != nil { 194 | fmt.Printf("%v\n", err) 195 | return 196 | } 197 | 198 | // Create file field 199 | fw, err = mw.CreateFormFile("file", "agent.go") //这里的file很重要,必须和服务器端的FormFile一致 200 | if err != nil { 201 | fmt.Printf("%v\n", err) 202 | return 203 | } 204 | fd, err = os.Open("agent.go") 205 | if err != nil { 206 | fmt.Printf("%v\n", err) 207 | return 208 | } 209 | defer fd.Close() 210 | // Write file field from file to upload 211 | _, err = io.Copy(fw, fd) 212 | if err != nil { 213 | fmt.Printf("%v\n", err) 214 | return 215 | } 216 | 217 | // Important if you do not close the multipart writer you will not have a 218 | // terminating boundry 219 | mw.Close() 220 | //req, err := http.NewRequest("POST","http://192.168.2.127/configure.go?portId=2", buf) 221 | req, err = http.NewRequest(http.MethodPost, "http://localhost:8000/agent?m="+m+"&u="+u, buf) 222 | if nil != err { 223 | fmt.Printf("%v\n", err) 224 | return 225 | } 226 | req.Header.Set("Content-Type", mw.FormDataContentType()) 227 | resp, err = client.Do(req) 228 | if nil != err { 229 | fmt.Printf("%v\n", err) 230 | return 231 | } 232 | fmt.Println(resp.StatusCode == 200) //check the response status 233 | fmt.Printf("POST:\n %v\n", resp) 234 | buf = new(bytes.Buffer) 235 | n, err = buf.ReadFrom(resp.Body) 236 | if nil != err { 237 | fmt.Printf("%v\n", err) 238 | return 239 | } 240 | fmt.Println(n) 241 | if nil != err { 242 | fmt.Printf("%v\n", err) 243 | return 244 | } 245 | fmt.Println(n) 246 | s = buf.String() 247 | fmt.Println(s) 248 | buf.Reset() 249 | 250 | } 251 | -------------------------------------------------------------------------------- /src/agent/server.go: -------------------------------------------------------------------------------- 1 | package agent 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | ) 7 | 8 | func StartServer() { 9 | http.HandleFunc("/agent", agent) 10 | http.HandleFunc("/admin", admin) 11 | log.Fatalln(http.ListenAndServe(":8000", nil)) 12 | } 13 | -------------------------------------------------------------------------------- /src/main/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "agent" 5 | ) 6 | 7 | func main() { 8 | agent.StartServer() 9 | } 10 | --------------------------------------------------------------------------------