uri/win_uri: Make method a free text field (#49719)
* uri/win_uri: Make method a free text field Since various interfaces introduce their own HTTP method (e.g. like PROPFIND, LIST or TRACE) it's better to leave this up to the user. * Fix HTTP method check in module_utils urls * Add integration test for method UNKNOWN * Clarify the change as requested during review
This commit is contained in:
parent
81ec48c7b4
commit
4e6c113bf0
7 changed files with 50 additions and 11 deletions
|
@ -1128,8 +1128,6 @@ class Request:
|
|||
urllib_request.install_opener(opener)
|
||||
|
||||
data = to_bytes(data, nonstring='passthru')
|
||||
if method not in ('OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT', 'PATCH'):
|
||||
raise ConnectionError('invalid HTTP request method; %s' % method)
|
||||
request = RequestWithMethod(url, method, data)
|
||||
|
||||
# add the custom agent header, to help prevent issues
|
||||
|
|
|
@ -61,9 +61,10 @@ options:
|
|||
version_added: "2.0"
|
||||
method:
|
||||
description:
|
||||
- The HTTP method of the request or response. It MUST be uppercase.
|
||||
- The HTTP method of the request or response.
|
||||
- In more recent versions we do not restrict the method at the module level anymore
|
||||
but it still must be a valid method accepted by the service handling the request.
|
||||
type: str
|
||||
choices: [ CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, REFRESH, TRACE ]
|
||||
default: GET
|
||||
return_content:
|
||||
description:
|
||||
|
@ -303,6 +304,7 @@ import cgi
|
|||
import datetime
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
@ -516,7 +518,7 @@ def main():
|
|||
body=dict(type='raw'),
|
||||
body_format=dict(type='str', default='raw', choices=['form-urlencoded', 'json', 'raw']),
|
||||
src=dict(type='path'),
|
||||
method=dict(type='str', default='GET', choices=['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'REFRESH', 'TRACE']),
|
||||
method=dict(type='str', default='GET'),
|
||||
return_content=dict(type='bool', default=False),
|
||||
follow_redirects=dict(type='str', default='safe', choices=['all', 'no', 'none', 'safe', 'urllib2', 'yes']),
|
||||
creates=dict(type='path'),
|
||||
|
@ -538,7 +540,7 @@ def main():
|
|||
url = module.params['url']
|
||||
body = module.params['body']
|
||||
body_format = module.params['body_format'].lower()
|
||||
method = module.params['method']
|
||||
method = module.params['method'].upper()
|
||||
dest = module.params['dest']
|
||||
return_content = module.params['return_content']
|
||||
creates = module.params['creates']
|
||||
|
@ -548,6 +550,9 @@ def main():
|
|||
|
||||
dict_headers = module.params['headers']
|
||||
|
||||
if not re.match('^[A-Z]+$', method):
|
||||
module.fail_json(msg="Parameter 'method' needs to be a single word in uppercase, like GET or POST.")
|
||||
|
||||
if body_format == 'json':
|
||||
# Encode the body unless its a string, then assume it is pre-formatted JSON
|
||||
if not isinstance(body, string_types):
|
||||
|
|
|
@ -15,7 +15,6 @@ $spec = @{
|
|||
method = @{
|
||||
type = "str"
|
||||
default = "GET"
|
||||
choices = "CONNECT", "DELETE", "GET", "HEAD", "MERGE", "OPTIONS", "PATCH", "POST", "PUT", "REFRESH", "TRACE"
|
||||
}
|
||||
content_type = @{ type = "str" }
|
||||
headers = @{ type = "dict" }
|
||||
|
@ -44,7 +43,7 @@ $spec = @{
|
|||
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
|
||||
|
||||
$url = $module.Params.url
|
||||
$method = $module.Params.method
|
||||
$method = $module.Params.method.ToUpper()
|
||||
$content_type = $module.Params.content_type
|
||||
$headers = $module.Params.headers
|
||||
$body = $module.Params.body
|
||||
|
@ -68,6 +67,10 @@ $JSON_CANDIDATES = @('text', 'json', 'javascript')
|
|||
$module.Result.elapsed = 0
|
||||
$module.Result.url = $url
|
||||
|
||||
if (-not ($method -cmatch '^[A-Z]+$')) {
|
||||
$module.FailJson("Parameter 'method' needs to be a single word in uppercase, like GET or POST.")
|
||||
}
|
||||
|
||||
if ($creates -and (Test-AnsiblePath -Path $creates)) {
|
||||
$module.Result.skipped = $true
|
||||
$module.Result.msg = "The 'creates' file or directory ($creates) already exists."
|
||||
|
|
|
@ -28,7 +28,6 @@ options:
|
|||
description:
|
||||
- The HTTP Method of the request or response.
|
||||
type: str
|
||||
choices: [ CONNECT, DELETE, GET, HEAD, MERGE, OPTIONS, PATCH, POST, PUT, REFRESH, TRACE ]
|
||||
default: GET
|
||||
content_type:
|
||||
description:
|
||||
|
|
|
@ -390,6 +390,20 @@
|
|||
- result is failure
|
||||
- "'failed to parse body as form_urlencoded: too many values to unpack' in result.msg"
|
||||
|
||||
- name: Validate invalid method
|
||||
uri:
|
||||
url: https://{{ httpbin_host }}/anything
|
||||
method: UNKNOWN
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Assert invalid method fails
|
||||
assert:
|
||||
that:
|
||||
- result is failure
|
||||
- result.status == 405
|
||||
- "'METHOD NOT ALLOWED' in result.msg"
|
||||
|
||||
- name: Test client cert auth, no certs
|
||||
uri:
|
||||
url: "https://ansible.http.tests/ssl_client_verify"
|
||||
|
|
|
@ -336,6 +336,20 @@
|
|||
- get_custom_header.json.headers['Test-Header'] == 'hello'
|
||||
- get_custom_header.json.headers['Another-Header'] == 'world'
|
||||
|
||||
- name: Validate invalid method
|
||||
win_uri:
|
||||
url: https://{{ httpbin_host }}/anything
|
||||
method: UNKNOWN
|
||||
register: invalid_method
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Assert invalid method fails
|
||||
assert:
|
||||
that:
|
||||
- invalid_method is failure
|
||||
- invalid_method.status_code == 405
|
||||
- invalid_method.status_description == 'METHOD NOT ALLOWED'
|
||||
|
||||
# client cert auth tests
|
||||
|
||||
- name: get request with timeout
|
||||
|
|
|
@ -363,8 +363,14 @@ def test_Request_open_cookies(urlopen_mock, install_opener_mock):
|
|||
|
||||
|
||||
def test_Request_open_invalid_method(urlopen_mock, install_opener_mock):
|
||||
with pytest.raises(ConnectionError):
|
||||
r = Request().open('BOGUS', 'https://ansible.com/')
|
||||
r = Request().open('UNKNOWN', 'https://ansible.com/')
|
||||
|
||||
args = urlopen_mock.call_args[0]
|
||||
req = args[0]
|
||||
|
||||
assert req.data is None
|
||||
assert req.get_method() == 'UNKNOWN'
|
||||
# assert r.status == 504
|
||||
|
||||
|
||||
def test_Request_open_custom_method(urlopen_mock, install_opener_mock):
|
||||
|
|
Loading…
Reference in a new issue