HMS

Home Media Server for Roku Players
git clone https://www.brianlane.com/git/HMS
Log | Files | Refs | README | LICENSE

urlUtils.brs (10289B)


      1 '**********************************************************
      2 '**  Video Player Example Application - URL Utilities 
      3 '**  November 2009
      4 '**  Copyright (c) 2009 Roku Inc. All Rights Reserved.
      5 '**********************************************************
      6 
      7 REM ******************************************************
      8 REM Constucts a URL Transfer object
      9 REM ******************************************************
     10 
     11 Function CreateURLTransferObject(url As String) as Object
     12     obj = CreateObject("roUrlTransfer")
     13     obj.SetPort(CreateObject("roMessagePort"))
     14     obj.SetUrl(url)
     15     obj.AddHeader("Content-Type", "application/x-www-form-urlencoded")
     16     obj.EnableEncodings(true)
     17     return obj
     18 End Function
     19 
     20 REM ******************************************************
     21 REM Url Query builder
     22 REM so this is a quick and dirty name/value encoder/accumulator
     23 REM ******************************************************
     24 
     25 Function NewHttp(url As String) as Object
     26     obj = CreateObject("roAssociativeArray")
     27     obj.Http                        = CreateURLTransferObject(url)
     28     obj.FirstParam                  = true
     29     obj.AddParam                    = http_add_param
     30     obj.AddRawQuery                 = http_add_raw_query
     31     obj.GetToStringWithRetry        = http_get_to_string_with_retry
     32     obj.PrepareUrlForQuery          = http_prepare_url_for_query
     33     obj.GetToStringWithTimeout      = http_get_to_string_with_timeout
     34     obj.PostFromStringWithTimeout   = http_post_from_string_with_timeout
     35 
     36     if Instr(1, url, "?") > 0 then obj.FirstParam = false
     37 
     38     return obj
     39 End Function
     40 
     41 
     42 REM ******************************************************
     43 REM Constucts a URL Transfer object 2
     44 REM ******************************************************
     45 
     46 Function CreateURLTransferObject2(url As String, contentHeader As String) as Object
     47     obj = CreateObject("roUrlTransfer")
     48     obj.SetPort(CreateObject("roMessagePort"))
     49     obj.SetUrl(url)
     50     obj.AddHeader("Content-Type", contentHeader)
     51     obj.EnableEncodings(true)
     52     return obj
     53 End Function
     54 
     55 REM ******************************************************
     56 REM Url Query builder 2
     57 REM so this is a quick and dirty name/value encoder/accumulator
     58 REM ******************************************************
     59 
     60 Function NewHttp2(url As String, contentHeader As String) as Object
     61     obj = CreateObject("roAssociativeArray")
     62     obj.Http                        = CreateURLTransferObject2(url, contentHeader)
     63     obj.FirstParam                  = true
     64     obj.AddParam                    = http_add_param
     65     obj.AddRawQuery                 = http_add_raw_query
     66     obj.GetToStringWithRetry        = http_get_to_string_with_retry
     67     obj.PrepareUrlForQuery          = http_prepare_url_for_query
     68     obj.GetToStringWithTimeout      = http_get_to_string_with_timeout
     69     obj.PostFromStringWithTimeout   = http_post_from_string_with_timeout
     70 
     71     if Instr(1, url, "?") > 0 then obj.FirstParam = false
     72 
     73     return obj
     74 End Function
     75 
     76 
     77 REM ******************************************************
     78 REM HttpEncode - just encode a string
     79 REM ******************************************************
     80 
     81 Function HttpEncode(str As String) As String
     82     o = CreateObject("roUrlTransfer")
     83     return o.Escape(str)
     84 End Function
     85 
     86 REM ******************************************************
     87 REM Prepare the current url for adding query parameters
     88 REM Automatically add a '?' or '&' as necessary
     89 REM ******************************************************
     90 
     91 Function http_prepare_url_for_query() As String
     92     url = m.Http.GetUrl()
     93     if m.FirstParam then
     94         url = url + "?"
     95         m.FirstParam = false
     96     else
     97         url = url + "&"
     98     endif
     99     m.Http.SetUrl(url)
    100     return url
    101 End Function
    102 
    103 REM ******************************************************
    104 REM Percent encode a name/value parameter pair and add the
    105 REM the query portion of the current url
    106 REM Automatically add a '?' or '&' as necessary
    107 REM Prevent duplicate parameters
    108 REM ******************************************************
    109 
    110 Function http_add_param(name As String, val As String) as Void
    111     q = m.Http.Escape(name)
    112     q = q + "="
    113     url = m.Http.GetUrl()
    114     if Instr(1, url, q) > 0 return    'Parameter already present
    115     q = q + m.Http.Escape(val)
    116     m.AddRawQuery(q)
    117 End Function
    118 
    119 REM ******************************************************
    120 REM Tack a raw query string onto the end of the current url
    121 REM Automatically add a '?' or '&' as necessary
    122 REM ******************************************************
    123 
    124 Function http_add_raw_query(query As String) as Void
    125     url = m.PrepareUrlForQuery()
    126     url = url + query
    127     m.Http.SetUrl(url)
    128 End Function
    129 
    130 REM ******************************************************
    131 REM Performs Http.AsyncGetToString() in a retry loop
    132 REM with exponential backoff. To the outside
    133 REM world this appears as a synchronous API.
    134 REM ******************************************************
    135 
    136 Function http_get_to_string_with_retry() as String
    137     timeout%         = 1500
    138     num_retries%     = 5
    139 
    140     str = ""
    141     while num_retries% > 0
    142 '        print "httpget try " + itostr(num_retries%)
    143         if (m.Http.AsyncGetToString())
    144             event = wait(timeout%, m.Http.GetPort())
    145             if type(event) = "roUrlEvent"
    146                 str = event.GetString()
    147                 exit while
    148             elseif event = invalid
    149                 m.Http.AsyncCancel()
    150                 REM reset the connection on timeouts
    151                 m.Http = CreateURLTransferObject(m.Http.GetUrl())
    152                 timeout% = 2 * timeout%
    153             else
    154                 print "roUrlTransfer::AsyncGetToString(): unknown event"
    155             endif
    156         endif
    157 
    158         num_retries% = num_retries% - 1
    159     end while
    160     
    161     return str
    162 End Function
    163 
    164 REM ******************************************************
    165 REM Performs Http.AsyncGetToString() with a single timeout in seconds
    166 REM To the outside world this appears as a synchronous API.
    167 REM ******************************************************
    168 
    169 Function http_get_to_string_with_timeout(seconds as Integer) as String
    170     timeout% = 1000 * seconds
    171 
    172     str = ""
    173     m.Http.EnableFreshConnection(true) 'Don't reuse existing connections
    174     if (m.Http.AsyncGetToString())
    175         event = wait(timeout%, m.Http.GetPort())
    176         if type(event) = "roUrlEvent"
    177             str = event.GetString()
    178         elseif event = invalid
    179             Dbg("AsyncGetToString timeout")
    180             m.Http.AsyncCancel()
    181         else
    182             Dbg("AsyncGetToString unknown event", event)
    183         endif
    184     endif
    185 
    186     return str
    187 End Function
    188 
    189 REM ******************************************************
    190 REM Performs Http.AsyncPostFromString() with a single timeout in seconds
    191 REM To the outside world this appears as a synchronous API.
    192 REM ******************************************************
    193 
    194 Function http_post_from_string_with_timeout(val As String, seconds as Integer) as String
    195     timeout% = 1000 * seconds
    196 
    197     str = ""
    198 '    m.Http.EnableFreshConnection(true) 'Don't reuse existing connections
    199     if (m.Http.AsyncPostFromString(val))
    200         event = wait(timeout%, m.Http.GetPort())
    201         if type(event) = "roUrlEvent"
    202             print "1"
    203             str = event.GetString()
    204         elseif event = invalid
    205             print "2"
    206             Dbg("AsyncPostFromString timeout")
    207             m.Http.AsyncCancel()
    208         else
    209             print "3"
    210             Dbg("AsyncPostFromString unknown event", event)
    211         endif
    212     endif
    213 
    214     return str
    215 End Function
    216 
    217 ' ********************************************************************
    218 ' ** Get a HTML page with timeout. Gather up error info and return it
    219 ' ********************************************************************
    220 Function getHTMLWithTimeout(url As String, seconds As Integer) as Object
    221     timeout% = 1000 * seconds
    222 
    223     result = { str : "", error : false, response : 0, reason : "" }
    224 
    225     http = CreateObject("roUrlTransfer")
    226     http.SetUrl(url)
    227     http.SetPort(CreateObject("roMessagePort"))
    228     http.EnableFreshConnection(true) 'Don't reuse existing connections
    229     if (http.AsyncGetToString())
    230         event = wait(timeout%, http.GetPort())
    231         if type(event) = "roUrlEvent"
    232             if event.GetResponseCode() = 200 then
    233                 result.str = event.GetString()
    234             else
    235                 result.error = true
    236                 result.response = event.GetResponseCode()
    237                 result.reason = event.GetFailureReason()
    238             end if
    239         elseif event = invalid
    240             Dbg("AsyncGetToString timeout")
    241             http.AsyncCancel()
    242         else
    243             Dbg("AsyncGetToString unknown event", event)
    244         endif
    245     endif
    246 
    247 '    print "HTTP result: "
    248 '    print result
    249 
    250     return result
    251 End Function
    252 
    253 ' ********************************************************************
    254 ' POST a value to a URL and get the response
    255 ' ********************************************************************
    256 Function postHTMLWithTimeout(url As String, content As String, seconds As Integer) as Object
    257     timeout% = 1000 * seconds
    258 
    259     result = { str : "", error : false, response : 0, reason : "" }
    260 
    261     http = CreateObject("roUrlTransfer")
    262     http.SetUrl(url)
    263     http.SetPort(CreateObject("roMessagePort"))
    264     http.EnableFreshConnection(true) 'Don't reuse existing connections
    265     if (http.AsyncPostFromString(content))
    266         event = wait(timeout%, http.GetPort())
    267         if type(event) = "roUrlEvent"
    268             if event.GetResponseCode() = 200 then
    269                 result.str = event.GetString()
    270             else
    271                 result.error = true
    272                 result.response = event.GetResponseCode()
    273                 result.reason = event.GetFailureReason()
    274             end if
    275         elseif event = invalid
    276             Dbg("AsyncGetToString timeout")
    277             http.AsyncCancel()
    278         else
    279             Dbg("AsyncGetToString unknown event", event)
    280         endif
    281     endif
    282 
    283 '    print "HTTP result: "
    284 '    print result
    285 
    286     return result
    287 End Function
    288 
    289 '************************************************************
    290 '** Check a URL to see if it is valid
    291 '************************************************************
    292 Function isUrlValid( url As String ) As Boolean
    293     result = getHTMLWithTimeout(url, 60)
    294     return not result.error
    295 End Function