github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/messaging/service/http/httpmsgsvc.go (about)

     1  /*
     2   *
     3   * Copyright SecureKey Technologies Inc. All Rights Reserved.
     4   *
     5   * SPDX-License-Identifier: Apache-2.0
     6   * /
     7   *
     8   */
     9  
    10  // Package http provides http-over-didcomm message service features.
    11  //
    12  // Any incoming message of type "https://didcomm.org/http-over-didcomm/1.0/request" and matching purpose can be handled
    13  // by registering 'OverDIDComm' message service.
    14  //
    15  // RFC Reference:
    16  //
    17  // https://github.com/hyperledger/aries-rfcs/blob/master/features/0335-http-over-didcomm/README.md
    18  // https://github.com/hyperledger/aries-rfcs/blob/master/features/0351-purpose-decorator/README.md
    19  //
    20  package http
    21  
    22  import (
    23  	"bytes"
    24  	"encoding/base64"
    25  	"fmt"
    26  	"net/http"
    27  
    28  	"github.com/hyperledger/aries-framework-go/pkg/common/log"
    29  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
    30  	"github.com/hyperledger/aries-framework-go/pkg/internal/logutil"
    31  )
    32  
    33  const (
    34  	// OverDIDCommSpec is http over DIDComm Spec value.
    35  	OverDIDCommSpec = "https://didcomm.org/http-over-didcomm/1.0/"
    36  
    37  	// OverDIDCommMsgRequestType is http over DIDComm request message type.
    38  	OverDIDCommMsgRequestType = OverDIDCommSpec + "request"
    39  
    40  	// error messages.
    41  	errNameAndHandleMandatory   = "service name and http request handle is mandatory"
    42  	errFailedToDecodeMsg        = "unable to decode DID comm message: %w"
    43  	errFailedToDecodeBody       = "unable to decode message body: %w"
    44  	errFailedToCreateNewRequest = "failed to create http request from incoming message: %w"
    45  
    46  	httpMessage = "httpMessage"
    47  )
    48  
    49  var logger = log.New("aries-framework/httpmsg")
    50  
    51  // RequestHandle handle function for http over did comm message service which gets called by
    52  // `OverDIDComm` message service to handle matching incoming request.
    53  //
    54  // Args
    55  //
    56  // msgID : message ID of incoming message.
    57  // request: http request derived from incoming DID comm message.
    58  //
    59  // Returns
    60  //
    61  // error : handle can return error back to service to notify message dispatcher about failures.
    62  type RequestHandle func(msgID string, request *http.Request) error
    63  
    64  // NewOverDIDComm creates new HTTP over DIDComm message service which serves
    65  // incoming DIDComm message over HTTP. DIDComm message receiver of [RFC-0351]
    66  //
    67  // Reference:
    68  //
    69  // https://github.com/hyperledger/aries-rfcs/blob/master/features/0335-http-over-didcomm/README.md
    70  // https://github.com/hyperledger/aries-rfcs/blob/master/features/0351-purpose-decorator/README.md
    71  //
    72  // Args:
    73  //
    74  // name - is name of this message service (this is mandatory argument).
    75  //
    76  // purpose - is optional list of purposes to be handled by this message service. If not provided then only message type
    77  // will be taken into consideration in acceptance criteria of this message service.
    78  //
    79  // httpHandle - is handle function to which incoming DIDComm message will be sent after being converted to
    80  // http request. (this is mandatory argument).
    81  //
    82  // Returns:
    83  //
    84  // OverDIDComm: http over didcomm message service,
    85  //
    86  // error: arg validation errors.
    87  func NewOverDIDComm(name string, httpHandle RequestHandle, purpose ...string) (*OverDIDComm, error) {
    88  	if name == "" || httpHandle == nil {
    89  		return nil, fmt.Errorf(errNameAndHandleMandatory)
    90  	}
    91  
    92  	return &OverDIDComm{
    93  		name:       name,
    94  		purpose:    purpose,
    95  		httpHandle: httpHandle,
    96  	}, nil
    97  }
    98  
    99  // OverDIDComm is message service which transports incoming DIDComm message over to
   100  // intended http resource providers.
   101  type OverDIDComm struct {
   102  	name       string
   103  	purpose    []string
   104  	httpHandle RequestHandle
   105  }
   106  
   107  // Name of HTTP over DIDComm message service.
   108  func (m *OverDIDComm) Name() string {
   109  	return m.name
   110  }
   111  
   112  // Accept is acceptance criteria for this HTTP over DIDComm message service,
   113  // it accepts http-didcomm-over message type [RFC-0335] and follows `A tagging system` purpose field validation
   114  // from RFC-0351.
   115  func (m *OverDIDComm) Accept(msgType string, purpose []string) bool {
   116  	if msgType != OverDIDCommMsgRequestType {
   117  		return false
   118  	}
   119  
   120  	// if purpose not set, then match only message type.
   121  	if len(m.purpose) == 0 {
   122  		return true
   123  	}
   124  
   125  	// match purpose if provided
   126  	for _, msgPurpose := range purpose {
   127  		for _, svcPurpose := range m.purpose {
   128  			if msgPurpose == svcPurpose {
   129  				return true
   130  			}
   131  		}
   132  	}
   133  
   134  	return false
   135  }
   136  
   137  // HandleInbound for HTTP over DIDComm message service.
   138  func (m *OverDIDComm) HandleInbound(msg service.DIDCommMsg, _ service.DIDCommContext) (string, error) {
   139  	svcMsg := httpOverDIDCommMsg{}
   140  
   141  	err := msg.Decode(&svcMsg)
   142  	if err != nil {
   143  		return "", fmt.Errorf(errFailedToDecodeMsg, err)
   144  	}
   145  
   146  	rqBody, err := base64.StdEncoding.DecodeString(svcMsg.BodyB64)
   147  	if err != nil {
   148  		return "", fmt.Errorf(errFailedToDecodeBody, err)
   149  	}
   150  
   151  	// create request
   152  	request, err := http.NewRequest(svcMsg.Method, svcMsg.ResourceURI, bytes.NewBuffer(rqBody))
   153  	if err != nil {
   154  		return "", fmt.Errorf(errFailedToCreateNewRequest, err)
   155  	}
   156  
   157  	// add headers
   158  	for _, header := range svcMsg.Headers {
   159  		request.Header.Add(header.Name, header.Value)
   160  	}
   161  
   162  	logutil.LogDebug(logger, httpMessage, "handleInbound", "received",
   163  		logutil.CreateKeyValueString("msgType", msg.Type()),
   164  		logutil.CreateKeyValueString("msgID", msg.ID()))
   165  
   166  	// TODO implement http version switch based on `msg.Version` [Issue:#1110]
   167  
   168  	return "", m.httpHandle(msg.ID(), request)
   169  }