[GitHub] [apisix-dashboard] Jaycean commented on a change in pull request #1465: feat(be): Online debug function adds support for transfer files
Jaycean commented on a change in pull request #1465: URL: https://github.com/apache/apisix-dashboard/pull/1465#discussion_r582692452 ## File path: api/test/e2enew/route_online_debug/route_online_debug_test.go ## @@ -0,0 +1,785 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package route_online_debug + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "path/filepath" + + "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/extensions/table" + "github.com/stretchr/testify/assert" + "github.com/tidwall/gjson" + + "e2enew/base" +) + +var _ = ginkgo.Describe("Route_Online_Debug", func() { + table.DescribeTable("Route_Online_Debug_Route_Not_Exist", + func(tc base.HttpTestCase) { + base.RunTestCase(tc) + }, + table.Entry("hit route that not exist", base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello_", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + }), + table.Entry("online debug route with query params", base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPost, + Path: "/apisix/admin/debug-request-forwarding", + Headers: map[string]string{ + "Authorization": base.GetToken(), + "online_debug_url": base.APISIXInternalUrl + `/hello_`, + "online_debug_request_protocol": "http", + "online_debug_method": "GET", + "Content-Type": "multipart/form-data", + "online_debug_header_params": `{"test":["test1"]}`, + }, + ExpectStatus: http.StatusOK, + ExpectBody: `{"code":404,"message":"404 Not Found","data":{"error_msg":"404 Route Not Found"}}`, + Sleep:base.SleepTime, + }), + ) + + table.DescribeTable("Route_Online_Debug_Route_With_Query_Params", + func(tc base.HttpTestCase) { + base.RunTestCase(tc) + }, + table.Entry("hit route that not exist", base.HttpTestCase{ + Object: base.APISIXExpect(), + Method: http.MethodGet, + Path: "/hello", + ExpectStatus: http.StatusNotFound, + ExpectBody: "{\"error_msg\":\"404 Route Not Found\"}\n", + }), + table.Entry("create route with query params", base.HttpTestCase{ + Object: base.ManagerApiExpect(), + Method: http.MethodPut, + Path: "/apisix/admin/routes/r1", + Body: `{ + "uri": "/hello", + "methods": ["GET"], + "vars": [ + ["arg_name","==","aaa"] + ], + "upstream": { + "type": "roundrobin", + "nodes": [{ + "host": "172.16.238.20", + "port": 1980, + "weight": 1 + }] + } + }`, + Headers: map[string]string{"Authorization": base.GetToken()}, + ExpectStatus: http.StatusOK, + }), + table.Entry("online debug route with query params", base.HttpTestCase{ + Object:
[GitHub] [apisix-dashboard] Jaycean commented on a change in pull request #1465: feat(be): Online debug function adds support for transfer files
Jaycean commented on a change in pull request #1465: URL: https://github.com/apache/apisix-dashboard/pull/1465#discussion_r580827765 ## File path: api/test/e2enew/base/http.go ## @@ -92,3 +104,44 @@ func BatchTestServerPort(times int) map[string]int { return res } + +func GetReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string, error) { + if strings.Index(contentType, "json") > -1 { + bytesData, _ := json.Marshal(reqParams) + return bytes.NewReader(bytesData), contentType, nil + } + if files != nil { + body := {} + writer := multipart.NewWriter(body) + for _, uploadFile := range files { + file, err := os.Open(uploadFile.Filepath) + if err != nil { + return nil, "", err + } + part, err := writer.CreateFormFile(uploadFile.Name, filepath.Base(uploadFile.Filepath)) + if err != nil { + return nil, "", err + } + _, err = io.Copy(part, file) + defer file.Close() + } + for k, v := range reqParams { + if err := writer.WriteField(k, v); err != nil { + return nil, "", err + } + } + if err := writer.Close(); err != nil { + return nil, "", err + } + return body, writer.FormDataContentType(), nil + } + + urlValues := url.Values{} + for key, val := range reqParams { + urlValues.Set(key, val) + } + + reqBody := urlValues.Encode() + + return strings.NewReader(reqBody), contentType, nil Review comment: @liuxiran Because the front-end has cross-domain problems, it needs to conduct a transparent transmission through the manage API. Here, the front-end places the file bytes in the body and transfers them to the back-end, and then directly transmits them through the back-end. Therefore, the E2E test needs to read the file here. This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org
[GitHub] [apisix-dashboard] Jaycean commented on a change in pull request #1465: feat(be): Online debug function adds support for transfer files
Jaycean commented on a change in pull request #1465: URL: https://github.com/apache/apisix-dashboard/pull/1465#discussion_r580806605 ## File path: api/test/e2enew/base/http.go ## @@ -92,3 +104,44 @@ func BatchTestServerPort(times int) map[string]int { return res } + +func GetReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string, error) { + if strings.Index(contentType, "json") > -1 { + bytesData, _ := json.Marshal(reqParams) + return bytes.NewReader(bytesData), contentType, nil + } + if files != nil { + body := {} + writer := multipart.NewWriter(body) + for _, uploadFile := range files { + file, err := os.Open(uploadFile.Filepath) + if err != nil { + return nil, "", err + } + part, err := writer.CreateFormFile(uploadFile.Name, filepath.Base(uploadFile.Filepath)) + if err != nil { + return nil, "", err + } + _, err = io.Copy(part, file) + defer file.Close() Review comment: Yes, this is my negligence. Done. Thks. This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org
[GitHub] [apisix-dashboard] Jaycean commented on a change in pull request #1465: feat(be): Online debug function adds support for transfer files
Jaycean commented on a change in pull request #1465: URL: https://github.com/apache/apisix-dashboard/pull/1465#discussion_r580805498 ## File path: api/test/e2enew/base/http.go ## @@ -92,3 +104,44 @@ func BatchTestServerPort(times int) map[string]int { return res } + +func GetReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string, error) { + if strings.Index(contentType, "json") > -1 { + bytesData, _ := json.Marshal(reqParams) + return bytes.NewReader(bytesData), contentType, nil + } + if files != nil { + body := {} + writer := multipart.NewWriter(body) + for _, uploadFile := range files { + file, err := os.Open(uploadFile.Filepath) + if err != nil { + return nil, "", err + } + part, err := writer.CreateFormFile(uploadFile.Name, filepath.Base(uploadFile.Filepath)) + if err != nil { + return nil, "", err + } + _, err = io.Copy(part, file) + defer file.Close() + } + for k, v := range reqParams { + if err := writer.WriteField(k, v); err != nil { + return nil, "", err + } + } + if err := writer.Close(); err != nil { + return nil, "", err + } + return body, writer.FormDataContentType(), nil + } + + urlValues := url.Values{} + for key, val := range reqParams { + urlValues.Set(key, val) + } + + reqBody := urlValues.Encode() + + return strings.NewReader(reqBody), contentType, nil Review comment: http.go In the getReader function, It from the old E2E/ http.go. My idea is that E2E will be rewritten with the new ginkgo, so I migrate it directly here without modifying the code. My understanding here is to deal with a variety of data formats, such as form-data, JSON, files. The incoming params may be in a variety of situations. Here is the third one url.Values set reqParams map[string]string This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org
[GitHub] [apisix-dashboard] Jaycean commented on a change in pull request #1465: feat(be): Online debug function adds support for transfer files
Jaycean commented on a change in pull request #1465: URL: https://github.com/apache/apisix-dashboard/pull/1465#discussion_r580799536 ## File path: api/test/e2enew/base/http.go ## @@ -92,3 +104,44 @@ func BatchTestServerPort(times int) map[string]int { return res } + +func GetReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string, error) { + if strings.Index(contentType, "json") > -1 { + bytesData, _ := json.Marshal(reqParams) + return bytes.NewReader(bytesData), contentType, nil + } + if files != nil { + body := {} + writer := multipart.NewWriter(body) + for _, uploadFile := range files { + file, err := os.Open(uploadFile.Filepath) + if err != nil { + return nil, "", err + } + part, err := writer.CreateFormFile(uploadFile.Name, filepath.Base(uploadFile.Filepath)) + if err != nil { + return nil, "", err + } + _, err = io.Copy(part, file) + defer file.Close() + } + for k, v := range reqParams { + if err := writer.WriteField(k, v); err != nil { + return nil, "", err + } + } + if err := writer.Close(); err != nil { + return nil, "", err + } + return body, writer.FormDataContentType(), nil + } + + urlValues := url.Values{} + for key, val := range reqParams { + urlValues.Set(key, val) + } + + reqBody := urlValues.Encode() + + return strings.NewReader(reqBody), contentType, nil Review comment: Yes, because the sending file is added, the data type in the body is bytes. If it is put in the body, it is not convenient to read. So I communicated with @liuxiran at that time, so it can only put other parameters in the header. Do you have a better suggestion? This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org
[GitHub] [apisix-dashboard] Jaycean commented on a change in pull request #1465: feat(be): Online debug function adds support for transfer files
Jaycean commented on a change in pull request #1465: URL: https://github.com/apache/apisix-dashboard/pull/1465#discussion_r580786927 ## File path: api/test/e2enew/base/http.go ## @@ -92,3 +104,44 @@ func BatchTestServerPort(times int) map[string]int { return res } + +func GetReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string) { + if strings.Index(contentType, "json") > -1 { + bytesData, _ := json.Marshal(reqParams) + return bytes.NewReader(bytesData), contentType + } + if files != nil { + body := {} + writer := multipart.NewWriter(body) + for _, uploadFile := range files { + file, err := os.Open(uploadFile.Filepath) + if err != nil { + panic(err) Review comment: done. This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org
[GitHub] [apisix-dashboard] Jaycean commented on a change in pull request #1465: feat(be): Online debug function adds support for transfer files
Jaycean commented on a change in pull request #1465: URL: https://github.com/apache/apisix-dashboard/pull/1465#discussion_r580708673 ## File path: api/test/e2enew/base/http.go ## @@ -92,3 +104,44 @@ func BatchTestServerPort(times int) map[string]int { return res } + +func GetReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string) { + if strings.Index(contentType, "json") > -1 { + bytesData, _ := json.Marshal(reqParams) + return bytes.NewReader(bytesData), contentType + } + if files != nil { + body := {} + writer := multipart.NewWriter(body) + for _, uploadFile := range files { + file, err := os.Open(uploadFile.Filepath) + if err != nil { + panic(err) + } + part, err := writer.CreateFormFile(uploadFile.Name, filepath.Base(uploadFile.Filepath)) + if err != nil { + panic(err) Review comment: done. ## File path: api/test/e2enew/base/http.go ## @@ -92,3 +104,44 @@ func BatchTestServerPort(times int) map[string]int { return res } + +func GetReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string) { + if strings.Index(contentType, "json") > -1 { + bytesData, _ := json.Marshal(reqParams) + return bytes.NewReader(bytesData), contentType + } + if files != nil { + body := {} + writer := multipart.NewWriter(body) + for _, uploadFile := range files { + file, err := os.Open(uploadFile.Filepath) + if err != nil { + panic(err) + } + part, err := writer.CreateFormFile(uploadFile.Name, filepath.Base(uploadFile.Filepath)) + if err != nil { + panic(err) + } + _, err = io.Copy(part, file) + file.Close() Review comment: done. ## File path: api/test/e2enew/base/http.go ## @@ -92,3 +104,44 @@ func BatchTestServerPort(times int) map[string]int { return res } + +func GetReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string) { + if strings.Index(contentType, "json") > -1 { + bytesData, _ := json.Marshal(reqParams) + return bytes.NewReader(bytesData), contentType + } + if files != nil { + body := {} + writer := multipart.NewWriter(body) + for _, uploadFile := range files { + file, err := os.Open(uploadFile.Filepath) + if err != nil { + panic(err) + } + part, err := writer.CreateFormFile(uploadFile.Name, filepath.Base(uploadFile.Filepath)) + if err != nil { + panic(err) + } + _, err = io.Copy(part, file) + file.Close() + } + for k, v := range reqParams { + if err := writer.WriteField(k, v); err != nil { + panic(err) Review comment: done. ## File path: api/test/e2enew/base/http.go ## @@ -92,3 +104,44 @@ func BatchTestServerPort(times int) map[string]int { return res } + +func GetReader(reqParams map[string]string, contentType string, files []UploadFile) (io.Reader, string) { + if strings.Index(contentType, "json") > -1 { + bytesData, _ := json.Marshal(reqParams) + return bytes.NewReader(bytesData), contentType + } + if files != nil { + body := {} + writer := multipart.NewWriter(body) + for _, uploadFile := range files { + file, err := os.Open(uploadFile.Filepath) + if err != nil { + panic(err) + } + part, err := writer.CreateFormFile(uploadFile.Name, filepath.Base(uploadFile.Filepath)) + if err != nil { + panic(err) + } + _, err = io.Copy(part, file) + file.Close() + } + for k, v := range reqParams { + if err := writer.WriteField(k, v); err != nil { + panic(err) + } + } + if err := writer.Close(); err != nil { + panic(err) Review
[GitHub] [apisix-dashboard] Jaycean commented on a change in pull request #1465: feat(be): Online debug function adds support for transfer files
Jaycean commented on a change in pull request #1465: URL: https://github.com/apache/apisix-dashboard/pull/1465#discussion_r579756889 ## File path: api/internal/handler/route_online_debug/route_online_debug.go ## @@ -63,66 +66,87 @@ type Result struct { Datainterface{} `json:"data,omitempty"` } -func DebugRequestForwarding(c droplet.Context) (interface{}, error) { +func (h *Handler) DebugRequestForwarding(c droplet.Context) (interface{}, error) { //TODO: other Protocols, e.g: grpc, websocket - paramsInput := c.Input().(*ParamsInput) - requestProtocol := paramsInput.RequestProtocol - if requestProtocol == "" { - requestProtocol = "http" + input := c.Input().(*DebugOnlineInput) + protocol := input.RequestProtocol + if protocol == "" { + protocol = "http" } protocolMap := make(map[string]ProtocolSupport) protocolMap["http"] = {} protocolMap["https"] = {} - if v, ok := protocolMap[requestProtocol]; ok { - return v.RequestForwarding(c) - } else { - return {StatusCode: http.StatusBadRequest}, - fmt.Errorf("Protocol unsupported %s, only http or https is allowed, but given %s", paramsInput.RequestProtocol, paramsInput.RequestProtocol) + if v, ok := protocolMap[protocol]; ok { + ret, err := v.RequestForwarding(c) + return ret, err } + + return {StatusCode: http.StatusBadRequest}, fmt.Errorf("protocol unspported %s", protocol) } type HTTPProtocolSupport struct { } func (h *HTTPProtocolSupport) RequestForwarding(c droplet.Context) (interface{}, error) { - paramsInput := c.Input().(*ParamsInput) - bodyParams := paramsInput.BodyParams - client := {} + input := c.Input().(*DebugOnlineInput) + url := input.URL + method := input.Method + body := input.Body + contentType := input.ContentType + + if url == "" || method == "" { + return {StatusCode: http.StatusBadRequest}, fmt.Errorf("parameters error") + } + client := {} client.Timeout = 5 * time.Second - req, err := http.NewRequest(strings.ToUpper(paramsInput.Method), paramsInput.URL, strings.NewReader(bodyParams)) + + var tempMap map[string][]string + err := json.Unmarshal([]byte(input.HeaderParams), ) + + if err != nil { + return {StatusCode: http.StatusInternalServerError}, err + } + + req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewReader(body)) if err != nil { return {StatusCode: http.StatusInternalServerError}, err } - for k, v := range paramsInput.HeaderParams { + + req.Header.Add("Content-Type", contentType) + for k, v := range tempMap { for _, v1 := range v { req.Header.Add(k, v1) } } + resp, err := client.Do(req) if err != nil { return {StatusCode: http.StatusInternalServerError}, err } - defer resp.Body.Close() + defer func() { + if resp != nil { + resp.Body.Close() + } + }() Review comment: done. This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org
[GitHub] [apisix-dashboard] Jaycean commented on a change in pull request #1465: feat(be): Online debug function adds support for transfer files
Jaycean commented on a change in pull request #1465: URL: https://github.com/apache/apisix-dashboard/pull/1465#discussion_r578541968 ## File path: api/internal/handler/route_online_debug/route_online_debug.go ## @@ -63,66 +66,87 @@ type Result struct { Datainterface{} `json:"data,omitempty"` } -func DebugRequestForwarding(c droplet.Context) (interface{}, error) { +func (h *Handler) DebugRequestForwarding(c droplet.Context) (interface{}, error) { //TODO: other Protocols, e.g: grpc, websocket - paramsInput := c.Input().(*ParamsInput) - requestProtocol := paramsInput.RequestProtocol - if requestProtocol == "" { - requestProtocol = "http" + input := c.Input().(*DebugOnlineInput) + protocol := input.RequestProtocol + if protocol == "" { + protocol = "http" } protocolMap := make(map[string]ProtocolSupport) protocolMap["http"] = {} protocolMap["https"] = {} - if v, ok := protocolMap[requestProtocol]; ok { - return v.RequestForwarding(c) - } else { - return {StatusCode: http.StatusBadRequest}, - fmt.Errorf("Protocol unsupported %s, only http or https is allowed, but given %s", paramsInput.RequestProtocol, paramsInput.RequestProtocol) + if v, ok := protocolMap[protocol]; ok { + ret, err := v.RequestForwarding(c) + return ret, err } + + return {StatusCode: http.StatusBadRequest}, fmt.Errorf("protocol unspported %s", protocol) } type HTTPProtocolSupport struct { } func (h *HTTPProtocolSupport) RequestForwarding(c droplet.Context) (interface{}, error) { - paramsInput := c.Input().(*ParamsInput) - bodyParams := paramsInput.BodyParams - client := {} + input := c.Input().(*DebugOnlineInput) + url := input.URL + method := input.Method + body := input.Body + contentType := input.ContentType + + if url == "" || method == "" { + return {StatusCode: http.StatusBadRequest}, fmt.Errorf("parameters error") + } + client := {} client.Timeout = 5 * time.Second - req, err := http.NewRequest(strings.ToUpper(paramsInput.Method), paramsInput.URL, strings.NewReader(bodyParams)) + + var tempMap map[string][]string + err := json.Unmarshal([]byte(input.HeaderParams), ) + + if err != nil { + return {StatusCode: http.StatusInternalServerError}, err + } + + req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewReader(body)) if err != nil { return {StatusCode: http.StatusInternalServerError}, err } - for k, v := range paramsInput.HeaderParams { + + req.Header.Add("Content-Type", contentType) + for k, v := range tempMap { for _, v1 := range v { req.Header.Add(k, v1) } } + resp, err := client.Do(req) if err != nil { return {StatusCode: http.StatusInternalServerError}, err } - defer resp.Body.Close() + defer func() { + if resp != nil { + resp.Body.Close() + } + }() Review comment: It can be used directly `resp.Body.Close ()` is OK. Correct reading will also close the transaction. This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org
[GitHub] [apisix-dashboard] Jaycean commented on a change in pull request #1465: feat(be): Online debug function adds support for transfer files
Jaycean commented on a change in pull request #1465: URL: https://github.com/apache/apisix-dashboard/pull/1465#discussion_r578515927 ## File path: api/internal/handler/route_online_debug/route_online_debug.go ## @@ -63,66 +66,87 @@ type Result struct { Datainterface{} `json:"data,omitempty"` } -func DebugRequestForwarding(c droplet.Context) (interface{}, error) { +func (h *Handler) DebugRequestForwarding(c droplet.Context) (interface{}, error) { //TODO: other Protocols, e.g: grpc, websocket - paramsInput := c.Input().(*ParamsInput) - requestProtocol := paramsInput.RequestProtocol - if requestProtocol == "" { - requestProtocol = "http" + input := c.Input().(*DebugOnlineInput) + protocol := input.RequestProtocol + if protocol == "" { + protocol = "http" } protocolMap := make(map[string]ProtocolSupport) protocolMap["http"] = {} protocolMap["https"] = {} - if v, ok := protocolMap[requestProtocol]; ok { - return v.RequestForwarding(c) - } else { - return {StatusCode: http.StatusBadRequest}, - fmt.Errorf("Protocol unsupported %s, only http or https is allowed, but given %s", paramsInput.RequestProtocol, paramsInput.RequestProtocol) + if v, ok := protocolMap[protocol]; ok { + ret, err := v.RequestForwarding(c) + return ret, err } + + return {StatusCode: http.StatusBadRequest}, fmt.Errorf("protocol unspported %s", protocol) } type HTTPProtocolSupport struct { } func (h *HTTPProtocolSupport) RequestForwarding(c droplet.Context) (interface{}, error) { - paramsInput := c.Input().(*ParamsInput) - bodyParams := paramsInput.BodyParams - client := {} + input := c.Input().(*DebugOnlineInput) + url := input.URL + method := input.Method + body := input.Body + contentType := input.ContentType + + if url == "" || method == "" { + return {StatusCode: http.StatusBadRequest}, fmt.Errorf("parameters error") + } + client := {} client.Timeout = 5 * time.Second - req, err := http.NewRequest(strings.ToUpper(paramsInput.Method), paramsInput.URL, strings.NewReader(bodyParams)) + + var tempMap map[string][]string + err := json.Unmarshal([]byte(input.HeaderParams), ) + + if err != nil { + return {StatusCode: http.StatusInternalServerError}, err + } + + req, err := http.NewRequest(strings.ToUpper(method), url, bytes.NewReader(body)) if err != nil { return {StatusCode: http.StatusInternalServerError}, err } - for k, v := range paramsInput.HeaderParams { + + req.Header.Add("Content-Type", contentType) + for k, v := range tempMap { for _, v1 := range v { req.Header.Add(k, v1) } } + resp, err := client.Do(req) if err != nil { return {StatusCode: http.StatusInternalServerError}, err } - defer resp.Body.Close() + defer func() { + if resp != nil { + resp.Body.Close() + } + }() Review comment: When an error occurs in the request, the returned body data will not be clean Close ensures that the body has been fully read. This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org