-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttpstatus.py
More file actions
executable file
·301 lines (273 loc) · 13.3 KB
/
Copy pathhttpstatus.py
File metadata and controls
executable file
·301 lines (273 loc) · 13.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
#!/usr/bin/env python3
"""
httpstatus - HTTP status code lookup CLI
Usage:
httpstatus 404
httpstatus 5xx
httpstatus --search "not found"
httpstatus --category client
"""
import argparse
import sys
STATUS_CODES = {
# 1xx Informational
100: ("Continue", "informational",
"The server has received the request headers and the client should proceed to send the request body."),
101: ("Switching Protocols", "informational",
"The requester has asked the server to switch protocols and the server has agreed."),
102: ("Processing", "informational",
"WebDAV. The server has received and is processing the request, but no response is available yet."),
103: ("Early Hints", "informational",
"Used to return some response headers before final HTTP message."),
# 2xx Success
200: ("OK", "success",
"Standard response for successful HTTP requests."),
201: ("Created", "success",
"The request has been fulfilled, resulting in the creation of a new resource."),
202: ("Accepted", "success",
"The request has been accepted for processing, but the processing has not been completed."),
203: ("Non-Authoritative Information", "success",
"The server is a transforming proxy that received a 200 OK from its origin."),
204: ("No Content", "success",
"The server successfully processed the request and is not returning any content."),
205: ("Reset Content", "success",
"The server successfully processed the request, asks that the requester reset its document view."),
206: ("Partial Content", "success",
"The server is delivering only part of the resource due to a range header sent by the client."),
207: ("Multi-Status", "success",
"WebDAV. The message body contains multiple separate response codes."),
208: ("Already Reported", "success",
"WebDAV. The members of a DAV binding have already been enumerated."),
226: ("IM Used", "success",
"The server has fulfilled a request for the resource using instance manipulations."),
# 3xx Redirection
300: ("Multiple Choices", "redirection",
"Indicates multiple options for the resource from which the client may choose."),
301: ("Moved Permanently", "redirection",
"This and all future requests should be directed to the given URI."),
302: ("Found", "redirection",
"The resource has been temporarily moved to a different URI."),
303: ("See Other", "redirection",
"The response to the request can be found under another URI using a GET method."),
304: ("Not Modified", "redirection",
"Indicates that the resource has not been modified since the version specified."),
305: ("Use Proxy", "redirection",
"The requested resource is available only through a proxy. Deprecated."),
307: ("Temporary Redirect", "redirection",
"The request should be repeated with another URI; future requests should still use the original URI."),
308: ("Permanent Redirect", "redirection",
"The request and all future requests should be repeated using another URI."),
# 4xx Client Errors
400: ("Bad Request", "client",
"The server cannot process the request due to a client error (malformed syntax, etc.)."),
401: ("Unauthorized", "client",
"Authentication is required and has failed or has not yet been provided."),
402: ("Payment Required", "client",
"Reserved for future use; sometimes used by digital payment systems."),
403: ("Forbidden", "client",
"The request was valid, but the server is refusing action. Authentication may not help."),
404: ("Not Found", "client",
"The requested resource could not be found but may be available in the future."),
405: ("Method Not Allowed", "client",
"A request method is not supported for the requested resource."),
406: ("Not Acceptable", "client",
"The requested resource is capable of generating only content not acceptable per Accept headers."),
407: ("Proxy Authentication Required", "client",
"The client must first authenticate itself with the proxy."),
408: ("Request Timeout", "client",
"The server timed out waiting for the request."),
409: ("Conflict", "client",
"Indicates that the request could not be processed because of conflict in the current state."),
410: ("Gone", "client",
"Indicates that the resource requested is no longer available and will not be available again."),
411: ("Length Required", "client",
"The request did not specify the length of its content, which is required."),
412: ("Precondition Failed", "client",
"The server does not meet one of the preconditions specified in the request."),
413: ("Payload Too Large", "client",
"The request is larger than the server is willing or able to process."),
414: ("URI Too Long", "client",
"The URI provided was too long for the server to process."),
415: ("Unsupported Media Type", "client",
"The request entity has a media type which the server or resource does not support."),
416: ("Range Not Satisfiable", "client",
"The client has asked for a portion of the file, but the server cannot supply that portion."),
417: ("Expectation Failed", "client",
"The server cannot meet the requirements of the Expect request-header field."),
418: ("I'm a teapot", "client",
"Defined in 1998 as one of the traditional IETF April Fools' jokes (RFC 2324)."),
421: ("Misdirected Request", "client",
"The request was directed at a server that is not able to produce a response."),
422: ("Unprocessable Entity", "client",
"The request was well-formed but was unable to be followed due to semantic errors."),
423: ("Locked", "client",
"WebDAV. The resource that is being accessed is locked."),
424: ("Failed Dependency", "client",
"WebDAV. The request failed because it depended on another request that failed."),
425: ("Too Early", "client",
"Indicates that the server is unwilling to risk processing a request that might be replayed."),
426: ("Upgrade Required", "client",
"The client should switch to a different protocol such as TLS/1.3."),
428: ("Precondition Required", "client",
"The origin server requires the request to be conditional."),
429: ("Too Many Requests", "client",
"The user has sent too many requests in a given amount of time. Rate limiting."),
431: ("Request Header Fields Too Large", "client",
"The server is unwilling to process the request because header fields are too large."),
451: ("Unavailable For Legal Reasons", "client",
"A server operator has received a legal demand to deny access to a resource."),
# 5xx Server Errors
500: ("Internal Server Error", "server",
"A generic error message when an unexpected condition was encountered."),
501: ("Not Implemented", "server",
"The server either does not recognize the request method or lacks the ability to fulfill it."),
502: ("Bad Gateway", "server",
"The server was acting as a gateway or proxy and received an invalid response from upstream."),
503: ("Service Unavailable", "server",
"The server cannot handle the request (overloaded or down for maintenance)."),
504: ("Gateway Timeout", "server",
"The upstream server failed to send a request in the time allowed by the server."),
505: ("HTTP Version Not Supported", "server",
"The server does not support the HTTP protocol version used in the request."),
506: ("Variant Also Negotiates", "server",
"Transparent content negotiation for the request results in a circular reference."),
507: ("Insufficient Storage", "server",
"WebDAV. The server is unable to store the representation needed to complete the request."),
508: ("Loop Detected", "server",
"WebDAV. The server detected an infinite loop while processing the request."),
510: ("Not Extended", "server",
"Further extensions to the request are required for the server to fulfill it."),
511: ("Network Authentication Required", "server",
"The client needs to authenticate to gain network access."),
}
CATEGORY_NAMES = {
"informational": "1xx Informational",
"success": "2xx Success",
"redirection": "3xx Redirection",
"client": "4xx Client Error",
"server": "5xx Server Error",
}
# ANSI colors
COLORS = {
"informational": "\033[36m", # cyan
"success": "\033[32m", # green
"redirection": "\033[33m", # yellow
"client": "\033[31m", # red
"server": "\033[35m", # magenta
}
RESET = "\033[0m"
BOLD = "\033[1m"
def color(text, category, use_color=True):
if not use_color:
return text
return f"{COLORS.get(category, '')}{text}{RESET}"
def print_status(code, info, use_color=True):
name, category, description = info
cat_label = CATEGORY_NAMES[category]
if use_color:
print(f"{BOLD}{color(str(code), category)}{RESET} {BOLD}{name}{RESET}")
print(f" Category: {color(cat_label, category)}")
else:
print(f"{code} {name}")
print(f" Category: {cat_label}")
print(f" {description}")
print()
def lookup_code(arg, use_color=True):
"""Look up a single code or wildcard like 4xx."""
arg = arg.lower().strip()
# Wildcard pattern: e.g. 4xx, 2xx
if len(arg) == 3 and arg[0].isdigit() and arg[1:] == "xx":
prefix = arg[0]
matches = [(c, info) for c, info in STATUS_CODES.items() if str(c).startswith(prefix)]
if not matches:
print(f"No status codes found starting with {prefix}", file=sys.stderr)
return 1
for code, info in sorted(matches):
print_status(code, info, use_color)
return 0
# Exact code
try:
code = int(arg)
except ValueError:
print(f"Invalid status code: {arg}", file=sys.stderr)
return 1
if code not in STATUS_CODES:
print(f"Status code {code} not found.", file=sys.stderr)
# Suggest nearby
nearby = [c for c in STATUS_CODES if abs(c - code) <= 5]
if nearby:
print(f"Did you mean: {', '.join(str(c) for c in sorted(nearby))}?", file=sys.stderr)
return 1
print_status(code, STATUS_CODES[code], use_color)
return 0
def search(query, use_color=True):
"""Search names and descriptions for a substring."""
q = query.lower()
matches = [(c, info) for c, info in STATUS_CODES.items()
if q in info[0].lower() or q in info[2].lower()]
if not matches:
print(f"No matches for '{query}'.", file=sys.stderr)
return 1
for code, info in sorted(matches):
print_status(code, info, use_color)
return 0
def list_category(cat, use_color=True):
"""List all codes in a category."""
cat = cat.lower()
aliases = {
"1xx": "informational", "info": "informational",
"2xx": "success", "ok": "success",
"3xx": "redirection", "redirect": "redirection",
"4xx": "client", "client_error": "client",
"5xx": "server", "server_error": "server",
}
cat = aliases.get(cat, cat)
if cat not in CATEGORY_NAMES:
print(f"Unknown category: {cat}", file=sys.stderr)
print(f"Valid: {', '.join(CATEGORY_NAMES.keys())}", file=sys.stderr)
return 1
matches = [(c, info) for c, info in STATUS_CODES.items() if info[1] == cat]
for code, info in sorted(matches):
print_status(code, info, use_color)
return 0
def list_all(use_color=True):
for code, info in sorted(STATUS_CODES.items()):
name, category, _ = info
cat_short = CATEGORY_NAMES[category].split()[0]
if use_color:
print(f"{color(str(code), category)} {name} {color(f'[{cat_short}]', category)}")
else:
print(f"{code} {name} [{cat_short}]")
return 0
def main():
parser = argparse.ArgumentParser(
description="HTTP status code lookup CLI",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
httpstatus 404 Look up a single code
httpstatus 4xx List all 4xx codes
httpstatus --search timeout Search by keyword
httpstatus --category client List all client errors
httpstatus --list List all known codes
""",
)
parser.add_argument("code", nargs="?", help="Status code (e.g. 404) or wildcard (e.g. 4xx)")
parser.add_argument("-s", "--search", help="Search names and descriptions")
parser.add_argument("-c", "--category", help="List all codes in category (1xx-5xx, info, success, redirection, client, server)")
parser.add_argument("-l", "--list", action="store_true", help="List all status codes")
parser.add_argument("--no-color", action="store_true", help="Disable ANSI colors")
args = parser.parse_args()
use_color = not args.no_color and sys.stdout.isatty()
if args.list:
return list_all(use_color)
if args.search:
return search(args.search, use_color)
if args.category:
return list_category(args.category, use_color)
if args.code:
return lookup_code(args.code, use_color)
parser.print_help()
return 1
if __name__ == "__main__":
sys.exit(main())