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