Archived
1
0
Fork 0
This repository has been archived on 2024-04-26. You can view files and clone it, but cannot push or open issues or pull requests.
akari-bot/core/utils/http.py

189 lines
8.1 KiB
Python
Raw Normal View History

2022-08-01 15:09:59 +00:00
import asyncio.exceptions
2022-07-22 14:59:28 +00:00
import re
2022-07-31 08:27:58 +00:00
import socket
2022-07-22 16:01:23 +00:00
import urllib.parse
2022-07-31 08:27:58 +00:00
from typing import Union
import aiohttp
import filetype as ft
from aiofile import async_open
from aiohttp import TCPConnector
2022-07-31 08:27:58 +00:00
from tenacity import retry, wait_fixed, stop_after_attempt
from config import Config
from core.logger import Logger
from .cache import random_cache_path
2022-08-18 18:10:42 +00:00
from ..exceptions import NoReportException
2022-06-12 07:07:53 +00:00
2023-01-16 11:46:55 +00:00
logging_resp = False
debug = Config('debug')
proxy = ''
2023-01-16 11:46:55 +00:00
2022-07-29 04:04:10 +00:00
def private_ip_check(url: str):
'''检查是否为私有IP若是则抛出ValueError异常。
2022-07-22 14:59:28 +00:00
2022-07-29 04:04:10 +00:00
:param url: 需要检查的url'''
hostname = urllib.parse.urlparse(url).hostname
2022-07-22 14:59:28 +00:00
addr_info = socket.getaddrinfo(hostname, 80)
private_ips = re.compile(
r'^(?:127\.|0?10\.|172\.0?1[6-9]\.|172\.0?2[0-9]\.172\.0?3[01]\.|192\.168\.|169\.254\.|::1|[fF][cCdD][0-9a-fA-F]{2}:|[fF][eE][89aAbB][0-9a-fA-F]:)')
addr = addr_info[0][4][0]
2022-07-29 04:04:10 +00:00
if private_ips.match(addr):
raise ValueError(
f'Attempt of requesting private IP addresses is not allowed, requesting {hostname}.')
2022-06-12 07:07:53 +00:00
async def get_url(url: str, status_code: int = False, headers: dict = None, params: dict = None, fmt=None, timeout=20, attempt=3,
2023-01-19 06:30:46 +00:00
request_private_ip=False, logging_err_resp=True):
2022-06-12 07:07:53 +00:00
"""利用AioHttp获取指定url的内容。
:param url: 需要获取的url
:param status_code: 指定请求到的状态码若不符则抛出ValueError
:param headers: 请求时使用的http头
:param params: 请求时使用的参数
2022-06-12 07:07:53 +00:00
:param fmt: 指定返回的格式
2022-06-23 13:51:53 +00:00
:param timeout: 超时时间
2023-01-16 06:46:37 +00:00
:param attempt: 指定请求尝试次数
2022-08-21 03:22:45 +00:00
:param request_private_ip: 是否允许请求私有IP
2023-01-19 06:30:46 +00:00
:param logging_err_resp: 是否记录错误响应
2022-06-12 07:07:53 +00:00
:returns: 指定url的内容字符串
"""
2023-01-16 06:46:37 +00:00
@retry(stop=stop_after_attempt(attempt), wait=wait_fixed(3), reraise=True)
async def get_():
Logger.debug(f'[GET] {url}')
if not Config('allow_request_private_ip') and not request_private_ip:
private_ip_check(url)
async with aiohttp.ClientSession(headers=headers,
connector=TCPConnector(verify_ssl=False) if debug else None, ) as session:
2023-01-16 06:46:37 +00:00
try:
async with session.get(url, timeout=aiohttp.ClientTimeout(total=timeout), headers=headers,
proxy=proxy, params=params) as req:
2023-01-16 06:46:37 +00:00
Logger.debug(f'[{req.status}] {url}')
2023-01-16 11:46:55 +00:00
if logging_resp:
Logger.debug(await req.read())
2023-01-16 06:46:37 +00:00
if status_code and req.status != status_code:
2023-01-19 06:30:46 +00:00
if not logging_resp and logging_err_resp:
2023-01-17 10:24:17 +00:00
Logger.error(await req.read())
2023-01-16 06:46:37 +00:00
raise ValueError(
f'{str(req.status)}[Ke:Image,path=https://http.cat/{str(req.status)}.jpg]')
if fmt is not None:
if hasattr(req, fmt):
return await getattr(req, fmt)()
else:
raise ValueError(f"NoSuchMethod: {fmt}")
2022-08-01 15:09:59 +00:00
else:
2023-01-16 06:46:37 +00:00
text = await req.text()
return text
except asyncio.exceptions.TimeoutError:
2023-03-05 11:15:44 +00:00
raise NoReportException('{error.request.api.timeout}')
2022-06-12 07:07:53 +00:00
2023-01-16 06:46:37 +00:00
return await get_()
2022-06-12 07:07:53 +00:00
2023-01-16 06:46:37 +00:00
async def post_url(url: str, data: any = None, status_code: int = False, headers: dict = None, fmt=None, timeout=20,
2023-01-19 06:30:46 +00:00
attempt=3, request_private_ip=False, logging_err_resp=True):
2022-06-12 07:07:53 +00:00
'''发送POST请求。
:param url: 需要发送的url
:param data: 需要发送的数据
2023-01-15 11:29:15 +00:00
:param status_code: 指定请求到的状态码若不符则抛出ValueError
2022-06-12 07:07:53 +00:00
:param headers: 请求时使用的http头
2023-01-15 11:29:15 +00:00
:param fmt: 指定返回的格式
2023-01-16 06:46:37 +00:00
:param timeout: 超时时间
:param attempt: 指定请求尝试次数
2022-08-21 03:22:45 +00:00
:param request_private_ip: 是否允许请求私有IP
2023-01-19 06:30:46 +00:00
:param logging_err_resp: 是否记录错误响应
2022-06-12 07:07:53 +00:00
:returns: 发送请求后的响应'''
2023-01-16 06:46:37 +00:00
@retry(stop=stop_after_attempt(attempt), wait=wait_fixed(3), reraise=True)
async def _post():
Logger.debug(f'[POST] {url}')
if not Config('allow_request_private_ip') and not request_private_ip:
private_ip_check(url)
async with aiohttp.ClientSession(headers=headers,
connector=TCPConnector(verify_ssl=False) if debug else None, ) as session:
2023-01-16 06:46:37 +00:00
try:
async with session.post(url, data=data, headers=headers,
timeout=aiohttp.ClientTimeout(total=timeout),
proxy=proxy) as req:
2023-01-16 06:46:37 +00:00
Logger.debug(f'[{req.status}] {url}')
2023-01-16 11:46:55 +00:00
if logging_resp:
Logger.debug(await req.read())
2023-01-16 06:46:37 +00:00
if status_code and req.status != status_code:
2023-01-19 06:30:46 +00:00
if not logging_resp and logging_err_resp:
2023-01-17 10:24:17 +00:00
Logger.error(await req.read())
2023-01-16 06:46:37 +00:00
raise ValueError(
f'{str(req.status)}[Ke:Image,path=https://http.cat/{str(req.status)}.jpg]')
if fmt is not None:
if hasattr(req, fmt):
return await getattr(req, fmt)()
else:
raise ValueError(f"NoSuchMethod: {fmt}")
2023-01-15 11:29:15 +00:00
else:
2023-01-16 06:46:37 +00:00
text = await req.text()
return text
except asyncio.exceptions.TimeoutError:
raise ValueError(f'Request timeout.')
2023-03-04 08:51:56 +00:00
except Exception as e:
Logger.error(f'Error while requesting {url}: {e}')
raise e
2022-06-12 07:07:53 +00:00
2023-01-16 06:46:37 +00:00
return await _post()
2022-06-12 07:07:53 +00:00
2023-01-16 06:46:37 +00:00
async def download_to_cache(url: str, filename=None, status_code: int = False, method="GET", post_data=None,
2023-01-19 06:30:46 +00:00
headers: dict = None, timeout=20, attempt=3, request_private_ip=False,
logging_err_resp=True) -> Union[str, bool]:
2022-06-12 07:07:53 +00:00
'''利用AioHttp下载指定url的内容并保存到缓存./cache目录
2022-07-22 16:01:23 +00:00
:param url: 需要获取的url
:param filename: 指定保存的文件名默认为随机文件名
2023-01-16 06:46:37 +00:00
:param status_code: 指定请求到的状态码若不符则抛出ValueError
:param method: 指定请求方式
:param post_data: 如果指定请求方式为POST需要传入的数据
:param headers: 请求时使用的http头
2023-01-16 06:46:37 +00:00
:param timeout: 超时时间
:param attempt: 指定请求尝试次数
2022-08-21 03:22:45 +00:00
:param request_private_ip: 是否允许请求私有IP
2023-01-19 06:30:46 +00:00
:param logging_err_resp: 是否记录错误响应
2022-06-12 07:07:53 +00:00
:returns: 文件的相对路径若获取失败则返回False'''
2022-07-28 15:53:15 +00:00
2023-02-07 18:45:40 +00:00
if post_data is not None:
method = 'POST'
2023-01-16 06:46:37 +00:00
@retry(stop=stop_after_attempt(attempt), wait=wait_fixed(3), reraise=True)
async def download_():
if not Config('allow_request_private_ip') and not request_private_ip:
private_ip_check(url)
2023-01-16 06:46:37 +00:00
data = None
if method.upper() == 'GET':
data = await get_url(url, status_code=status_code, headers=headers, fmt='read', timeout=timeout, attempt=1,
2023-01-19 06:30:46 +00:00
request_private_ip=request_private_ip, logging_err_resp=logging_err_resp)
2023-01-16 06:46:37 +00:00
if method.upper() == 'POST':
data = await post_url(url, data=post_data, status_code=status_code, headers=headers, fmt='read',
2023-01-19 06:30:46 +00:00
timeout=timeout, attempt=1, request_private_ip=request_private_ip,
logging_err_resp=logging_err_resp)
2023-01-16 06:46:37 +00:00
if data is not None:
if filename is None:
2023-02-07 18:36:18 +00:00
ftt = ft.match(data).extension
2023-01-16 06:46:37 +00:00
path = f'{random_cache_path()}.{ftt}'
else:
path = Config("cache_path") + f'/{filename}'
async with async_open(path, 'wb+') as file:
await file.write(data)
return path
2023-01-19 06:30:46 +00:00
else:
return False
2023-01-28 05:53:11 +00:00
2023-01-16 06:46:37 +00:00
return await download_()
2022-06-12 07:07:53 +00:00
__all__ = ['get_url', 'post_url', 'download_to_cache']