content: function init()
      m.jobQueue = {}
      m.port = createObject("roMessagePort")
      m.top.observeField("requestGet", m.port)
      m.top.functionName = "listen"
      m.top.control = "run"
  end function
  
  function listen() as Void ' Task loop that listens for new requests on field 'request' and for responses
    while (true)
          msg = wait(0, m.port)
          mt = type(msg)
          if mt = "roSGNodeEvent"  ' Received new request (observeField)
              if msg.getField() = "requestGet"
                makeAPIRequestGet(msg.getData())
              else
                  print "[ERROR] ContentRetriever: unrecognized field '"; msg.getField(); "'"
              end if
          else if mt = "roUrlEvent" ' Received response from API call
          processResponse(msg)
          else
              print "[ERROR] ContentRetriever: unrecognized event type '"; mt; "'"
          end if
      end while
  end function
  
  function makeAPIRequestGet(request as Object) as Boolean
    ' this is called by listen() when the request field is changed
    ' the value of request will be passed in here as a parameter
    if type(request) = "roAssociativeArray"  ' sanity check: should pass in roAA
      context = request.context
      if type(context) = "roSGNode" ' sanity check: request should have one field (context as roSGNode)
          parameters = context.parameters
          if type(parameters) = "roAssociativeArray"
            uri = parameters.uri
            if type(uri) = "roString"
              urlTransfer = CreateObject("roUrlTransfer")
              urlTransfer.SetCertificatesFile("common:/certs/ca-bundle.crt")
              urlTransfer.AddHeader("X-Roku-Reserved-Dev-Id", "")
              urlTransfer.AddHeader("Content-Type","application/json")
              urlTransfer.RetainBodyOnError(true)
  
              urlTransfer.InitClientCertificates()
              urlTransfer.setUrl(uri)
              urlTransfer.setPort(m.port)
              if (urlTransfer.AsyncGetToString()) ' response will be caught by listen()
                id = stri(urlTransfer.getIdentity()).trim()
                m.jobQueue[id] = {context:context, transfer:urlTransfer} ' saving the urlTransfer object here is essential to keeping it alive (global scope) until the request is finished
                print "Request to "; uri; " successful."
              else
                print "[ERROR] ContentRetriever: invalid uri: "; uri
              end if
            end if
          end if
      end if
    end if
    return true
  end function
  
  function processResponse(msg as Object)
    id = stri(msg.GetSourceIdentity()).trim()
      job = m.jobQueue[id]
      if job <> invalid
          result = {
        uri: job.context.parameters.uri,
        parameters: job.context.parameters,
        code: msg.getResponseCode(),
        content: msg.getString(),
        headers:msg.GetResponseHeaders()
      }
      job.context.response = result ' update response field of context node being watched by caller
          m.jobQueue.delete(id)  ' job has been completed
      else
          print "[ERROR] Received response message for unknown job: "; idkey
      end if
  end function
  
  