Metadata-Version: 2.4
Name: bas-http
Version: 1.0.4
Summary: Copy-paste your browser's request, bas handles the rest. No impersonation needed. Zero dependencies.
Author: bas
License-Expression: MIT
Project-URL: Homepage, https://pypi.org/project/bas-http/
Project-URL: Repository, https://pypi.org/project/bas-http/
Project-URL: Issues, https://pypi.org/project/bas-http/
Keywords: http,curl,cookie,scraping,browser,devtools,requests
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# bas

Copy-paste your browser's request, bas handles the rest.

Zero dependencies. Pure Python. Just grab your headers + cookies from DevTools and go.

## Why bas?

| Feature | bas | requests | pycurl | curl_cffi |
|---------|-----|----------|--------|----------|
| Zero dependencies | ✅ | ❌ (urllib3) | ❌ (libcurl) | ❌ (curl-impersonate) |
| Paste curl from DevTools | ✅ | ❌ | ❌ | ❌ |
| Auto Cookie injection | ✅ | ✅ | ❌ | ⚠️ |
| Cookie jar (RFC 6265) | ✅ | ✅ | ❌ | ⚠️ |
| No compilation needed | ✅ | ✅ | ❌ | ❌ |
| Bring your own headers | ✅ | ❌ (generates) | ❌ | ❌ (impersonation) |
| Works on all platforms | ✅ | ✅ | ⚠️ | ⚠️ |
| Session persistence | ✅ | ✅ | ❌ | ✅ |
| Follow redirects | ✅ | ✅ | Manual | ✅ |
| JSON support | ✅ | ✅ | ❌ | ✅ |

## Installation

```bash
pip install bas-http
```

## Quick Start

### Method 1: Paste a curl command from DevTools (Recommended)

This is the fastest way. Copy a curl command from your browser and bas does the rest.

```
1. Open browser DevTools (F12) → Network tab
2. Make a request on the website
3. Right-click the request → "Copy as cURL"
4. Paste into Python
```

```python
import bas

# Paste your curl command (use r'' raw string to preserve backslashes)
s = bas.from_curl(r'''curl "https://example.com/page" ^
  -H "accept: text/html,application/xhtml+xml" ^
  -H "accept-language: en-US,en;q=0.9" ^
  -H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" ^
  -b "cf_clearance=abc123; session_id=xyz; token=def456"''')

# Now make requests — all headers and cookies are auto-injected
r = s.get("https://example.com/page")
print(r.status_code)
print(r.text)

# Follow-up requests keep the same headers and cookies
r2 = s.get("https://example.com/dashboard")
print(r2.status_code)

# Add more cookies manually if needed
s.set_cookie("new_cookie", "value", domain="example.com")
r3 = s.get("https://example.com/api/data")
```

### Method 2: Build headers manually

If you have headers copied from DevTools (not as a curl command):

```python
import bas

s = bas.Session()

# Paste your headers from DevTools → Network → Headers tab
s.headers = {
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
    "accept-language": "en-US,en;q=0.9",
    "cache-control": "max-age=0",
    "sec-ch-ua": '"Chromium";v="137", "Not/A)Brand";v="24"',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"Windows"',
    "sec-fetch-dest": "document",
    "sec-fetch-mode": "navigate",
    "sec-fetch-site": "none",
    "sec-fetch-user": "?1",
    "upgrade-insecure-requests": "1",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
}

# Add your cookies from DevTools → Application → Cookies
s.set_cookie("cf_clearance", "abc123", domain="example.com")
s.set_cookie("session_id", "xyz", domain="example.com")
s.set_cookie("csrf_token", "def456", domain="example.com")

# Go!
r = s.get("https://example.com/page")
print(r.status_code, len(r.text))
```

### Method 3: Headers + cookies in one call

```python
import bas

s = bas.from_headers(
    url="https://example.com",
    headers={
        "user-agent": "Mozilla/5.0 ...",
        "accept": "text/html,...",
    },
    cookies={
        "cf_clearance": "abc123",
        "session": "xyz",
    },
)

r = s.get("https://example.com/page")
```

## HTTP Methods

```python
import bas

s = bas.from_curl(r'''curl "https://example.com" -b "session=abc"''')

# GET
r = s.get("https://example.com/page")

# POST with form data
r = s.post("https://example.com/login", data={"username": "user", "password": "pass"})

# POST with JSON
r = s.post("https://example.com/api", json={"key": "value"})

# PUT
r = s.put("https://example.com/api/123", json={"name": "updated"})

# DELETE
r = s.delete("https://example.com/api/123")

# PATCH
r = s.patch("https://example.com/api/123", json={"name": "patched"})

# HEAD
r = s.head("https://example.com/page")
```

## Response Object

```python
r = s.get("https://example.com/page")

# Status code
print(r.status_code)        # 200
print(r.ok)                  # True (status < 400)
print(r.reason_phrase)       # "OK"

# Content
print(r.text)                # Decoded text
print(r.body)                # Raw bytes
print(r.json)                # Parsed JSON
print(r.content_type)        # "text/html; charset=utf-8"

# Headers
print(r.headers)             # Case-insensitive headers dict
print(r.headers["content-type"])

# URL info
print(r.url)                 # Final URL (after redirects)
print(r.ip)                  # Server IP
print(r.http_version)        # "1.1" or "2.0"
print(r.elapsed)             # Response time in seconds

# Redirect history
for resp in r.history:
    print(resp.status_code, resp.url)

# Raise exception on error
r.raise_for_status()  # Raises HTTPError if status >= 400

# Iterate over content
for chunk in r.iter_content(chunk_size=8192):
    process(chunk)

for line in r.iter_lines():
    print(line)
```

## Cookie Management

### Auto-injection

bas automatically injects cookies into every request. No manual header construction needed.

```python
import bas

s = bas.from_curl(r'''curl "https://example.com" -b "session=abc123; token=xyz"''')

# This request automatically has Cookie: session=abc123; token=xyz
r = s.get("https://example.com/page")

# See what cookies were sent
print(r.request.headers.get("Cookie"))
# "session=abc123; token=xyz"
```

### Set cookies manually

```python
s = bas.Session()
s.set_cookie("name", "value", domain="example.com", path="/")
```

### Get cookies for a URL

```python
cookies = s.get_cookies("https://example.com/page")
print(cookies)  # {"session": "abc123", "token": "xyz"}
```

### Clear all cookies

```python
s.clear_cookies()
```

### Save cookies to file

```python
# JSON format
s.save_cookies("cookies.json")

# Netscape format (compatible with curl/wget)
s.save_cookies("cookies.txt", format="netscape")
```

### Load cookies from file

```python
s.load_cookies("cookies.json")
s.load_cookies("cookies.txt", format="netscape")
```

### Cookie accumulation across requests

When the server sends Set-Cookie headers, bas automatically stores them:

```python
s = bas.Session()

# Server sets cookies in this response
r1 = s.get("https://example.com/login")
# Set-Cookie: session_id=abc123; Path=/
# Set-Cookie: user=john; Path=/

# These cookies are automatically sent with the next request
r2 = s.get("https://example.com/dashboard")
# Cookie: session_id=abc123; user=john
```

### Cookies survive redirects

```python
# Server redirects and sets more cookies
r = s.get("https://example.com/page")
# 302 → https://example.com/dashboard
# Set-Cookie: tracking=xyz; Path=/

# ALL cookies are preserved through redirects
# No cookies lost (unlike bas!)
```

## Session Persistence

Keep a session alive across multiple script runs:

```python
import bas

s = bas.Session()

# Try to load previous session
try:
    s.load_cookies("my_session.json")
except FileNotFoundError:
    pass

# Make requests
r = s.get("https://example.com/page")

# Save session for next run
s.save_cookies("my_session.json")
```

## Parse curl commands

```python
from bas.curl_parser import parse_curl, print_curl_summary

# See what's in a curl command
print_curl_summary(r'''curl "https://example.com" -H "User-Agent: ..." -b "cookie=value"''')

# Or get it as a dict
parsed = parse_curl(r'''curl "https://example.com" -H "User-Agent: ..." -b "cookie=value"''')
print(parsed["method"])     # "GET"
print(parsed["url"])        # "https://example.com"
print(parsed["headers"])    # {"User-Agent": "..."}
print(parsed["cookies"])    # {"cookie": "value"}
print(parsed["user_agent"]) # "Mozilla/5.0 ..."
print(parsed["referer"])    # "..."
```

## SSL Verification

```python
# Disable SSL verification (default is enabled)
s = bas.Session(verify=False)

# Or per-request
r = s.get("https://self-signed.example.com", verify=False)
```

## Timeouts

```python
# Set default timeout (seconds)
s = bas.Session(timeout=60)

# Or per-request
r = s.get("https://slow.example.com", timeout=120)
```

## Redirects

```python
# Follow redirects (default: True)
r = s.get("https://example.com/page")

# Don't follow redirects
r = s.get("https://example.com/page", allow_redirects=False)
print(r.status_code)  # 302
print(r.location)     # "https://example.com/dashboard"

# Set max redirects
s = bas.Session(max_redirects=5)
```

## Proxy Support

```python
# TODO: Proxy support coming in v1.1
```

## Real-World Example: Web Scraping

```python
import bas

# Step 1: Copy curl from DevTools
s = bas.from_curl(r'''curl "https://spaceshooter.net/faucet/ltc" ^
  -H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" ^
  -H "accept-language: en-US,en;q=0.9" ^
  -H "sec-ch-ua: \"Chromium\";v=\"137\", \"Not/A)Brand\";v=\"24\"" ^
  -H "sec-ch-ua-mobile: ?0" ^
  -H "sec-ch-ua-platform: \"Windows\"" ^
  -H "sec-fetch-dest: document" ^
  -H "sec-fetch-mode: navigate" ^
  -H "sec-fetch-site: none" ^
  -H "upgrade-insecure-requests: 1" ^
  -H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" ^
  -b "captcha=rscaptcha; cf_clearance=abc123; ci_session=xyz123; uf=def456"''')

# Step 2: Make the request
r = s.get("https://spaceshooter.net/faucet/ltc")

# Step 3: Parse the response
if r.ok:
    print("Success!")
    print(f"Status: {r.status_code}")
    print(f"Content length: {len(r.text)}")
    print(f"Cookies after request: {s.get_cookies('https://spaceshooter.net')}")
else:
    print(f"Failed: {r.status_code}")
```

## Real-World Example: Form Submission

```python
import bas

# Copy the POST request curl from DevTools
s = bas.from_curl(r'''curl "https://example.com/login" ^
  -H "content-type: application/x-www-form-urlencoded" ^
  -H "user-agent: Mozilla/5.0 ..." ^
  -H "referer: https://example.com/login" ^
  -b "csrf_token=abc123" ^
  --data-raw "username=myuser&password=mypass"''')

r = s.post("https://example.com/login", data={
    "username": "myuser",
    "password": "mypass",
})

print(r.status_code)
```

## Comparison with Other Libraries

### requests

```python
import requests

# Manual header/cookie setup
s = requests.Session()
s.headers["User-Agent"] = "Mozilla/5.0 ..."
s.cookies.set("session", "abc", domain="example.com")

r = s.get("https://example.com")
# Works, but no curl copy-paste support
# Generates its own fingerprint (detectable)
```

### pycurl

```python
import pycurl
from io import BytesIO

# Low-level, verbose setup
c = pycurl.Curl()
c.setopt(c.URL, "https://example.com")
c.setopt(c.HTTPHEADER, ["User-Agent: Mozilla/5.0 ...", "Cookie: session=abc"])
buffer = BytesIO()
c.setopt(c.WRITEDATA, buffer)
c.perform()
c.close()

# No built-in cookie jar, no redirect handling
# Requires libcurl installed on the system
```

### curl_cffi

```python
from curl_cffi.requests import Session

# Requires curl-impersonate installed
s = Session(impersonate="chrome131")
r = s.get("https://example.com")

# Impersonation fingerprint can become outdated
# Cookie handling has known issues
```

### bas

```python
import bas

# Paste curl from DevTools — done
s = bas.from_curl(r'''curl "https://example.com" -b "session=abc"''')

r = s.get("https://example.com")
# All headers auto-applied
# Cookies auto-injected
# Zero setup, zero dependencies
```

## API Reference

### `bas.from_curl(curl_cmd, **kwargs)` → Session

Create a Session from a curl command. Main entry point.

### `bas.from_headers(url, headers, cookies, **kwargs)` → Session

Create a Session from raw headers and cookies.

### `bas.Session(headers, cookies, verify, timeout, allow_redirects, max_redirects)`

Pure Python HTTP session. Zero external dependencies.

**Methods:**
- `get(url, **kwargs)` → Response
- `post(url, **kwargs)` → Response
- `put(url, **kwargs)` → Response
- `delete(url, **kwargs)` → Response
- `patch(url, **kwargs)` → Response
- `head(url, **kwargs)` → Response
- `set_cookie(name, value, domain, path)` → None
- `get_cookies(url)` → dict
- `clear_cookies()` → None
- `save_cookies(filepath, format)` → None
- `load_cookies(filepath, format)` → None

### `bas.Cookie(name, value, domain, path, expires, max_age, secure, http_only, same_site)`

Individual cookie object with RFC 6265 compliance.

### `bas.CookieJar`

Thread-safe cookie container. Full RFC 6265 domain/path matching.

**Methods:**
- `add(cookie)` → None
- `remove(cookie)` → None
- `get(name, domain, path)` → Cookie | None
- `match(url)` → list[Cookie]
- `to_header(url)` → str
- `parse_set_cookie(header, url)` → Cookie
- `save_json(filepath)` → None
- `load_json(filepath)` → None
- `save_netscape(filepath)` → None
- `load_netscape(filepath)` → None

### `bas.Response`

HTTP response object.

**Properties:**
- `status_code` (int): HTTP status code
- `ok` (bool): True if status < 400
- `text` (str): Decoded text content
- `body` (bytes): Raw bytes
- `json` (Any): Parsed JSON
- `headers` (Headers): Response headers
- `url` (str): Final URL
- `elapsed` (float): Response time
- `history` (list): Redirect history
- `cookies` (CookieJar): Cookie jar

## License

MIT
