Sample REXX Client Application

REXX on the IBM mainframe has its own function pack for sockets support, effectively its sockets API. Support is via a single function SOCKET() whose first parameter is the type of socket call to be made. This example is the full program from which snippets have been taken as examples in the Sockets Programming Tutorial. The application connects to a web server and uses the HTTP 1.1 protocol to request the root page of the web site.

To use REXX sockets the TCPIP SEZALOAD library must be in LNKLST or STEPLIB'd.

Sockets
Contents
Contents
Sockets
Examples
Prev

 

The Code



/** REXX *************************************************************/
/*                                                                   */
/* A sample REXX client application that uses the HTTP 1.1 protocol  */ 
/* to GET a page from a web server. Note with HTTP 1.1 the HTTP CRLF */
/* ('0D25'x in EBCDIC, '0D0A'x in ASCII) delimited records will      */ 
/* include a "Content-Length:" entity header specifying the length   */
/* of the html payload.                                              */
/*                                                                   */
/* This example program is provided to illustrate use of the IBM     */ 
/* REXX sockets API on a best effort basis. There is no warranty to  */ 
/* its functioning in any environment nor to its suitability for any */ 
/* express function. You are free to include the entirety or         */ 
/* portions of this code into your own environment/application as    */ 
/* you wish and under your own responsibility with regard quality    */ 
/* and suitability of purpose.                                       */
/*********************************************************************/

   p.page = '/'                      /* default page           */
   p.host = 'www.test.site'          /* target system name     */
   p.service = 80                    /* target service port    */
   p.type = 'port'                   /* service is a port num  */
   p.data = 'GET' p.page 'HTTP/1.1' || '0D25'x ,
      || 'Host:' p.host || '0D25'x ,
      || '0D25'x

   p.verbose = 'Y'                   /* verbose output Y or N  */
   p.trace   = 'N'                   /* trace data     Y or N  */

   html = DO_TRAN()
   do i = 1 to 999 while html <> ''
      parse var html line '0D25'x html
      say line
   end
   exit

/*********************************************************************/
/*  Process a transactional client server request                    */
/*********************************************************************/
DO_TRAN: procedure expose p.

   tcpId = "TSREXX"

   if p.verbose = 'Y' then
      say 'Transaction starting, server:' p.host p.type p.service

/*********************************************************************/
/*  Initialise the REXX API interface                                */
/*********************************************************************/

   Resp = SOCKET( 'SOCKETSETSTATUS', tcpId)
   if WORD( resp, 1) = 0 then
      Resp = SOCKET( 'TERMINATE', tcpId)
   Resp = SOCKET( 'INITIALIZE', tcpId)
   if WORD( resp, 1) <> 0 then
   do
      say 'Error initialising TCP API'
      say resp
      exit
   end

/*********************************************************************/
/*  Get the target system address information                        */
/*********************************************************************/

   Resp = SOCKET( 'GETADDRINFO', p.host, p.service,,,
                       'AF_UNSPEC', 'SOCK_STREAM', 'IPPROTO_TCP')
   if p.verbose = 'Y' then
      say 'GETADDRINFO:' resp
   if WORD( resp, 1) <> 0 then
   do
      say 'Error with GETADDRINFO for:' p.host p.service
      say resp
      exit
   end
   addrList = SUBWORD( resp, 3)
   socNum = ''
   do while addrList <> '' & socNum = ''
      if ( WORD( addrList,1) = 'AF_INET') then
         parse var addrList fam port addr addrList
      else
         parse var addrList fam port flw addr scp addrList
      if p.verbose = 'Y' then
         say 'Attempting connection with:' fam addr 'port' port
      addrList = STRIP( addrList)

      /***************************************************************/
      /*  Open the socket, set auto ASCII EBCDIC translation         */
      /***************************************************************/

      Resp = SOCKET( 'SOCKET', fam, 'SOCK_STREAM', 'IPPROTO_TCP')
      if p.verbose = 'Y' then
         say 'SOCKET:' resp
      if WORD( resp, 1) <> 0 then
      do
         say 'Error opening socket' fam
         say resp
      end
      else
         socNum = WORD( resp, 2)

      if socNum <> '' then
      do
         Resp = SOCKET( 'SETSOCKOPT', socNum, 'IPPROTO_TCP',,
                            'SO_ASCII', 1)
         if p.verbose = 'Y' then
            say 'SETSOCKOPT:' resp
         if WORD( resp, 1) <> 0 then
         do
            say 'Error setting socket options'
            say resp
            resp = SOCKET( 'CLOSE', socNum)
            socNum = ''
         end
      end

      /***************************************************************/
      /*  Connect with service on target system                      */
      /***************************************************************/

      if socNum <> '' then
      do
         if fam = 'AF_INET' then
             Resp = SOCKET( 'CONNECT', socNum, fam port addr)
         else
             Resp = SOCKET( 'CONNECT', socNum, fam port flw addr scp)
         if p.verbose = 'Y' then
            say 'CONNECT:' resp
         if WORD( resp, 1) <> 0 then
         do
            say 'Error connecting to' addr':'port
            say resp
            resp = SOCKET( 'CLOSE', socNum)
            socNum = ''
         end
      end
   end
   if socNum = '' then
   do
      say 'Unable to connect with' p.host p.type p.service
      return -1
   end
   if p.verbose = 'Y' then
      say 'Connection with' addr 'port' port 'established'

/*********************************************************************/
/*  Send the data                                                    */
/*********************************************************************/

   if p.trace = 'Y' then
   do
      say 'Sending:'
      call DO_TRACE p.data
   end
   Resp = SOCKET( 'SEND', socNum, p.data)
   if WORD( resp, 1) <> 0 then
   do
      say 'Error sending data'
      say resp
      return -1
   end
   retc = WORD( resp, 2)
   if retc <> LENGTH( p.data) then
   do
       say 'Error partial send' retc 'of' LENGTH(p.data) 'bytes'
       say resp
       return -1
   end

/*********************************************************************/
/*  Receive HTTP header (probably more). The header is delimited by  */
/*  a CRLF-CRLF sequence (i.e. a null line.                          */
/*********************************************************************/

   tdata = ''                           /* total received data       */
   tlen = 0                             /* total length              */
   dlen = 1                             /* loop control              */
   clen = 99999999                      /* If Content-length missing */
   do while dlen <> 0                   /* at least once             */
      Resp = SOCKET( 'RECV', socNum)
      parse var resp retc dlen data
      if retc <> 0 then
      do
          say 'Error receiving data'
          say resp
          exit
      end
      if p.trace = 'Y' then
      do
         say 'Received:'
         call DO_TRACE data
      end
      tdata = tdata || data
      tlen = tlen + dlen
      ndx = POS( '0D250D25'x, tdata)
      if ndx <> 0 then
         dlen = 0
   end

   http = SUBSTR( tdata, 1, ndx+2)
   do while http <> ''
      parse var http line'0D25'x http
      if p.verbose = 'Y' then
         say 'http' line
      parse var line kywd': 'val .
      if TRANSLATE(kywd) = 'CONTENT-LENGTH' then
         clen = val-0
   end

/*********************************************************************/
/*  Receive HTML (if not already received)                           */
/*********************************************************************/

   html = SUBSTR( tdata, ndx+4)
   dlen = 1
   do while ((LENGTH( html) < clen) & (dlen <> 0))
      Resp = SOCKET( 'RECV', socNum)
      parse var resp retc dlen data
      if retc <> 0 then
      do
          say 'Error receiving data'
          say resp
          exit
      end
      if p.trace = 'Y' then
      do
         say 'Received:'
         call DO_TRACE data
      end
      html = html || data
   end

/*********************************************************************/
/*  Close the socket                                                 */
/*********************************************************************/

   resp = SOCKET( 'CLOSE', socNum)

/*********************************************************************/
/*  terminate the REXX socket set                                    */
/*********************************************************************/

   resp = SOCKET( 'TERMINATE', tcpId)

   return html

/*********************************************************************/
/*  trace                                                            */
/*********************************************************************/
DO_TRACE: procedure
   pad = '      '
   parse arg data
   do i = 1 to LENGTH( data) by 16
      lc = RIGHT( D2X( i-1), 4, '0')
      w1 = C2X( SUBSTR( data, i, 4))
      w2 = C2X( SUBSTR( data, i+4, 4))
      w3 = C2X( SUBSTR( data, i+8, 4))
      w4 = C2X( SUBSTR( data, i+12, 4))
      if i+15 >= LENGTH( data) then
      do
         x = (LENGTH( data)-i)+1
         x = x * 2
         if x > 24 then
            x = x + 1
         if x > 16 then
            x = x + 1
         if x > 8 then
            x = x + 1
         dat1 = LEFT( LEFT(w1 w2 w3 w4, x), 35)
         say pad lc'+' w1 w2 w3 w4 '+'SUBSTR( data, i, 16)'+'
      end
      else
         say pad lc'+' w1 w2 w3 w4 '+'SUBSTR( data, i, 16)'+'
   end
   return 0