kcsc-mcp / src /utils /http_client.py
nicefree19's picture
Upload 105 files
0ee3c92 verified
Raw
History Blame Contribute Delete
6.55 kB
"""
HTTP μš”μ²­ 처리λ₯Ό μœ„ν•œ μœ ν‹Έλ¦¬ν‹° λͺ¨λ“ˆ
"""
import time
import logging
import requests
from typing import Dict, Any, Optional, Union
from src.utils.exceptions import APIError
from src import config
logger = config.setup_logger("http_client")
class HTTPClient:
"""
HTTP μš”μ²­μ„ μ²˜λ¦¬ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈ 클래슀
μž¬μ‹œλ„ 둜직, μ—λŸ¬ 처리 λ“±μ˜ 곡톡 κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.
"""
def __init__(
self,
base_url: str = None,
default_headers: Dict[str, str] = None,
timeout: int = None,
retry_count: int = None,
retry_delay: float = None
):
"""
HTTP ν΄λΌμ΄μ–ΈνŠΈ μ΄ˆκΈ°ν™”
Args:
base_url: κΈ°λ³Έ URL
default_headers: κΈ°λ³Έ HTTP 헀더
timeout: μš”μ²­ νƒ€μž„μ•„μ›ƒ (초)
retry_count: μž¬μ‹œλ„ 횟수
retry_delay: μž¬μ‹œλ„ κ°„ μ§€μ—° μ‹œκ°„ (초)
"""
self.base_url = base_url
self.default_headers = default_headers or {}
self.timeout = timeout or config.REQUEST_TIMEOUT
self.retry_count = retry_count or config.API_RETRY_COUNT
self.retry_delay = retry_delay or config.API_RETRY_DELAY
def _make_url(self, endpoint: str) -> str:
"""
μ—”λ“œν¬μΈνŠΈλ‘œλΆ€ν„° 전체 URL 생성
Args:
endpoint: API μ—”λ“œν¬μΈνŠΈ 경둜
Returns:
μ™„μ „ν•œ URL λ¬Έμžμ—΄
"""
if self.base_url and not endpoint.startswith(('http://', 'https://')):
# μƒλŒ€ 경둜인 경우 κΈ°λ³Έ URLκ³Ό κ²°ν•©
if endpoint.startswith('/'):
return f"{self.base_url}{endpoint}"
return f"{self.base_url}/{endpoint}"
return endpoint
def _prepare_headers(self, headers: Dict[str, str] = None) -> Dict[str, str]:
"""
μš”μ²­ 헀더 μ€€λΉ„
Args:
headers: μš”μ²­λ³„ 헀더
Returns:
λ³‘ν•©λœ 헀더 λ”•μ…”λ„ˆλ¦¬
"""
merged_headers = self.default_headers.copy()
if headers:
merged_headers.update(headers)
return merged_headers
def request(
self,
method: str,
endpoint: str,
params: Dict[str, Any] = None,
data: Any = None,
json: Any = None,
headers: Dict[str, str] = None,
timeout: int = None,
retry_count: int = None,
retry_delay: float = None,
raise_for_status: bool = True
) -> requests.Response:
"""
HTTP μš”μ²­ μˆ˜ν–‰
Args:
method: HTTP λ©”μ„œλ“œ (GET, POST λ“±)
endpoint: API μ—”λ“œν¬μΈνŠΈ
params: URL 쿼리 νŒŒλΌλ―Έν„°
data: μš”μ²­ 데이터
json: JSON μš”μ²­ 데이터
headers: μš”μ²­ 헀더
timeout: μš”μ²­ νƒ€μž„μ•„μ›ƒ (초)
retry_count: μž¬μ‹œλ„ 횟수
retry_delay: μž¬μ‹œλ„ κ°„ μ§€μ—° μ‹œκ°„ (초)
raise_for_status: 응닡 μƒνƒœκ°€ μ—λŸ¬λ©΄ μ˜ˆμ™Έ λ°œμƒ μ—¬λΆ€
Returns:
HTTP 응닡 객체
Raises:
APIError: API 톡신 였λ₯˜ λ°œμƒ μ‹œ
"""
url = self._make_url(endpoint)
merged_headers = self._prepare_headers(headers)
timeout_value = timeout or self.timeout
retry_count_value = retry_count if retry_count is not None else self.retry_count
retry_delay_value = retry_delay if retry_delay is not None else self.retry_delay
# μš”μ²­ λ‘œκΉ…
logger.debug(
f"HTTP {method} μš”μ²­: {url}, νŒŒλΌλ―Έν„°: {params}, "
f"μž¬μ‹œλ„: {retry_count_value}, νƒ€μž„μ•„μ›ƒ: {timeout_value}"
)
# μž¬μ‹œλ„ 루프
for attempt in range(retry_count_value + 1):
try:
response = requests.request(
method=method,
url=url,
params=params,
data=data,
json=json,
headers=merged_headers,
timeout=timeout_value
)
# 응닡 μƒνƒœ 확인
if raise_for_status:
response.raise_for_status()
return response
except requests.RequestException as e:
# λ§ˆμ§€λ§‰ μ‹œλ„μ—μ„œ μ‹€νŒ¨ν•˜λ©΄ μ˜ˆμ™Έ λ°œμƒ
if attempt >= retry_count_value:
status_code = getattr(e.response, 'status_code', None) if hasattr(e, 'response') else None
response_data = None
# 응닡 데이터 μΆ”μΆœ μ‹œλ„
if hasattr(e, 'response') and e.response:
try:
response_data = e.response.json()
except ValueError:
response_data = e.response.text
error_msg = f"HTTP {method} μš”μ²­ μ‹€νŒ¨ (URL: {url}): {str(e)}"
logger.error(error_msg)
raise APIError(
message=error_msg,
status_code=status_code,
response=response_data
) from e
# μž¬μ‹œλ„ μ „ λŒ€κΈ°
wait_time = retry_delay_value * (2 ** attempt) # μ§€μˆ˜ λ°±μ˜€ν”„
logger.warning(
f"HTTP {method} μš”μ²­ μ‹€νŒ¨ (URL: {url}), "
f"{attempt+1}/{retry_count_value+1} 번째 μ‹œλ„, "
f"{wait_time}초 ν›„ μž¬μ‹œλ„: {str(e)}"
)
time.sleep(wait_time)
def get(self, endpoint, params=None, **kwargs):
"""GET μš”μ²­ μˆ˜ν–‰"""
return self.request("GET", endpoint, params=params, **kwargs)
def post(self, endpoint, json=None, data=None, **kwargs):
"""POST μš”μ²­ μˆ˜ν–‰"""
return self.request("POST", endpoint, json=json, data=data, **kwargs)
def put(self, endpoint, json=None, data=None, **kwargs):
"""PUT μš”μ²­ μˆ˜ν–‰"""
return self.request("PUT", endpoint, json=json, data=data, **kwargs)
def delete(self, endpoint, **kwargs):
"""DELETE μš”μ²­ μˆ˜ν–‰"""
return self.request("DELETE", endpoint, **kwargs)