mirror of
https://github.com/httpie/cli.git
synced 2024-11-24 16:53:35 +01:00
Better DNS error handling (#1249)
* Better DNS error handling * Update httpie/core.py Co-authored-by: Batuhan Taskaya <isidentical@gmail.com> Co-authored-by: Jakub Roztocil <jakub@roztocil.co>
This commit is contained in:
parent
be87da8bbd
commit
e0e03f3237
@ -15,6 +15,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Added support for basic JSON types on `--form`/`--multipart` when using JSON only operators (`:=`/`:=@`). ([#1212](https://github.com/httpie/httpie/issues/1212))
|
- Added support for basic JSON types on `--form`/`--multipart` when using JSON only operators (`:=`/`:=@`). ([#1212](https://github.com/httpie/httpie/issues/1212))
|
||||||
- Added support for automatically enabling `--stream` when `Content-Type` is `text/event-stream`. ([#376](https://github.com/httpie/httpie/issues/376))
|
- Added support for automatically enabling `--stream` when `Content-Type` is `text/event-stream`. ([#376](https://github.com/httpie/httpie/issues/376))
|
||||||
- Added new `pie-dark`/`pie-light` (and `pie`) styles that match with [HTTPie for Web and Desktop](https://httpie.io/product). ([#1237](https://github.com/httpie/httpie/issues/1237))
|
- Added new `pie-dark`/`pie-light` (and `pie`) styles that match with [HTTPie for Web and Desktop](https://httpie.io/product). ([#1237](https://github.com/httpie/httpie/issues/1237))
|
||||||
|
- Added support for better error handling on DNS failures. ([#1248](https://github.com/httpie/httpie/issues/1248))
|
||||||
- Broken plugins will no longer crash the whole application. ([#1204](https://github.com/httpie/httpie/issues/1204))
|
- Broken plugins will no longer crash the whole application. ([#1204](https://github.com/httpie/httpie/issues/1204))
|
||||||
- Fixed auto addition of XML declaration to every formatted XML response. ([#1156](https://github.com/httpie/httpie/issues/1156))
|
- Fixed auto addition of XML declaration to every formatted XML response. ([#1156](https://github.com/httpie/httpie/issues/1156))
|
||||||
- Fixed highlighting when `Content-Type` specifies `charset`. ([#1242](https://github.com/httpie/httpie/issues/1242))
|
- Fixed highlighting when `Content-Type` specifies `charset`. ([#1242](https://github.com/httpie/httpie/issues/1242))
|
||||||
|
@ -2,6 +2,7 @@ import argparse
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
|
import socket
|
||||||
from typing import List, Optional, Tuple, Union, Callable
|
from typing import List, Optional, Tuple, Union, Callable
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
@ -21,6 +22,7 @@ from .models import (
|
|||||||
from .output.writer import write_message, write_stream, MESSAGE_SEPARATOR_BYTES
|
from .output.writer import write_message, write_stream, MESSAGE_SEPARATOR_BYTES
|
||||||
from .plugins.registry import plugin_manager
|
from .plugins.registry import plugin_manager
|
||||||
from .status import ExitStatus, http_status_to_exit_status
|
from .status import ExitStatus, http_status_to_exit_status
|
||||||
|
from .utils import unwrap_context
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyDefaultArgument
|
# noinspection PyDefaultArgument
|
||||||
@ -41,6 +43,21 @@ def raw_main(
|
|||||||
include_debug_info = '--debug' in args
|
include_debug_info = '--debug' in args
|
||||||
include_traceback = include_debug_info or '--traceback' in args
|
include_traceback = include_debug_info or '--traceback' in args
|
||||||
|
|
||||||
|
def handle_generic_error(e, annotation=None):
|
||||||
|
msg = str(e)
|
||||||
|
if hasattr(e, 'request'):
|
||||||
|
request = e.request
|
||||||
|
if hasattr(request, 'url'):
|
||||||
|
msg = (
|
||||||
|
f'{msg} while doing a {request.method}'
|
||||||
|
f' request to URL: {request.url}'
|
||||||
|
)
|
||||||
|
if annotation:
|
||||||
|
msg += annotation
|
||||||
|
env.log_error(f'{type(e).__name__}: {msg}')
|
||||||
|
if include_traceback:
|
||||||
|
raise
|
||||||
|
|
||||||
if include_debug_info:
|
if include_debug_info:
|
||||||
print_debug_info(env)
|
print_debug_info(env)
|
||||||
if args == ['--debug']:
|
if args == ['--debug']:
|
||||||
@ -90,19 +107,23 @@ def raw_main(
|
|||||||
f'Too many redirects'
|
f'Too many redirects'
|
||||||
f' (--max-redirects={parsed_args.max_redirects}).'
|
f' (--max-redirects={parsed_args.max_redirects}).'
|
||||||
)
|
)
|
||||||
|
except requests.exceptions.ConnectionError as exc:
|
||||||
|
annotation = None
|
||||||
|
original_exc = unwrap_context(exc)
|
||||||
|
if isinstance(original_exc, socket.gaierror):
|
||||||
|
if original_exc.errno == socket.EAI_AGAIN:
|
||||||
|
annotation = '\nCouldn\'t connect to a DNS server. Perhaps check your connection and try again.'
|
||||||
|
elif original_exc.errno == socket.EAI_NONAME:
|
||||||
|
annotation = '\nCouldn\'t resolve the given hostname. Perhaps check it and try again.'
|
||||||
|
propagated_exc = original_exc
|
||||||
|
else:
|
||||||
|
propagated_exc = exc
|
||||||
|
|
||||||
|
handle_generic_error(propagated_exc, annotation=annotation)
|
||||||
|
exit_status = ExitStatus.ERROR
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# TODO: Further distinction between expected and unexpected errors.
|
# TODO: Further distinction between expected and unexpected errors.
|
||||||
msg = str(e)
|
handle_generic_error(e)
|
||||||
if hasattr(e, 'request'):
|
|
||||||
request = e.request
|
|
||||||
if hasattr(request, 'url'):
|
|
||||||
msg = (
|
|
||||||
f'{msg} while doing a {request.method}'
|
|
||||||
f' request to URL: {request.url}'
|
|
||||||
)
|
|
||||||
env.log_error(f'{type(e).__name__}: {msg}')
|
|
||||||
if include_traceback:
|
|
||||||
raise
|
|
||||||
exit_status = ExitStatus.ERROR
|
exit_status = ExitStatus.ERROR
|
||||||
|
|
||||||
return exit_status
|
return exit_status
|
||||||
|
@ -229,3 +229,11 @@ def split(iterable: Iterable[T], key: Callable[[T], bool]) -> Tuple[List[T], Lis
|
|||||||
else:
|
else:
|
||||||
right.append(item)
|
right.append(item)
|
||||||
return left, right
|
return left, right
|
||||||
|
|
||||||
|
|
||||||
|
def unwrap_context(exc: Exception) -> Optional[Exception]:
|
||||||
|
context = exc.__context__
|
||||||
|
if isinstance(context, Exception):
|
||||||
|
return unwrap_context(context)
|
||||||
|
else:
|
||||||
|
return exc
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import pytest
|
||||||
|
import socket
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from pytest import raises
|
from pytest import raises
|
||||||
from requests import Request
|
from requests import Request
|
||||||
@ -31,6 +33,21 @@ def test_error_traceback(program):
|
|||||||
http('--traceback', 'www.google.com')
|
http('--traceback', 'www.google.com')
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('httpie.core.program')
|
||||||
|
@pytest.mark.parametrize("error_code, expected_message", [
|
||||||
|
(socket.EAI_AGAIN, "check your connection"),
|
||||||
|
(socket.EAI_NONAME, "check the URL"),
|
||||||
|
])
|
||||||
|
def test_error_custom_dns(program, error_code, expected_message):
|
||||||
|
exc = ConnectionError('Connection aborted')
|
||||||
|
exc.__context__ = socket.gaierror(error_code, "<test>")
|
||||||
|
program.side_effect = exc
|
||||||
|
|
||||||
|
r = http('www.google.com', tolerate_error_exit_status=True)
|
||||||
|
assert r.exit_status == ExitStatus.ERROR
|
||||||
|
assert expected_message in r.stderr
|
||||||
|
|
||||||
|
|
||||||
def test_max_headers_limit(httpbin_both):
|
def test_max_headers_limit(httpbin_both):
|
||||||
with raises(ConnectionError) as e:
|
with raises(ConnectionError) as e:
|
||||||
http('--max-headers=1', httpbin_both + '/get')
|
http('--max-headers=1', httpbin_both + '/get')
|
||||||
|
Loading…
Reference in New Issue
Block a user