X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=E2Manager%2Fcontrollers%2Fnodeb_controller.go;h=f2efd652bef7c8800f56e21b89e4603c674b9ba7;hb=fe8d481d7f3e8d4b889ee3dcd995984eeb64cc6e;hp=d0448aa3efd22dbf5cb3af6aa4355d2b334c5776;hpb=07ef76dd471a0892a893c90e0ab06713aee34be1;p=ric-plt%2Fe2mgr.git diff --git a/E2Manager/controllers/nodeb_controller.go b/E2Manager/controllers/nodeb_controller.go index d0448aa..f2efd65 100644 --- a/E2Manager/controllers/nodeb_controller.go +++ b/E2Manager/controllers/nodeb_controller.go @@ -13,244 +13,370 @@ // 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. -// + +// This source code is part of the near-RT RIC (RAN Intelligent Controller) +// platform project (RICP). package controllers import ( + "e2mgr/e2managererrors" "e2mgr/logger" "e2mgr/models" - "e2mgr/providers" - "e2mgr/rNibWriter" - "e2mgr/services" - "e2mgr/sessions" - "e2mgr/utils" + "e2mgr/providers/httpmsghandlerprovider" "encoding/json" - "errors" - "gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/common" - "gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/reader" - "github.com/go-ozzo/ozzo-validation" - "github.com/go-ozzo/ozzo-validation/is" + "gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities" "github.com/golang/protobuf/jsonpb" - "github.com/julienschmidt/httprouter" + "github.com/golang/protobuf/proto" + "github.com/gorilla/mux" + "io" + "io/ioutil" "net/http" "net/http/httputil" "strings" - "sync" - "time" ) const ( - parseErrorCode int = 401 - validationErrorCode int = 402 - notFoundErrorCode int = 404 - internalErrorCode int = 501 - requiredInputErrorMessage = "Mandatory fields are missing" - validationFailedMessage = "Validation failed" - parseErrorMessage = "Parse failure" - notFoundErrorMessage = "Resource not found" - internalErrorMessage = "Internal Server Error. Please try again later" - sendMessageErrorMessage = "Failed to send message. For more information please check logs" + ParamRanName = "ranName" + LimitRequest = 2000 ) - -var E2Sessions = make(sessions.E2Sessions) - -var messageChannel chan *models.E2RequestMessage -var errorChannel chan error +const ApplicationJson = "application/json" +const ContentType = "Content-Type" + +type INodebController interface { + Shutdown(writer http.ResponseWriter, r *http.Request) + X2Reset(writer http.ResponseWriter, r *http.Request) + GetNodeb(writer http.ResponseWriter, r *http.Request) + UpdateGnb(writer http.ResponseWriter, r *http.Request) + UpdateEnb(writer http.ResponseWriter, r *http.Request) + GetNodebIdList(writer http.ResponseWriter, r *http.Request) + SetGeneralConfiguration(writer http.ResponseWriter, r *http.Request) + AddEnb(writer http.ResponseWriter, r *http.Request) + DeleteEnb(writer http.ResponseWriter, r *http.Request) + HealthCheckRequest(writer http.ResponseWriter, r *http.Request) +} type NodebController struct { - rmrService *services.RmrService - Logger *logger.Logger - rnibReaderProvider func() reader.RNibReader - rnibWriterProvider func() rNibWriter.RNibWriter + logger *logger.Logger + handlerProvider *httpmsghandlerprovider.IncomingRequestHandlerProvider } -func NewNodebController(logger *logger.Logger, rmrService *services.RmrService, rnibReaderProvider func() reader.RNibReader, - rnibWriterProvider func() rNibWriter.RNibWriter) *NodebController { - messageChannel = make(chan *models.E2RequestMessage) - errorChannel = make(chan error) +func NewNodebController(logger *logger.Logger, handlerProvider *httpmsghandlerprovider.IncomingRequestHandlerProvider) *NodebController { return &NodebController{ - rmrService: rmrService, - Logger: logger, - rnibReaderProvider: rnibReaderProvider, - rnibWriterProvider: rnibWriterProvider, + logger: logger, + handlerProvider: handlerProvider, } } +func (c *NodebController) GetNodebIdList(writer http.ResponseWriter, r *http.Request) { + c.logger.Infof("[Client -> E2 Manager] #NodebController.GetNodebIdList - request: %v", c.prettifyRequest(r)) -func prettifyRequest(request *http.Request) string { - dump, _ := httputil.DumpRequest(request, true) - requestPrettyPrint := strings.Replace(string(dump), "\r\n", " ", -1) - return strings.Replace(requestPrettyPrint, "\n", "", -1) + c.handleRequest(writer, &r.Header, httpmsghandlerprovider.GetNodebIdListRequest, nil, false, http.StatusOK) } -func (rc NodebController) HandleRequest(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { - startTime := time.Now() - rc.Logger.Infof("[Client -> E2 Manager] #nodeb_controller.HandleRequest - request: %v", prettifyRequest(request)) +func (c *NodebController) GetNodeb(writer http.ResponseWriter, r *http.Request) { + c.logger.Infof("[Client -> E2 Manager] #NodebController.GetNodeb - request: %v", c.prettifyRequest(r)) + vars := mux.Vars(r) + ranName := vars["ranName"] + request := models.GetNodebRequest{RanName: ranName} + c.handleRequest(writer, &r.Header, httpmsghandlerprovider.GetNodebRequest, request, false, http.StatusOK) +} - messageTypeParam := params.ByName("messageType") - requestHandlerProvider := providers.NewRequestHandlerProvider(rc.rnibWriterProvider) - handler, err := requestHandlerProvider.GetHandler(rc.Logger, messageTypeParam) +func (c *NodebController) UpdateGnb(writer http.ResponseWriter, r *http.Request) { + c.logger.Infof("[Client -> E2 Manager] #NodebController.UpdateGnb - request: %v", c.prettifyRequest(r)) + vars := mux.Vars(r) + ranName := vars[ParamRanName] - if err != nil { - handleErrorResponse(rc.Logger, http.StatusNotFound, notFoundErrorCode, notFoundErrorMessage, writer, startTime) - return - } + request := models.UpdateGnbRequest{} - requestDetails, err := parseJson(rc.Logger, request) + gnb := entities.Gnb{} - if err != nil { - handleErrorResponse(rc.Logger, http.StatusBadRequest, parseErrorCode, parseErrorMessage, writer, startTime) + if !c.extractRequestBodyToProto(r, &gnb, writer) { return } - rc.Logger.Infof("#nodeb_controller.HandleRequest - request: %+v", requestDetails) + request.Gnb = &gnb + request.RanName = ranName + c.handleRequest(writer, &r.Header, httpmsghandlerprovider.UpdateGnbRequest, &request, true, http.StatusOK) +} + +func (c *NodebController) UpdateEnb(writer http.ResponseWriter, r *http.Request) { + c.logger.Infof("[Client -> E2 Manager] #NodebController.UpdateEnb - request: %v", c.prettifyRequest(r)) - if err := validateRequestDetails(rc.Logger, requestDetails); err != nil { - handleErrorResponse(rc.Logger, http.StatusBadRequest, validationErrorCode, requiredInputErrorMessage, writer, startTime) + defer r.Body.Close() + body, err := ioutil.ReadAll(r.Body) + + if err != nil { + c.logger.Errorf("[Client -> E2 Manager] #NodebController.UpdateEnb - unable to read request body - error: %s", err) + c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer) return } - err = handler.PreHandle(rc.Logger, &requestDetails) + updateEnbRequest := models.UpdateEnbRequest{} + err = json.Unmarshal(body, &updateEnbRequest) if err != nil { - handleErrorResponse(rc.Logger, http.StatusInternalServerError, internalErrorCode, err.Error(), writer, startTime) + c.logger.Errorf("[Client -> E2 Manager] #NodebController.UpdateEnb - unable to unmarshal json - error: %s", err) + c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer) return } - rc.Logger.Infof("[E2 Manager -> Client] #nodeb_controller.HandleRequest - http status: 200") - writer.WriteHeader(http.StatusOK) + vars := mux.Vars(r) + ranName := vars[ParamRanName] - var wg sync.WaitGroup + updateEnbRequest.RanName = ranName + + c.handleRequest(writer, &r.Header, httpmsghandlerprovider.UpdateEnbRequest, &updateEnbRequest, true, http.StatusOK) +} - go handler.CreateMessage(rc.Logger, &requestDetails, messageChannel, E2Sessions, startTime, wg) +func (c *NodebController) AddEnb(writer http.ResponseWriter, r *http.Request) { + c.logger.Infof("[Client -> E2 Manager] #NodebController.AddEnb - request: %v", c.prettifyRequest(r)) - go rc.rmrService.SendMessage(handler.GetMessageType(), messageChannel, errorChannel, wg) + defer r.Body.Close() + body, err := ioutil.ReadAll(r.Body) - wg.Wait() + if err != nil { + c.logger.Errorf("[Client -> E2 Manager] #NodebController.AddEnb - unable to read request body - error: %s", err) + c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer) + return + } - err = <-errorChannel + addEnbRequest := models.AddEnbRequest{} + err = json.Unmarshal(body, &addEnbRequest) if err != nil { - handleErrorResponse(rc.Logger, http.StatusInternalServerError, internalErrorCode, sendMessageErrorMessage, writer, startTime) + c.logger.Errorf("[Client -> E2 Manager] #NodebController.AddEnb - unable to unmarshal json - error: %s", err) + c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer) return } - printHandlingRequestElapsedTimeInMs(rc.Logger, startTime) + c.handleRequest(writer, &r.Header, httpmsghandlerprovider.AddEnbRequest, &addEnbRequest, true, http.StatusCreated) } -func (rc NodebController) GetNodebIdList (writer http.ResponseWriter, request *http.Request, params httprouter.Params) { - startTime := time.Now() - rnibReaderService := services.NewRnibReaderService(rc.rnibReaderProvider()) - nodebIdList, rnibError := rnibReaderService.GetNodebIdList() +func (c *NodebController) DeleteEnb(writer http.ResponseWriter, r *http.Request) { + c.logger.Infof("[Client -> E2 Manager] #NodebController.DeleteEnb - request: %v", c.prettifyRequest(r)) + vars := mux.Vars(r) + ranName := vars["ranName"] + request := &models.DeleteEnbRequest{RanName: ranName} + c.handleRequest(writer, &r.Header, httpmsghandlerprovider.DeleteEnbRequest, request, true, http.StatusNoContent) +} - if rnibError != nil { - rc.Logger.Errorf("%v", rnibError); - httpStatusCode, errorCode, errorMessage := rnibErrorToHttpError(rnibError) - handleErrorResponse(rc.Logger,httpStatusCode,errorCode,errorMessage,writer,startTime ) - return; - } +func (c *NodebController) SetGeneralConfiguration(writer http.ResponseWriter, r *http.Request) { + c.logger.Infof("[Client -> E2 Manager] #NodebController.SetGeneralConfiguration - request: %v", c.prettifyRequest(r)) - pmList:= utils.ConvertNodebIdListToProtoMessageList(*nodebIdList) - result, err := utils.MarshalProtoMessageListToJsonArray(pmList) + request := models.GeneralConfigurationRequest{} - if err != nil { - rc.Logger.Errorf("%v", err); - handleErrorResponse(rc.Logger,http.StatusInternalServerError,internalErrorCode,internalErrorMessage,writer,startTime ) - return; + if !c.extractJsonBodyDisallowUnknownFields(r, &request, writer) { + return } + c.handleRequest(writer, &r.Header, httpmsghandlerprovider.SetGeneralConfigurationRequest, request, false, http.StatusOK) +} - writer.Header().Set("Content-Type", "application/json") - rc.Logger.Infof("[E2 Manager -> Client] #nodeb_controller.GetNodebIdList - response: %s", result) - writer.Write([]byte(result)) +func (c *NodebController) Shutdown(writer http.ResponseWriter, r *http.Request) { + c.logger.Infof("[Client -> E2 Manager] #NodebController.Shutdown - request: %v", c.prettifyRequest(r)) + c.handleRequest(writer, &r.Header, httpmsghandlerprovider.ShutdownRequest, nil, false, http.StatusNoContent) } -func (rc NodebController) GetNodeb(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { - startTime := time.Now() - ranName := params.ByName("ranName") - // WAS: respondingNode, rnibError := reader.GetRNibReader().GetNodeb(ranName) - rnibReaderService := services.NewRnibReaderService(rc.rnibReaderProvider()); - respondingNode, rnibError := rnibReaderService.GetNodeb(ranName) - if rnibError != nil { - rc.Logger.Errorf("%v", rnibError) - httpStatusCode, errorCode, errorMessage := rnibErrorToHttpError(rnibError) - handleErrorResponse(rc.Logger, httpStatusCode, errorCode, errorMessage, writer, startTime) +func (c *NodebController) X2Reset(writer http.ResponseWriter, r *http.Request) { + c.logger.Infof("[Client -> E2 Manager] #NodebController.X2Reset - request: %v", c.prettifyRequest(r)) + request := models.ResetRequest{} + vars := mux.Vars(r) + ranName := vars[ParamRanName] + + if err := c.extractJsonBody(r, &request); err != nil { + c.handleErrorResponse(err, writer) return } + request.RanName = ranName + c.handleRequest(writer, &r.Header, httpmsghandlerprovider.ResetRequest, request, false, http.StatusNoContent) +} - m := jsonpb.Marshaler{} - result, err := m.MarshalToString(respondingNode) +func (c *NodebController) HealthCheckRequest(writer http.ResponseWriter, r *http.Request) { + c.logger.Infof("[Client -> E2 Manager] #NodebController.HealthCheckRequest - request: %v", c.prettifyRequest(r)) - if err != nil { - rc.Logger.Errorf("%v", err) - handleErrorResponse(rc.Logger, http.StatusInternalServerError, internalErrorCode, internalErrorMessage, writer, startTime) + request := models.HealthCheckRequest{} + + if err := c.extractJsonBody(r, &request); err != nil { + c.handleErrorResponse(err, writer) return } - writer.Header().Set("Content-Type", "application/json") - rc.Logger.Infof("[E2 Manager -> Client] #nodeb_controller.GetNodeb - response: %s", result) - writer.Write([]byte(result)) + c.handleRequest(writer, &r.Header, httpmsghandlerprovider.HealthCheckRequest, request, true, http.StatusNoContent) } -func (rc NodebController) HandleHealthCheckRequest(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { - //fmt.Println("[X-APP -> Client] #HandleHealthCheckRequest - http status: 200") - writer.WriteHeader(http.StatusOK) +func (c *NodebController) extractRequestBodyToProto(r *http.Request, pb proto.Message, writer http.ResponseWriter) bool { + defer r.Body.Close() + + err := jsonpb.Unmarshal(r.Body, pb) + + if err != nil { + c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err) + c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer) + return false + } + + return true } -func parseJson(logger *logger.Logger, request *http.Request) (models.RequestDetails, error) { - var requestDetails models.RequestDetails - if err := json.NewDecoder(request.Body).Decode(&requestDetails); err != nil { - logger.Errorf("#nodeb_controller.parseJson - cannot deserialize incoming request. request: %v, error: %v", request, err) - return requestDetails, err +func (c *NodebController) extractJsonBodyDisallowUnknownFields(r *http.Request, request models.Request, writer http.ResponseWriter) bool { + defer r.Body.Close() + + decoder := json.NewDecoder(r.Body) + decoder.DisallowUnknownFields() + + if err := decoder.Decode(&request); err != nil { + c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err) + c.handleErrorResponse(e2managererrors.NewInvalidJsonError(), writer) + return false } - return requestDetails, nil + + return true } -func validateRequestDetails(logger *logger.Logger, requestDetails models.RequestDetails) error { +func (c *NodebController) extractJsonBody(r *http.Request, request models.Request) error { + defer r.Body.Close() + body, err := ioutil.ReadAll(io.LimitReader(r.Body, LimitRequest)) - if requestDetails.RanPort == 0 { - logger.Errorf("#nodeb_controller.validateRequestDetails - validation failure: port cannot be zero") - return errors.New("port: cannot be blank") + if err != nil { + c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err) + return e2managererrors.NewInvalidJsonError() } - err := validation.ValidateStruct(&requestDetails, - validation.Field(&requestDetails.RanIp, validation.Required, is.IP), - validation.Field(&requestDetails.RanName, validation.Required), - ) + + err = json.Unmarshal(body, &request) if err != nil { - logger.Errorf("#nodeb_controller.validateRequestDetails - validation failure, error: %v", err) + c.logger.Errorf("[Client -> E2 Manager] #NodebController.extractJsonBody - unable to extract json body - error: %s", err) + return e2managererrors.NewInvalidJsonError() } - return err + return nil } -func handleErrorResponse(logger *logger.Logger, httpStatus int, errorCode int, errorMessage string, writer http.ResponseWriter, startTime time.Time) { - errorResponseDetails := models.ErrorResponse{errorCode, errorMessage} - errorResponse, _ := json.Marshal(errorResponseDetails) - printHandlingRequestElapsedTimeInMs(logger, startTime) - logger.Infof("[E2 Manager -> Client] #nodeb_controller.handleErrorResponse - http status: %d, error response: %+v", httpStatus, errorResponseDetails) - writer.Header().Set("Content-Type", "application/json") - writer.WriteHeader(httpStatus) - _, err := writer.Write(errorResponse) +func (c *NodebController) handleRequest(writer http.ResponseWriter, header *http.Header, requestName httpmsghandlerprovider.IncomingRequest, request models.Request, validateRequestHeaders bool, successStatusCode int) { + + if validateRequestHeaders { + + err := c.validateRequestHeader(header) + if err != nil { + c.handleErrorResponse(err, writer) + return + } + } + + handler, err := c.handlerProvider.GetHandler(requestName) if err != nil { - logger.Errorf("#nodeb_controller.handleErrorResponse - Cannot send response. writer:%v", writer) + c.handleErrorResponse(err, writer) + return } + + response, err := handler.Handle(request) + + if err != nil { + c.handleErrorResponse(err, writer) + return + } + + if successStatusCode == http.StatusNoContent { + writer.WriteHeader(successStatusCode) + c.logger.Infof("[E2 Manager -> Client] #NodebController.handleRequest - status response: %v", http.StatusNoContent) + return + } + + result, err := response.Marshal() + + if err != nil { + c.handleErrorResponse(err, writer) + return + } + + c.logger.Infof("[E2 Manager -> Client] #NodebController.handleRequest - response: %s", result) + writer.Header().Set(ContentType, ApplicationJson) + writer.WriteHeader(successStatusCode) + writer.Write(result) } -func printHandlingRequestElapsedTimeInMs(logger *logger.Logger, startTime time.Time) { - logger.Infof("Summary: #nodeb_controller.printElapsedTimeInMs - Elapsed time for handling request from client to E2 termination: %f ms", - float64(time.Since(startTime))/float64(time.Millisecond)) +func (c *NodebController) validateRequestHeader(header *http.Header) error { + + if header.Get(ContentType) != ApplicationJson { + c.logger.Errorf("#NodebController.validateRequestHeader - validation failure, incorrect content type") + + return e2managererrors.NewHeaderValidationError() + } + return nil } -func rnibErrorToHttpError(rnibError common.IRNibError) (int, int, string) { - switch rnibError.GetCode() { - case common.RESOURCE_NOT_FOUND: - return http.StatusNotFound, notFoundErrorCode, notFoundErrorMessage - case common.INTERNAL_ERROR: - return http.StatusInternalServerError, internalErrorCode, internalErrorMessage - case common.VALIDATION_ERROR: - return http.StatusBadRequest, validationErrorCode, validationFailedMessage - default: - return http.StatusInternalServerError, internalErrorCode, internalErrorMessage +func (c *NodebController) handleErrorResponse(err error, writer http.ResponseWriter) { + + var errorResponseDetails models.ErrorResponse + var httpError int + + if err != nil { + switch err.(type) { + case *e2managererrors.RnibDbError: + e2Error, _ := err.(*e2managererrors.RnibDbError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusInternalServerError + case *e2managererrors.CommandAlreadyInProgressError: + e2Error, _ := err.(*e2managererrors.CommandAlreadyInProgressError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusMethodNotAllowed + case *e2managererrors.HeaderValidationError: + e2Error, _ := err.(*e2managererrors.HeaderValidationError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusUnsupportedMediaType + case *e2managererrors.WrongStateError: + e2Error, _ := err.(*e2managererrors.WrongStateError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusBadRequest + case *e2managererrors.RequestValidationError: + e2Error, _ := err.(*e2managererrors.RequestValidationError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusBadRequest + case *e2managererrors.InvalidJsonError: + e2Error, _ := err.(*e2managererrors.InvalidJsonError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusBadRequest + case *e2managererrors.RmrError: + e2Error, _ := err.(*e2managererrors.RmrError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusInternalServerError + case *e2managererrors.ResourceNotFoundError: + e2Error, _ := err.(*e2managererrors.ResourceNotFoundError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusNotFound + case *e2managererrors.E2TInstanceAbsenceError: + e2Error, _ := err.(*e2managererrors.E2TInstanceAbsenceError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusServiceUnavailable + case *e2managererrors.RoutingManagerError: + e2Error, _ := err.(*e2managererrors.RoutingManagerError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusServiceUnavailable + case *e2managererrors.NodebExistsError: + e2Error, _ := err.(*e2managererrors.NodebExistsError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusBadRequest + case *e2managererrors.NoConnectedRanError: + e2Error, _ := err.(*e2managererrors.NoConnectedRanError) + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusNotFound + default: + e2Error := e2managererrors.NewInternalError() + errorResponseDetails = models.ErrorResponse{Code: e2Error.Code, Message: e2Error.Message} + httpError = http.StatusInternalServerError + } } + errorResponse, _ := json.Marshal(errorResponseDetails) + + c.logger.Errorf("[E2 Manager -> Client] #NodebController.handleErrorResponse - http status: %d, error response: %+v", httpError, errorResponseDetails) + + writer.Header().Set(ContentType, ApplicationJson) + writer.WriteHeader(httpError) + _, err = writer.Write(errorResponse) +} + +func (c *NodebController) prettifyRequest(request *http.Request) string { + dump, _ := httputil.DumpRequest(request, true) + requestPrettyPrint := strings.Replace(string(dump), "\r\n", " ", -1) + return strings.Replace(requestPrettyPrint, "\n", "", -1) }