Further Adventures with COBOL, XML & SSL

By | March 26, 2014

Building on earlier articles COBOL POSIX Sockets,  COBOL SSL Sockets and Consuming an XML service with COBOL, we want to enhance our project to handle multiple requests to the XML service, to better handle some errors and HTTP chunking.

Objectives,

  • Handle multiple requests read in from SYSIN.
  • Process the requests over a single connection to the server–don’t connect/disconnect for each request.
  • Interrupt the XML parsing once the data required is found.
  • Handle ‘not found’ conditions returned from the server.
  • Handle server disconnects by retrying once before failing.
  • Handle chunking better.

First cut, the approach is fairly straightforward:

procedure division.                                           

    perform initialize-ssl-environment                        

    open input sysin-fd                                       

    read sysin-fd                                             
      into    ws-service-uri-address                          
      at end  set ws-sysin-eof to true                        
    end-read                                                  

    perform                                                   
      with  test before                                       
      until ws-sysin-eof                                      

      if ws-not-connected                                     
        perform connect-to-server thru connect-to-server-exit 
      end-if                                                  

      perform send-request        thru send-request-exit      
      perform read-reply          thru read-reply-exit        
      perform parse-geocoderesponse                           

      display sysin-address                                   
      display ws-formatted-address                            
      display ' '                                             

      read sysin-fd                                           
        into    ws-service-uri-address                        
        at end  set ws-sysin-eof to true                      
      end-read                                                

    end-perform                                               

    perform disconnect-from-server                            
    perform close-ssl-environment                             

    close sysin-fd                                            

    goback                                                    
    .

This approach will allow the processing of multiple requests using the same TCP/IP and SSL connection to the server. With HTTP 1.1 the connection can be resused by default, although the server can choose to close the connection at ay time. When that happens, we would get an error, most likely in the read-reply (gsk_secure_socket_read). We can handle this by detecting the error, cleaning up the old connection and starting a new one and resend the request–we don’t have to reinitialize the SSL environment, however. We’ll use a flag so that we only retry once.

set ws-continue-on-gsk-error to true                   

perform send-request        thru send-request-exit     

if ws-not-connected                                    
  set ws-exit-on-gsk-error to true                     
  perform connect-to-server thru connect-to-server-exit
  perform send-request      thru send-request-exit     
end-if                                                 

set ws-continue-on-gsk-error to true                   

perform read-reply          thru read-reply-exit       

if ws-not-connected                                    
  set ws-exit-on-gsk-error to true                     
  perform connect-to-server thru connect-to-server-exit
  perform send-request      thru send-request-exit     
  perform read-reply        thru read-reply-exit       
end-if                                                 

perform parse-geocoderesponse

connect-to-server is responsible for detecting that the old socket is still around and cleaning up before opening a new one. And in the gsk_error routine there is some new code for this:

if ws-exit-on-gsk-error        
  goback                       
end-if                         

set ws-not-connected to true   
.

So, when send-request or read-reply have an error, we will be ws-not-connected and go through some retry logic, but only once since the retry logic sets ws-exit-on-gsk-error which will cause gsk_error to goback if there is another error.

Now, for this exercise I only want to get the formatted address for a given freehand address fro the Google geocoder API.  So I’ve adjusted the XML parsing logic to quit when it hits that XML element. Also, we nned to handle the condition when the data we send to the API to so garbled that it doesn’t return any good result, status not = ‘OK’.

 goecoderesponse-handler.                                  

* parse the XML and quit once we find the formatted-address

*    display xml-event ' ' xml-text                        

     evaluate xml-event                                    
       when 'START-OF-ELEMENT'                             
         move  xml-text  to ws-element-name                
       when 'CONTENT-CHARACTERS'                           
         if ws-element-name = 'formatted_address'          
           move xml-text to ws-formatted-address           
           move -1 to XML-CODE                             
         else                                              
           if ws-element-name = 'status'                   
           and xml-text not = 'OK'                         
             move xml-text to ws-formatted-address         
             move -1 to XML-CODE                           
           end-if                                          
         end-if                                            
       when 'END-OF-ELEMENT'                               
         move  spaces    to ws-element-name                
       when 'EXCEPTION'                                    
         display 'Exception '  XML-CODE                    
                 ' at offset ' length of xml-text '.'      
     end-evaluate                                          
     .

OK, that’s everything but the chunking.  I’m not particularly confident of my dechunking logic, but here it goes:

*    de-chunk the reply                                           
*    . 1st skip past header                                       

     perform varying ws-offset from 1 by 1                        
             until  ws-reply (ws-offset : 4) = ws-double-cr-lf    
     end-perform                                                  

     compute ws-offset = ws-offset + 4                            

*    . 2nd process <length><cr>lf><data><cr><lf>...<0><cr><lf>    

     move ws-offset to ws-tail-offset                             

     perform with test after                                      
             until ws-chunk-length = 0                            

       call 'sscanf'                                              
         using by value     address of ws-reply (ws-tail-offset : )
               by content   z'%x'                                 
               by value     address of ws-chunk-length            
         returning          ws-items                              

       if ws-chunk-length > 0                                     
       then                                                       
         perform varying ws-tail-offset from ws-tail-offset by 1  
                 until   ws-reply (ws-tail-offset : 2)            
                         = ws-carriage-return-line-feed           
         end-perform                                              

         compute ws-tail-offset = ws-tail-offset + 2              

         move ws-reply (ws-tail-offset : ws-chunk-length)         
           to ws-reply (ws-offset : ws-chunk-length)              

         compute ws-offset = ws-offset + ws-chunk-length          
         compute ws-tail-offset                                   
                 = ws-tail-offset + ws-chunk-length + 2           

       end-if                                                     

     end-perform                                                  

     move ws-null   to ws-reply (ws-offset : 1)                   

Here’s the full source at this point, though I expect to continue debugging…

cobxml2._cbl

Remember, to copybooks are in an earlier posting.

Here’s my compile/link/go JCL for this:

//*                                                      
//* Set a runtime option of POSIX=ON                     
//*                                                      
//CEEUOPT  EXEC ASMAC                                    
//C.SYSLIB DD  DSN=CEE.SCEEMAC                           
//C.SYSIN  DD  *                                         
CEEUOPT CSECT                                            
CEEUOPT AMODE ANY                                        
CEEUOPT RMODE ANY                                        
        CEEXOPT POSIX=ON                                 
        END   CEEUOPT                                    
//C.SYSLIN DD  DSN=&&LOADSET                             
//*                                                      
//* Compile, Link and GO                                 
//*                                                      
//COBXML    EXEC IGYWCLG,LNGPRFX='IGY',                  
// PARM.LKED='MAP,DYNAM(DLL),RENT,CASE(MIXED)'           
//COBOL.SYSIN    DD DISP=SHR,DSN=xxxxxxxx.SRC.COBOL(COBXML2)
//COBOL.SYSLIB   DD DISP=SHR,DSN=xxxxxxxx.SRC.COBOL        
//LKED.SYSLMOD DD DSNTYPE=LIBRARY                        
//LKED.SYSLIB  DD                                        
//LKED.SYSIN   DD *                                      
 INCLUDE '/usr/lpp/gskssl/lib/GSKSSL.x'                  
 INCLUDE '/usr/lpp/gskssl/lib/GSKCMS31.x'                
 ENTRY COBXML                                            
//GO.SYSPRINT DD SYSOUT=*                                
//GO.SYSOUT   DD SYSOUT=*                                
//GO.SYSIN    DD *                                       
1 New Orchard Road, Armonk, New York                     
1 Infinite, Cupertino, California                        
500 Oracle Parkway, Redwood Shores, CA                   
123 main street, anytown usa                             
asdfasdfasdfasdfasdfasdfasdfasdfasdfasdfs                
760 United Nations Plaza, New York, New York             
1600 pennsylvania avenue, washington dc                  
10 downing street, london, england                       
5046 S Greenwood Av, Chicago, IL

 

Leave a Reply

Your email address will not be published. Required fields are marked *