Returns (entry, http_code, body_bytes). http_code=None means a network/timeout error. body_bytes=None means the request failed or returned error status.
(
session: aiohttp.ClientSession,
sem: asyncio.Semaphore,
entry: FeedEntry,
)
| 134 | # ── HTTP fetch ──────────────────────────────────────────────────────────────── |
| 135 | |
| 136 | async def fetch_feed( |
| 137 | session: aiohttp.ClientSession, |
| 138 | sem: asyncio.Semaphore, |
| 139 | entry: FeedEntry, |
| 140 | ) -> tuple[FeedEntry, Optional[int], Optional[bytes]]: |
| 141 | """ |
| 142 | Returns (entry, http_code, body_bytes). |
| 143 | http_code=None means a network/timeout error. |
| 144 | body_bytes=None means the request failed or returned error status. |
| 145 | """ |
| 146 | async with sem: |
| 147 | try: |
| 148 | async with session.get( |
| 149 | entry.xml_url, |
| 150 | timeout=aiohttp.ClientTimeout(total=HTTP_TIMEOUT), |
| 151 | allow_redirects=True, |
| 152 | max_redirects=10, |
| 153 | ) as resp: |
| 154 | code = resp.status |
| 155 | if code >= 400: |
| 156 | return entry, code, None |
| 157 | data = await resp.content.read(MAX_FEED_BYTES) |
| 158 | return entry, code, data |
| 159 | except aiohttp.TooManyRedirects: |
| 160 | return entry, 310, None |
| 161 | except asyncio.TimeoutError: |
| 162 | return entry, None, None |
| 163 | except Exception: |
| 164 | return entry, None, None |
| 165 | |
| 166 | # ── Feed parsing ────────────────────────────────────────────────────────────── |
| 167 |