github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/transport/http/inbound.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package http 8 9 import ( 10 "context" 11 "errors" 12 "fmt" 13 "io/ioutil" 14 "net/http" 15 16 "github.com/rs/cors" 17 18 "github.com/hyperledger/aries-framework-go/pkg/common/log" 19 "github.com/hyperledger/aries-framework-go/pkg/didcomm/transport" 20 "github.com/hyperledger/aries-framework-go/pkg/didcomm/transport/internal" 21 ) 22 23 var logger = log.New("aries-framework/http") 24 25 // TODO https://github.com/hyperledger/aries-framework-go/issues/891 Support for Transport Return Route (Duplex) 26 27 // NewInboundHandler will create a new handler to enforce Did-Comm HTTP transport specs 28 // then routes processing to the mandatory 'msgHandler' argument. 29 // 30 // Arguments: 31 // * 'msgHandler' is the handler function that will be executed with the inbound request payload. 32 // Users of this library must manage the handling of all inbound payloads in this function. 33 func NewInboundHandler(prov transport.Provider) (http.Handler, error) { 34 if prov == nil || prov.InboundMessageHandler() == nil { 35 logger.Errorf("Error creating a new inbound handler: message handler function is nil") 36 return nil, errors.New("creation of inbound handler failed") 37 } 38 39 handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 40 processPOSTRequest(w, r, prov) 41 }) 42 43 return cors.Default().Handler(handler), nil 44 } 45 46 func processPOSTRequest(w http.ResponseWriter, r *http.Request, prov transport.Provider) { 47 if valid := validateHTTPMethod(w, r); !valid { 48 return 49 } 50 51 if valid := validatePayload(r, w); !valid { 52 return 53 } 54 55 body, err := ioutil.ReadAll(r.Body) 56 if err != nil { 57 logger.Errorf("Error reading request body: %s - returning Code: %d", err, http.StatusInternalServerError) 58 http.Error(w, "Failed to read payload", http.StatusInternalServerError) 59 60 return 61 } 62 63 unpackMsg, err := internal.UnpackMessage(body, prov.Packager(), "http") 64 if err != nil { 65 logger.Errorf("%w - returning Code: %d", err, http.StatusInternalServerError) 66 http.Error(w, "failed to unpack msg", http.StatusInternalServerError) 67 68 return 69 } 70 71 messageHandler := prov.InboundMessageHandler() 72 73 err = messageHandler(unpackMsg) 74 if err != nil { 75 // TODO https://github.com/hyperledger/aries-framework-go/issues/271 HTTP Response Codes based on errors 76 // from service 77 logger.Errorf("incoming msg processing failed: %s", err) 78 w.WriteHeader(http.StatusInternalServerError) 79 } else { 80 w.WriteHeader(http.StatusAccepted) 81 } 82 } 83 84 // validatePayload validate and get the payload from the request. 85 func validatePayload(r *http.Request, w http.ResponseWriter) bool { 86 if r.ContentLength == 0 { // empty payload should not be accepted 87 http.Error(w, "Empty payload", http.StatusBadRequest) 88 return false 89 } 90 91 return true 92 } 93 94 // validateHTTPMethod validate HTTP method and content-type. 95 func validateHTTPMethod(w http.ResponseWriter, r *http.Request) bool { 96 if r.Method != "POST" { 97 http.Error(w, "HTTP Method not allowed", http.StatusMethodNotAllowed) 98 return false 99 } 100 101 ct := r.Header.Get("Content-type") 102 103 if ct != commContentType && ct != commContentTypeLegacy { 104 http.Error(w, fmt.Sprintf("Unsupported Content-type \"%s\"", ct), http.StatusUnsupportedMediaType) 105 return false 106 } 107 108 return true 109 } 110 111 // Inbound http type. 112 type Inbound struct { 113 externalAddr string 114 server *http.Server 115 certFile, keyFile string 116 } 117 118 // NewInbound creates a new HTTP inbound transport instance. 119 func NewInbound(internalAddr, externalAddr, certFile, keyFile string) (*Inbound, error) { 120 if internalAddr == "" { 121 return nil, errors.New("http address is mandatory") 122 } 123 124 if externalAddr == "" { 125 externalAddr = internalAddr 126 } 127 128 return &Inbound{ 129 certFile: certFile, 130 keyFile: keyFile, 131 externalAddr: externalAddr, 132 server: &http.Server{Addr: internalAddr}, 133 }, nil 134 } 135 136 // Start the http server. 137 func (i *Inbound) Start(prov transport.Provider) error { 138 handler, err := NewInboundHandler(prov) 139 if err != nil { 140 return fmt.Errorf("HTTP server start failed: %w", err) 141 } 142 143 i.server.Handler = handler 144 145 go func() { 146 if err := i.listenAndServe(); !errors.Is(err, http.ErrServerClosed) { 147 logger.Fatalf("HTTP server start with address [%s] failed, cause: %s", i.server.Addr, err) 148 } 149 }() 150 151 return nil 152 } 153 154 func (i *Inbound) listenAndServe() error { 155 if i.certFile != "" && i.keyFile != "" { 156 return i.server.ListenAndServeTLS(i.certFile, i.keyFile) 157 } 158 159 return i.server.ListenAndServe() 160 } 161 162 // Stop the http server. 163 func (i *Inbound) Stop() error { 164 if err := i.server.Shutdown(context.Background()); err != nil { 165 return fmt.Errorf("HTTP server shutdown failed: %w", err) 166 } 167 168 return nil 169 } 170 171 // Endpoint provides the http connection details. 172 func (i *Inbound) Endpoint() string { 173 // return http prefix as framework only supports http 174 return i.externalAddr 175 }