github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/transport/http/outbound.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 "bytes" 11 "crypto/tls" 12 "errors" 13 "fmt" 14 "net/http" 15 "strings" 16 "time" 17 18 "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" 19 "github.com/hyperledger/aries-framework-go/pkg/didcomm/transport" 20 ) 21 22 //go:generate testdata/scripts/openssl_env.sh testdata/scripts/generate_test_keys.sh 23 24 const ( 25 commContentType = "application/didcomm-envelope-enc" 26 commContentTypeLegacy = "application/ssi-agent-wire" 27 httpScheme = "http" 28 ) 29 30 // outboundCommHTTPOpts holds options for the HTTP transport implementation of CommTransport 31 // it has an http.Client instance. 32 type outboundCommHTTPOpts struct { 33 client *http.Client 34 } 35 36 // OutboundHTTPOpt is an outbound HTTP transport option. 37 type OutboundHTTPOpt func(opts *outboundCommHTTPOpts) 38 39 // WithOutboundHTTPClient option is for creating an Outbound HTTP transport using an http.Client instance. 40 func WithOutboundHTTPClient(client *http.Client) OutboundHTTPOpt { 41 return func(opts *outboundCommHTTPOpts) { 42 opts.client = client 43 } 44 } 45 46 // WithOutboundTimeout option is for creating an Outbound HTTP transport using a client timeout value. 47 func WithOutboundTimeout(timeout time.Duration) OutboundHTTPOpt { 48 return func(opts *outboundCommHTTPOpts) { 49 opts.client.Timeout = timeout 50 } 51 } 52 53 // WithOutboundTLSConfig option is for creating an Outbound HTTP transport using a tls.Config instance. 54 func WithOutboundTLSConfig(tlsConfig *tls.Config) OutboundHTTPOpt { 55 return func(opts *outboundCommHTTPOpts) { 56 opts.client = &http.Client{ 57 Transport: &http.Transport{ 58 TLSClientConfig: tlsConfig, 59 }, 60 } 61 } 62 } 63 64 // OutboundHTTPClient represents the Outbound HTTP transport instance. 65 type OutboundHTTPClient struct { 66 client *http.Client 67 } 68 69 // NewOutbound creates a new instance of Outbound HTTP transport to Post requests to other Agents. 70 // An http.Client or tls.Config options is mandatory to create a transport instance. 71 func NewOutbound(opts ...OutboundHTTPOpt) (*OutboundHTTPClient, error) { 72 clOpts := &outboundCommHTTPOpts{} 73 // Apply options 74 for _, opt := range opts { 75 opt(clOpts) 76 } 77 78 if clOpts.client == nil { 79 return nil, errors.New("creation of outbound transport requires an HTTP client") 80 } 81 82 cs := &OutboundHTTPClient{ 83 client: clOpts.client, 84 } 85 86 return cs, nil 87 } 88 89 // Start starts outbound transport. 90 func (cs *OutboundHTTPClient) Start(prov transport.Provider) error { 91 return nil 92 } 93 94 // Send sends a2a exchange data via HTTP (client side). 95 func (cs *OutboundHTTPClient) Send(data []byte, destination *service.Destination) (string, error) { 96 uri, err := destination.ServiceEndpoint.URI() 97 if err != nil { 98 return "", fmt.Errorf("error getting ServiceEndpoint URI: %w", err) 99 } 100 101 resp, err := cs.client.Post(uri, commContentType, bytes.NewBuffer(data)) 102 if err != nil { 103 logger.Errorf("posting DID envelope to agent failed [%s, %v]", destination.ServiceEndpoint, err) 104 return "", err 105 } 106 107 var respData string 108 109 if resp != nil { 110 // handle response 111 defer func() { 112 e := resp.Body.Close() 113 if e != nil { 114 logger.Errorf("closing response body failed: %v", e) 115 } 116 }() 117 118 buf := new(bytes.Buffer) 119 120 _, e := buf.ReadFrom(resp.Body) 121 if e != nil { 122 return "", e 123 } 124 125 respData = buf.String() 126 127 isStatusSuccess := resp.StatusCode == http.StatusAccepted || resp.StatusCode == http.StatusOK 128 if !isStatusSuccess { 129 logger.Errorf("didcomm failed : transport=http serviceEndpoint=%s status=%v errMsg=%s", 130 destination.ServiceEndpoint, resp.Status, respData) 131 132 return "", fmt.Errorf("received unsuccessful POST HTTP status from agent "+ 133 "[%s, %v %s]", destination.ServiceEndpoint, resp.Status, respData) 134 } 135 } 136 137 return respData, nil 138 } 139 140 // AcceptRecipient checks if there is a connection for the list of recipient keys. 141 func (cs *OutboundHTTPClient) AcceptRecipient([]string) bool { 142 return false 143 } 144 145 // Accept url. 146 func (cs *OutboundHTTPClient) Accept(url string) bool { 147 return strings.HasPrefix(url, httpScheme) 148 }