Explicated Example

asn1set__SWIdent swIdent[] = { { asn1set__SWIdent__printableString, 
                                 { "Terisa Demo Gateway Software v.beta1" } } };

int
gateway_reads_CapReq_writes_CapRes(instream, outstream)
    io_ctx    instream;
    io_ctx    outstream;
{
    set_app_ctx             app_ctx    = 0;  /* per-thread global data */
    CapReq                  capReq     = 0;  /* SET message subtype */
    CapRes                  capRes     = 0;  /* SET message subtype */
    alloc_ctx               heap       = 0;  /* memory is allocated from */
    log_ctx                 history    = 0;  /* past-transaction info */
    cache_ctx               msg_cache[2];    /* per-transaction data */
    int                     msg_type;        /* asn1set__Message__... */
    set_msg                 request    = 0;  /* SET message base type */
    set_msg                 response   = 0;  /* SET message base type */
    int                     status;          /* return value */

    
    /*  Open the log.  The log contains, for the most part, the
     *  RRPID and/or XID of messages that have been processed.  This
     *  information is used primarily to detect repeated/replayed
     *  messages.
     *
     *  SETREF expects there to be a single, global log file
     *  which the user manages.  Instead of creating a local copy of
     *  the log, as is done here, it is more likely that a single,
     *  global 'history' would created when the application 
     *  starts up. */
    
    status = create_log_ctx_db(&history, "gateway.log");
    assert(status == NO_ERROR);

    /*  Create the thread-specific context, setting the default software
     *  identifier to "swIdent", and using the 8-character password 
     *  "password" to unlock the certificate database.  No callback  
     *  function is specified. 
     *
     *  The set_app_ctx context contains "global" data that is specific
     *  to a single thread, in order to support multi-threaded 
     *  applications.  A single set_app_ctx may not be shared among
     *  threads.
     *
     *  A set_app_ctx is independent of any message or transaction, such
     *  that a single set_app_ctx may be used to process multiple 
     *  messages (or transactions) concurrently. */ 

    status = create_set_app_ctx(&app_ctx, swIdent, history, "password", 8, 0, 0);
    assert(status == NO_ERROR);

    /*  Create an alloc_ctx, which is a memory allocation abstraction
     *  based on the standard malloc allocator package.  
     *
     *  SETREF uses user-supplied heaps to allocate any memory that
     *  is "passed back" to the user.  The user can incrementally free
     *  pointers using the AC_free() method, or can deallocate the
     *  entire heap using the AC_delete() method.  */

    status = create_pool_ctx(&heap);
    assert(status == NO_ERROR);

    /*  Create a blank SET message and associate the heap "heap"
     *  with the message.  All memory allocated and placed into 
     *  "request" by receive_set_msg() or decode_set_msg() is 
     *  allocated out of this heap.   
     *
     *  An instance of set_msg may only be associated with a
     *  single SET message.  It may not be re-used.
     *
     *  set_msg is the parent class for all SET messages.
     *  set_msg provides access to the SET message wrapper
     *  associated with an instance of a SET message.  
     *
     *  Objects of type set_msg, once they have a subtype, can be
     *  "cast" to their specific subtype.  A newly-created set_msg
     *  does not yet have a subtype.  */

    status = create_set_msg(&request, app_ctx, heap);
    assert(status == NO_ERROR);

    /*  Get the CapReq message from the io_ctx instream.  After the
     *  call to receive_set_msg(), "msg_type" contains the subtype of 
     *  the message that was read from the input stream.  In addition,
     *  "request" now has a subtype and may be cast to its specific
     *  subtype.
     *
     *  receive_set_msg() decodes the message's MessageWrapper, 
     *  although this information is suspect and should only
     *  be used as a possible "hint" as to the contents of the
     *  message.
     *
     *  receive_set_msg() will receive a message of any type, so it
     *  is important to check the msg_type parameter after receiving
     *  the message. */

    status = receive_set_msg(request, app_ctx, instream, &msg_type);
    assert(status == NO_ERROR);
    assert(msg_type == asn1set__Message__capReq);

    /*  Decode and decrypt the message.  After this call, the contents
     *  have been cryptographically verified, so the contents may 
     *  be used to locate the appropriate per-transaction cache_ctx.
     *  Since the protocol verification step has not yet been performed,
     *  the contents should not be used for much more than locating
     *  the appropriate cache(s).
     *
     *  decode_set_msg() will return a SET_WARN_ILLEGAL_REPLAY 
     *  warning if this message has be received before. */

    status = decode_set_msg(request, app_ctx);
    assert(status == NO_ERROR);

    /*  Cast the set_msg "request" to its subtype, CapReq.  If
     *  set_msg is not of type CapReq, then safe_cast_CapReq()
     *  will return a 0 pointer. */
    
    capReq = safe_cast_CapReq(request);
    assert(capReq != 0);

    /*  Open the per-transaction cache.  
     *
     *  Batch messages--which may contain more than one transaction-- 
     *  take an array of cache_ctx.  Specifically, the user is expected
     *  to supply 1 cache_ctx per transaction, in the order in which
     *  they appear in the "CapSeq" capture sequence.  The array element
     *  following the last cache_ctx must be zeroed.
     *
     *  The code below assumes only a single transaction in the
     *  capture.  This is incorrect.
     *
     *  The transaction ID (XID) is used to identify the appropriate 
     *  cache. */
     
    status = create_cache_ctx_db(&msg_cache[0], xid2filename(
               &capReq->capReqData.capSeq->capItem.transIDs.xID));     
    assert(status == NO_ERROR);
    memset(&msg_cache[1], 0, sizeof(msg_cache[1]));

    /*  Perform protocol verification of the request message.
     *  After these checks have been performed, the contents
     *  of "request" can be used by the application. 
     *
     *  As a last step, verify_set_msg() will store message data in
     *  the cache(s). */
     
    status = verify_set_msg(request, app_ctx, msg_cache);
    assert(status == NO_ERROR);

    /*  Process CapReq message */
    /* ... */





    /*  Create a blank SET message and associate the heap "heap"
     *  with the message.  All memory allocated and placed into 
     *  "request" by receive_set_msg() or decode_set_msg() is 
     *  allocated out of this heap. */
     
    status = create_set_msg(&response, app_ctx, heap);
    assert(status == NO_ERROR);

    /*  Build the CapRes message by calling make_set_msg().  Previous  
     *  state in this transaction (which at this point consists of
     *  at least the previous CapReq message) is obtained using 
     *  "msg_cache".  
     *
     *  Protocol layer data is inserted into the message by 
     *  make_set_msg().  The documentation for each message type
     *  enumerates the fields that make_set_msg() completes. 
     *
     *  As with verify_set_msg(), the user is expected to supply
     *  1 cache_ctx per transaction, in the order in which
     *  the transactions are to appear in the CapResSeq
     *  sequence. */

    msg_type = asn1set__Message__capRes;
    status = make_set_msg(response, app_ctx, msg_cache, msg_type);
    assert(status == NO_ERROR);

    /*  After a call to make_set_msg(), "response" can be cast to
     *  its specific SET message subtype, in this case CapRes. */

    capRes = safe_cast_CapRes(response);
    assert(capReq != 0);

    /*  Application-layer data is inserted into the message. 
     *  Documentation for each message type enumerates the fields
     *  that the user _must_ complete. */

    capRes->capResData.capResSeq->capResItem.capResPayload.capCode =
                                                   asn1set__CapCode__success;

    capRes->capResData.capResSeq->capResItem.capResPayload.capAmt.currency =
        capReq->capReqData.capSeq->capItem.capPayload.capReqAmt.currency;
    capRes->capResData.capResSeq->capResItem.capResPayload.capAmt.amount =
        capReq->capReqData.capSeq->capItem.capPayload.capReqAmt.amount;
    capRes->capResData.capResSeq->capResItem.capResPayload.capAmt.amtExp10 =
        capReq->capReqData.capSeq->capItem.capPayload.capReqAmt.amtExp10;

    /*  Send the CapRes message to the output stream "outstream".
     *  Again, the user is expected to supply an array of cache_ctx,
     *  1 per transaction.  
     *
     *  As a last step, send_set_msg() will store message data in
     *  the cache(s). */
     
    status = send_set_msg(response, app_ctx, msg_cache, outstream);
    assert(status == NO_ERROR);

    /*  Perform the cleanup, by deleting any contexts which had been 
     *  created.  After being deleted, these contexts can no longer
     *  be used (unless they are created again). */

    /*  Delete the cache object.  If another messages associated
     *  with this transaction were going to be processed, the
     *  cache could remain open and used to process messages
     *  associated with the transaction. */
     
    status = CACHE_delete(&msg_cache[0]);
    assert(status == NO_ERROR);

    /*  Delete the "response" and "request" set_msg objects.  All 
     *  memory allocated and placed in either of these objects was
     *  allocated from the alloc_ctx "heap".  This memory will not
     *  be de-allocated by delete_set_msg().  Instead, the user is
     *  given control of when this memory will be deallocated. 
     *
     *  A set_msg object should be deleted before to deleting the
     *  heap associated with that object in order to prevent 
     *  "dangling pointers". */
     
    status = delete_set_msg(&response);
    assert(status == NO_ERROR);

    status = delete_set_msg(&request);
    assert(status == NO_ERROR);

    /*  Delete the alloc_ctx "heap".  After this call, there will
     *  be no per-message data left allocated. */
     
    status = AC_delete(&heap);
    assert(status == NO_ERROR);

    /*  Delete the thread-specific global context.
     *
     *  If another message is going to be processed by this thread, 
     *  even one that is part of another transaction, then app_ctx can
     *  be used to process that message, rather than being deleted. */
     
    status = delete_set_app_ctx(&app_ctx);
    assert(status == NO_ERROR);

    /*  Delete the global log context.
     *
     *  Although the log is closed here, as long as the application 
     *  doesn't exit, the log could remain open because there is
     *  only a single, global log. 
     *  
     *  The log must be the last object deleted. */
  
    status = LOG_delete(&history);
    assert(status == NO_ERROR);

    /*  At this point, there is no SETREF memory left allocated. */
    
    return NO_ERROR;
}


Copyright © 1996, 1997, Visa International Service Association and MasterCard International Incorporated
All Rights Reserved.