github.com/vmware/transport-go@v1.3.4/plank/pkg/server/endpointer_handler_factory.go (about) 1 package server 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "github.com/vmware/transport-go/model" 8 "github.com/vmware/transport-go/plank/utils" 9 "github.com/vmware/transport-go/service" 10 "net/http" 11 "strings" 12 "time" 13 ) 14 15 // buildEndpointHandler builds a http.HandlerFunc that wraps Transport Bus operations in an HTTP request-response cycle. 16 // service channel, request builder and rest bridge timeout are passed as parameters. 17 func (ps *platformServer) buildEndpointHandler(svcChannel string, reqBuilder service.RequestBuilder, restBridgeTimeout time.Duration, msgChan chan *model.Message) http.HandlerFunc { 18 return func(w http.ResponseWriter, r *http.Request) { 19 defer func() { 20 if r := recover(); r != nil { 21 utils.Log.Errorln(r) 22 http.Error(w, "Internal Server Error", 500) 23 } 24 }() 25 26 // set context that expires after the provided amount of time in restBridgeTimeout to prevent requests from hanging forever 27 ctx, cancelFn := context.WithTimeout(context.Background(), restBridgeTimeout) 28 defer cancelFn() 29 30 // relay the request to transport channel 31 reqModel := reqBuilder(w, r) 32 err := ps.eventbus.SendRequestMessage(svcChannel, reqModel, reqModel.Id) 33 34 // get a response from the channel, render the results using ResponseWriter and log the data/error 35 // to the console as well. 36 select { 37 case <-ctx.Done(): 38 http.Error( 39 w, 40 fmt.Sprintf("No response received from service channel in %s, request timed out", restBridgeTimeout.String()), 500) 41 case msg := <-msgChan: 42 if msg.Error != nil { 43 utils.Log.WithError(msg.Error).Errorf( 44 "Error received from channel %s:", svcChannel) 45 http.Error(w, msg.Error.Error(), 500) 46 } else { 47 // only send the actual user payloadChannel not wrapper information 48 response := msg.Payload.(*model.Response) 49 var respBody interface{} 50 if response.Error { 51 if response.Payload != nil { 52 respBody = response.Payload 53 } else { 54 respBody = response 55 } 56 } else { 57 respBody = response.Payload 58 } 59 60 // if our Message is an error and it has a code, lets send that back to the client. 61 if response.Error { 62 63 // we have to set the headers for the error response 64 for k, v := range response.Headers { 65 w.Header().Set(k, v) 66 } 67 68 // deal with the response body now, if set. 69 n, e := json.Marshal(respBody) 70 if e != nil { 71 72 w.WriteHeader(response.ErrorCode) 73 w.Write([]byte(response.ErrorMessage)) 74 return 75 76 } else { 77 w.WriteHeader(response.ErrorCode) 78 w.Write(n) 79 return 80 } 81 } else { 82 // if the response has headers, set those headers. particularly if you're sending around 83 // byte array data for things like zip files etc. 84 for k, v := range response.Headers { 85 w.Header().Set(k, v) 86 if strings.ToLower(k) == "content-type" { 87 respBody, err = utils.ConvertInterfaceToByteArray(v, respBody) 88 } 89 } 90 91 var respBodyBytes []byte 92 // ensure respBody is properly converted to a byte slice as Content-Type header might not be 93 // set in the request and the restBody could be in a format that is not a byte slice. 94 respBodyBytes, err = ensureResponseInByteSlice(respBody) 95 96 // write the non-error payload back. 97 if _, err = w.Write(respBodyBytes); err != nil { 98 utils.Log.WithError(err).Errorf("Error received from channel %s:", svcChannel) 99 http.Error(w, err.Error(), 500) 100 } 101 } 102 } 103 } 104 } 105 }