0
# Plugin Development
1
2
HTTPie provides an extensible plugin system that allows developers to add custom authentication methods, output formatters, content converters, and transport adapters.
3
4
```python
5
from httpie.plugins.base import BasePlugin, AuthPlugin, FormatterPlugin, ConverterPlugin, TransportPlugin
6
from httpie.plugins.registry import plugin_manager
7
from typing import Tuple
8
import requests.auth
9
```
10
11
## Capabilities
12
13
### Base Plugin Class
14
15
All HTTPie plugins inherit from the base plugin class and are automatically discovered when installed.
16
17
```python { .api }
18
class BasePlugin:
19
"""Base class for all HTTPie plugins."""
20
name: str = None # Plugin display name
21
description: str = None # Short description for help text
22
package_name: str = None # Set automatically when loaded
23
```
24
25
### Authentication Plugins
26
27
Create custom authentication methods for APIs that require non-standard authentication schemes.
28
29
```python { .api }
30
class AuthPlugin(BasePlugin):
31
"""Base class for authentication plugins."""
32
33
auth_type: str = None # Auth type identifier for --auth-type
34
auth_require: bool = True # Whether -a argument is required
35
auth_parse: bool = True # Whether to parse -a as username:password
36
netrc_parse: bool = False # Whether to use netrc as fallback
37
prompt_password: bool = True # Whether to prompt for missing password
38
raw_auth: str = None # Raw -a argument value
39
40
def get_auth(self, username: str = None, password: str = None):
41
"""
42
Return a requests.auth.AuthBase instance.
43
44
Args:
45
username: Parsed username (if auth_parse=True)
46
password: Parsed password (if auth_parse=True)
47
48
Returns:
49
requests.auth.AuthBase: Authentication handler
50
"""
51
raise NotImplementedError()
52
```
53
54
**Example Authentication Plugin:**
55
56
```python
57
from httpie.plugins import AuthPlugin
58
import requests.auth
59
60
class APIKeyAuthPlugin(AuthPlugin):
61
name = 'API Key Authentication'
62
description = 'API key in custom header'
63
auth_type = 'api-key'
64
auth_require = True
65
auth_parse = False # Don't parse as username:password
66
67
def get_auth(self, username=None, password=None):
68
# Use raw_auth as the API key
69
api_key = self.raw_auth
70
71
class APIKeyAuth(requests.auth.AuthBase):
72
def __call__(self, request):
73
request.headers['X-API-Key'] = api_key
74
return request
75
76
return APIKeyAuth()
77
78
# Usage: http --auth-type=api-key -a my-secret-key example.com/api
79
```
80
81
### Formatter Plugins
82
83
Customize how HTTPie displays HTTP messages, headers, and response bodies.
84
85
```python { .api }
86
class FormatterPlugin(BasePlugin):
87
"""Base class for output formatter plugins."""
88
89
group_name: str = 'format' # Plugin group identifier
90
enabled: bool = True # Whether formatter is enabled
91
format_options: dict # Formatting options from CLI
92
93
def __init__(self, **kwargs):
94
"""
95
Initialize formatter with options.
96
97
Args:
98
env: Environment instance
99
format_options: Dictionary of format options
100
**kwargs: Additional formatter arguments
101
"""
102
self.enabled = True
103
self.kwargs = kwargs
104
self.format_options = kwargs['format_options']
105
106
def format_headers(self, headers: str) -> str:
107
"""
108
Format HTTP headers for display.
109
110
Args:
111
headers: Raw headers as string
112
113
Returns:
114
str: Formatted headers
115
"""
116
return headers
117
118
def format_body(self, content: str, mime: str) -> str:
119
"""
120
Format response body for display.
121
122
Args:
123
content: Response body as string
124
mime: Content-Type MIME type (e.g., 'application/json')
125
126
Returns:
127
str: Formatted body content
128
"""
129
return content
130
131
def format_metadata(self, metadata: str) -> str:
132
"""
133
Format response metadata for display.
134
135
Args:
136
metadata: Metadata string
137
138
Returns:
139
str: Formatted metadata
140
"""
141
return metadata
142
```
143
144
### Converter Plugins
145
146
Transform binary response data into textual representations for terminal display.
147
148
```python { .api }
149
class ConverterPlugin(BasePlugin):
150
"""Base class for content converter plugins."""
151
152
def __init__(self, mime: str):
153
"""
154
Initialize converter for specific MIME type.
155
156
Args:
157
mime: MIME type this converter handles
158
"""
159
self.mime = mime
160
161
def convert(self, body: bytes) -> Tuple[str, str]:
162
"""
163
Convert binary body to textual representation.
164
165
Args:
166
body: Binary response body
167
168
Returns:
169
Tuple[str, str]: (new_content_type, converted_content)
170
"""
171
raise NotImplementedError()
172
173
@classmethod
174
def supports(cls, mime: str) -> bool:
175
"""
176
Check if this converter supports the given MIME type.
177
178
Args:
179
mime: MIME type to check
180
181
Returns:
182
bool: True if supported
183
"""
184
raise NotImplementedError()
185
```
186
187
**Example Converter Plugin:**
188
189
```python
190
from httpie.plugins import ConverterPlugin
191
import base64
192
193
class Base64ConverterPlugin(ConverterPlugin):
194
name = 'Base64 Decoder'
195
description = 'Decode base64 content for display'
196
197
@classmethod
198
def supports(cls, mime):
199
return mime == 'application/base64'
200
201
def convert(self, body):
202
try:
203
decoded = base64.b64decode(body).decode('utf-8')
204
return 'text/plain', decoded
205
except Exception:
206
return 'text/plain', f'<base64 data: {len(body)} bytes>'
207
```
208
209
### Transport Plugins
210
211
Add support for custom protocols or modify how HTTPie handles HTTP transport.
212
213
```python { .api }
214
class TransportPlugin(BasePlugin):
215
"""Base class for transport adapter plugins."""
216
217
prefix: str = None # URL prefix to mount adapter to
218
219
def get_adapter(self):
220
"""
221
Return a requests transport adapter.
222
223
Returns:
224
requests.adapters.BaseAdapter: Transport adapter instance
225
"""
226
raise NotImplementedError()
227
```
228
229
**Example Transport Plugin:**
230
231
```python
232
from httpie.plugins import TransportPlugin
233
import requests.adapters
234
235
class CustomProtocolPlugin(TransportPlugin):
236
name = 'Custom Protocol'
237
description = 'Support for custom:// URLs'
238
prefix = 'custom://'
239
240
def get_adapter(self):
241
class CustomAdapter(requests.adapters.BaseAdapter):
242
def send(self, request, **kwargs):
243
# Custom request handling logic
244
response = requests.models.Response()
245
response.status_code = 200
246
response.headers['Content-Type'] = 'text/plain'
247
response._content = b'Custom protocol response'
248
return response
249
250
return CustomAdapter()
251
252
# Usage: http custom://example.com/resource
253
```
254
255
### Plugin Registration
256
257
HTTPie automatically discovers plugins installed in the Python environment. Plugins must be properly packaged and declare entry points.
258
259
**setup.py for plugin distribution:**
260
261
```python
262
from setuptools import setup
263
264
setup(
265
name='httpie-my-plugin',
266
version='1.0.0',
267
py_modules=['httpie_my_plugin'],
268
install_requires=['httpie>=3.0.0'],
269
entry_points={
270
'httpie.plugins.auth.v1': [
271
'my_auth = httpie_my_plugin:MyAuthPlugin'
272
],
273
'httpie.plugins.formatter.v1': [
274
'my_formatter = httpie_my_plugin:MyFormatterPlugin'
275
]
276
}
277
)
278
```
279
280
### Plugin Management
281
282
```python { .api }
283
# Plugin registry access
284
from httpie.plugins.registry import plugin_manager
285
286
def list_plugins() -> dict:
287
"""List all available plugins by type."""
288
289
def load_installed_plugins(plugins_dir: str) -> None:
290
"""Load plugins from the specified directory."""
291
```
292
293
### Built-in Plugins
294
295
HTTPie includes several built-in plugins:
296
297
- **Basic Auth**: Username/password authentication
298
- **Digest Auth**: HTTP Digest authentication
299
- **JSON Formatter**: Pretty-print JSON responses
300
- **XML Formatter**: Format XML responses
301
- **Colors Formatter**: Syntax highlighting
302
303
### Error Handling
304
305
Plugins should handle errors gracefully and provide meaningful error messages:
306
307
```python
308
class MyAuthPlugin(AuthPlugin):
309
def get_auth(self, username=None, password=None):
310
if not self.raw_auth:
311
raise ValueError("API key is required")
312
313
try:
314
# Plugin logic here
315
return MyAuth(self.raw_auth)
316
except Exception as e:
317
raise ValueError(f"Authentication setup failed: {e}")
318
```
319
320
Plugin errors are reported with `ExitStatus.PLUGIN_ERROR` (exit code 7).