1 #!powershell
2 
3 #AnsibleRequires -CSharpUtil Ansible.Basic
4 #Requires -Module Ansible.ModuleUtils.WebRequest
5 
6 $spec = @{
7     options = @{
8         httpbin_host = @{ type = 'str'; required = $true }
9     }
10 }
11 
12 $module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
13 
14 $httpbin_host = $module.Params.httpbin_host
15 
Assert-Equals()16 Function Assert-Equals {
17     param(
18         [Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
19         [Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
20     )
21 
22     $matched = $false
23     if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array] -or $Actual -is [System.Collections.IList]) {
24         $Actual.Count | Assert-Equals -Expected $Expected.Count
25         for ($i = 0; $i -lt $Actual.Count; $i++) {
26             $actualValue = $Actual[$i]
27             $expectedValue = $Expected[$i]
28             Assert-Equals -Actual $actualValue -Expected $expectedValue
29         }
30         $matched = $true
31     } else {
32         $matched = $Actual -ceq $Expected
33     }
34 
35     if (-not $matched) {
36         if ($Actual -is [PSObject]) {
37             $Actual = $Actual.ToString()
38         }
39 
40         $call_stack = (Get-PSCallStack)[1]
41         $module.Result.test = $test
42         $module.Result.actual = $Actual
43         $module.Result.expected = $Expected
44         $module.Result.line = $call_stack.ScriptLineNumber
45         $module.Result.method = $call_stack.Position.Text
46 
47         $module.FailJson("AssertionError: actual != expected")
48     }
49 }
50 
Convert-StreamToString()51 Function Convert-StreamToString {
52     [CmdletBinding()]
53     param (
54         [Parameter(Mandatory=$true)]
55         [System.IO.Stream]
56         $Stream
57     )
58 
59     $ms = New-Object -TypeName System.IO.MemoryStream
60     try {
61         $Stream.CopyTo($ms)
62         [System.Text.Encoding]::UTF8.GetString($ms.ToArray())
63     } finally {
64         $ms.Dispose()
65     }
66 }
67 
68 $tests = [Ordered]@{
69     'GET request over http' = {
70         $r = Get-AnsibleWebRequest -Uri "http://$httpbin_host/get"
71 
72         $r.Method | Assert-Equals -Expected 'GET'
73         $r.Timeout | Assert-Equals -Expected 30000
74         $r.UseDefaultCredentials | Assert-Equals -Expected $false
75         $r.Credentials | Assert-Equals -Expected $null
76         $r.ClientCertificates.Count | Assert-Equals -Expected 0
77         $r.Proxy.Credentials | Assert-Equals -Expected $null
78         $r.UserAgent | Assert-Equals -Expected 'ansible-httpget'
79 
80         $actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
81             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
82 
83             $Response.StatusCode | Assert-Equals -Expected 200
84             Convert-StreamToString -Stream $Stream
85         } | ConvertFrom-Json
86 
87         $actual.headers.'User-Agent' | Assert-Equals -Expected 'ansible-httpget'
88         $actual.headers.'Host' | Assert-Equals -Expected $httpbin_host
89 
90         $module.Result.msg | Assert-Equals -Expected 'OK'
91         $module.Result.status_code | Assert-Equals -Expected 200
92         $module.Result.ContainsKey('elapsed') | Assert-Equals -Expected $true
93     }
94 
95     'GET request over https' = {
96         # url is an alias for the -Uri parameter.
97         $r = Get-AnsibleWebRequest -url "https://$httpbin_host/get"
98 
99         $r.Method | Assert-Equals -Expected 'GET'
100         $r.Timeout | Assert-Equals -Expected 30000
101         $r.UseDefaultCredentials | Assert-Equals -Expected $false
102         $r.Credentials | Assert-Equals -Expected $null
103         $r.ClientCertificates.Count | Assert-Equals -Expected 0
104         $r.Proxy.Credentials | Assert-Equals -Expected $null
105         $r.UserAgent | Assert-Equals -Expected 'ansible-httpget'
106 
107         $actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
108             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
109 
110             $Response.StatusCode | Assert-Equals -Expected 200
111             Convert-StreamToString -Stream $Stream
112         } | ConvertFrom-Json
113 
114         $actual.headers.'User-Agent' | Assert-Equals -Expected 'ansible-httpget'
115         $actual.headers.'Host' | Assert-Equals -Expected $httpbin_host
116     }
117 
118     'POST request' = {
119         $getParams = @{
120             Headers = @{
121                 'Content-Type' = 'application/json'
122             }
123             Method = 'POST'
124             Uri = "https://$httpbin_host/post"
125         }
126         $r = Get-AnsibleWebRequest @getParams
127 
128         $r.Method | Assert-Equals -Expected 'POST'
129         $r.Timeout | Assert-Equals -Expected 30000
130         $r.UseDefaultCredentials | Assert-Equals -Expected $false
131         $r.Credentials | Assert-Equals -Expected $null
132         $r.ClientCertificates.Count | Assert-Equals -Expected 0
133         $r.Proxy.Credentials | Assert-Equals -Expected $null
134         $r.ContentType | Assert-Equals -Expected 'application/json'
135         $r.UserAgent | Assert-Equals -Expected 'ansible-httpget'
136 
137         $body = New-Object -TypeName System.IO.MemoryStream -ArgumentList @(,
138             ([System.Text.Encoding]::UTF8.GetBytes('{"foo":"bar"}'))
139         )
140         $actual = Invoke-WithWebRequest -Module $module -Request $r -Body $body -Script {
141             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
142 
143             $Response.StatusCode | Assert-Equals -Expected 200
144             Convert-StreamToString -Stream $Stream
145         } | ConvertFrom-Json
146 
147         $actual.headers.'User-Agent' | Assert-Equals -Expected 'ansible-httpget'
148         $actual.headers.'Host' | Assert-Equals -Expected $httpbin_host
149         $actual.data | Assert-Equals -Expected '{"foo":"bar"}'
150     }
151 
152     'Safe redirection of GET' = {
153         $r = Get-AnsibleWebRequest -Uri "http://$httpbin_host/redirect/2"
154 
155         Invoke-WithWebRequest -Module $module -Request $r -Script {
156             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
157 
158             $Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get"
159             $Response.StatusCode | Assert-Equals -Expected 200
160         }
161     }
162 
163     'Safe redirection of HEAD' = {
164         $r = Get-AnsibleWebRequest -Uri "http://$httpbin_host/redirect/2" -Method HEAD
165 
166         Invoke-WithWebRequest -Module $module -Request $r -Script {
167             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
168 
169             $Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get"
170             $Response.StatusCode | Assert-Equals -Expected 200
171         }
172     }
173 
174     'Safe redirection of PUT' = {
175         $params = @{
176             Method = 'PUT'
177             Uri = "http://$httpbin_host/redirect-to?url=https://$httpbin_host/put"
178         }
179         $r = Get-AnsibleWebRequest @params
180 
181         Invoke-WithWebRequest -Module $module -Request $r -Script {
182             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
183 
184             $Response.ResponseUri | Assert-Equals -Expected $r.RequestUri
185             $Response.StatusCode | Assert-Equals -Expected 302
186         }
187     }
188 
189     'None redirection of GET' = {
190         $params = @{
191             FollowRedirects = 'None'
192             Uri = "http://$httpbin_host/redirect/2"
193         }
194         $r = Get-AnsibleWebRequest @params
195 
196         Invoke-WithWebRequest -Module $module -Request $r -Script {
197             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
198 
199             $Response.ResponseUri | Assert-Equals -Expected $r.RequestUri
200             $Response.StatusCode | Assert-Equals -Expected 302
201         }
202     }
203 
204     'None redirection of HEAD' = {
205         $params = @{
206             follow_redirects = 'None'
207             method = 'HEAD'
208             Uri = "http://$httpbin_host/redirect/2"
209         }
210         $r = Get-AnsibleWebRequest @params
211 
212         Invoke-WithWebRequest -Module $module -Request $r -Script {
213             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
214 
215             $Response.ResponseUri | Assert-Equals -Expected $r.RequestUri
216             $Response.StatusCode | Assert-Equals -Expected 302
217         }
218     }
219 
220     'None redirection of PUT' = {
221         $params = @{
222             FollowRedirects = 'None'
223             Method = 'PUT'
224             Uri = "http://$httpbin_host/redirect-to?url=https://$httpbin_host/put"
225         }
226         $r = Get-AnsibleWebRequest @params
227 
228         Invoke-WithWebRequest -Module $module -Request $r -Script {
229             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
230 
231             $Response.ResponseUri | Assert-Equals -Expected $r.RequestUri
232             $Response.StatusCode | Assert-Equals -Expected 302
233         }
234     }
235 
236     'All redirection of GET' = {
237         $params = @{
238             FollowRedirects = 'All'
239             Uri = "http://$httpbin_host/redirect/2"
240         }
241         $r = Get-AnsibleWebRequest @params
242 
243         Invoke-WithWebRequest -Module $module -Request $r -Script {
244             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
245 
246             $Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get"
247             $Response.StatusCode | Assert-Equals -Expected 200
248         }
249     }
250 
251     'All redirection of HEAD' = {
252         $params = @{
253             follow_redirects = 'All'
254             method = 'HEAD'
255             Uri = "http://$httpbin_host/redirect/2"
256         }
257         $r = Get-AnsibleWebRequest @params
258 
259         Invoke-WithWebRequest -Module $module -Request $r -Script {
260             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
261 
262             $Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get"
263             $Response.StatusCode | Assert-Equals -Expected 200
264         }
265     }
266 
267     'All redirection of PUT' = {
268         $params = @{
269             FollowRedirects = 'All'
270             Method = 'PUT'
271             Uri = "http://$httpbin_host/redirect-to?url=https://$httpbin_host/put"
272         }
273         $r = Get-AnsibleWebRequest @params
274 
275         Invoke-WithWebRequest -Module $module -Request $r -Script {
276             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
277 
278             $Response.ResponseUri | Assert-Equals -Expected "https://$httpbin_host/put"
279             $Response.StatusCode | Assert-Equals -Expected 200
280         }
281     }
282 
283     'Exceeds maximum redirection - ignored' = {
284         $params = @{
285             MaximumRedirection = 4
286             Uri = "https://$httpbin_host/redirect/5"
287         }
288         $r = Get-AnsibleWebRequest @params
289 
290         Invoke-WithWebRequest -Module $module -Request $r -IgnoreBadResponse -Script {
291             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
292 
293             $Response.ResponseUri | Assert-Equals -Expected "https://$httpbin_host/relative-redirect/1"
294             $Response.StatusCode | Assert-Equals -Expected 302
295         }
296     }
297 
298     'Exceeds maximum redirection - exception' = {
299         $params = @{
300             MaximumRedirection = 1
301             Uri = "https://$httpbin_host/redirect/2"
302         }
303         $r = Get-AnsibleWebRequest @params
304 
305         $failed = $false
306         try {
307             $null = Invoke-WithWebRequest -Module $module -Request $r -Script {}
308         } catch {
309             $_.Exception.GetType().Name | Assert-Equals -Expected 'WebException'
310             $_.Exception.Message | Assert-Equals -Expected 'Too many automatic redirections were attempted.'
311             $failed = $true
312         }
313         $failed | Assert-Equals -Expected $true
314     }
315 
316     'Basic auth as Credential' = {
317         $params = @{
318             Url = "http://$httpbin_host/basic-auth/username/password"
319             UrlUsername = 'username'
320             UrlPassword = 'password'
321         }
322         $r = Get-AnsibleWebRequest @params
323 
324         Invoke-WithWebRequest -Module $module -Request $r -IgnoreBadResponse -Script {
325             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
326 
327             $Response.StatusCode | Assert-Equals -Expected 200
328         }
329     }
330 
331     'Basic auth as Header' = {
332         $params = @{
333             Url = "http://$httpbin_host/basic-auth/username/password"
334             url_username = 'username'
335             url_password = 'password'
336             ForceBasicAuth = $true
337         }
338         $r = Get-AnsibleWebRequest @params
339 
340         Invoke-WithWebRequest -Module $module -Request $r -IgnoreBadResponse -Script {
341             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
342 
343             $Response.StatusCode | Assert-Equals -Expected 200
344         }
345     }
346 
347     'Send request with headers' = {
348         $params = @{
349             Headers = @{
350                 'Content-Length' = 0
351                 testingheader = 'testing_header'
352                 TestHeader = 'test-header'
353                 'User-Agent' = 'test-agent'
354             }
355             Url = "https://$httpbin_host/get"
356         }
357         $r = Get-AnsibleWebRequest @params
358 
359         $actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
360             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
361 
362             $Response.StatusCode | Assert-Equals -Expected 200
363             Convert-StreamToString -Stream $Stream
364         } | ConvertFrom-Json
365 
366         $actual.headers.'Testheader' | Assert-Equals -Expected 'test-header'
367         $actual.headers.'testingheader' | Assert-Equals -Expected 'testing_header'
368         $actual.Headers.'User-Agent' | Assert-Equals -Expected 'test-agent'
369     }
370 
371     'Request with timeout' = {
372         $params = @{
373             Uri = "https://$httpbin_host/delay/5"
374             Timeout = 1
375         }
376         $r = Get-AnsibleWebRequest @params
377 
378         $failed = $false
379         try {
380             $null = Invoke-WithWebRequest -Module $module -Request $r -Script {}
381         } catch {
382             $failed = $true
383             $_.Exception.GetType().Name | Assert-Equals -Expected WebException
384             $_.Exception.Message | Assert-Equals -Expected 'The operation has timed out'
385         }
386         $failed | Assert-Equals -Expected $true
387     }
388 
389     'Request with file URI' = {
390         $filePath = Join-Path $module.Tmpdir -ChildPath 'test.txt'
391         Set-Content -LiteralPath $filePath -Value 'test'
392 
393         $r = Get-AnsibleWebRequest -Uri $filePath
394 
395         $actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
396             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
397 
398             $Response.ContentLength | Assert-Equals -Expected 6
399             Convert-StreamToString -Stream $Stream
400         }
401         $actual | Assert-Equals -Expected "test`r`n"
402         $module.Result.msg | Assert-Equals -Expected "OK"
403         $module.Result.status_code | Assert-Equals -Expected 200
404     }
405 
406     'Web request based on module options' = {
407         Set-Variable complex_args -Scope Global -Value @{
408             url = "https://$httpbin_host/redirect/2"
409             method = 'GET'
410             follow_redirects = 'safe'
411             headers = @{
412                 'User-Agent' = 'other-agent'
413             }
414             http_agent = 'actual-agent'
415             maximum_redirection = 2
416             timeout = 10
417             validate_certs = $false
418         }
419         $spec = @{
420             options = @{
421                 url = @{ type = 'str'; required = $true }
422                 test = @{ type = 'str'; choices = 'abc', 'def'}
423             }
424             mutually_exclusive = @(,@('url', 'test'))
425         }
426 
427         $testModule = [Ansible.Basic.AnsibleModule]::Create(@(), $spec, @(Get-AnsibleWebRequestSpec))
428         $r = Get-AnsibleWebRequest -Url $testModule.Params.url -Module $testModule
429 
430         $actual = Invoke-WithWebRequest -Module $testModule -Request $r -Script {
431             Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
432 
433             $Response.ResponseUri | Assert-Equals -Expected "https://$httpbin_host/get"
434             Convert-StreamToString -Stream $Stream
435         } | ConvertFrom-Json
436         $actual.headers.'User-Agent' | Assert-Equals -Expected 'actual-agent'
437     }
438 
439     'Web request with default proxy' = {
440         $params = @{
441             Uri = "https://$httpbin_host/get"
442         }
443         $r = Get-AnsibleWebRequest @params
444 
445         $null -ne $r.Proxy | Assert-Equals -Expected $true
446     }
447 
448     'Web request with no proxy' = {
449         $params = @{
450             Uri = "https://$httpbin_host/get"
451             UseProxy = $false
452         }
453         $r = Get-AnsibleWebRequest @params
454 
455         $null -eq $r.Proxy | Assert-Equals -Expected $true
456     }
457 }
458 
459 # setup and teardown should favour native tools to create and delete the service and not the util we are testing.
460 foreach ($testImpl in $tests.GetEnumerator()) {
461     Set-Variable -Name complex_args -Scope Global -Value @{}
462     $test = $testImpl.Key
463     &$testImpl.Value
464 }
465 
466 $module.Result.data = "success"
467 $module.ExitJson()
468