ghttp-tool 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ghttp/__init__.py ADDED
@@ -0,0 +1,14 @@
1
+ """
2
+ GHTTP By GPGStudio created for easy work with http,
3
+ See everything in "README"
4
+ """
5
+
6
+ from .parsearg import parsearg, parse, unparse
7
+ from .server import server, tserver
8
+ from .client import client
9
+ from .transfer_encoding import read as transfer_encoding
10
+ from .simple_client import get, head, post, put, delete, connect, options, trace, patch, Session, parseurl
11
+ from .simple_server import serve
12
+
13
+ __all__ = ['parsearg','parse','unparse','server','tserver','client','transfer_encoding','get','head','post','put','delete','connect','options','trace','patch','Session','serve','parseurl']
14
+ __version__ = '0.0.1'
ghttp/client.py ADDED
@@ -0,0 +1,134 @@
1
+ from .transfer_encoding import read as read_encoding
2
+ import socket
3
+
4
+ def read(s):
5
+ try:
6
+ data = s.recv(65500)
7
+ while b'\r\n\r\n' not in data:
8
+ chunk = s.recv(65500)
9
+ if chunk == b'':
10
+ break
11
+ data += chunk
12
+ except: data = b''
13
+ if not data == b'':
14
+ data_split = data.find(b'\r\n\r\n')
15
+ header, dta = data[:data_split], data[data_split+4:]
16
+ headers = {}
17
+ h = header.split(b'\r\n')
18
+ answer = h[0].split(b' ',1)
19
+ answer = answer[0].decode(), answer[1].decode()
20
+ for i in h[1:]:
21
+ key, value = i.split(b':',1)
22
+ key = key.decode(errors='replace')
23
+ value = value.lstrip(b' ').decode(errors='replace')
24
+ headers[key.lower()] = int(value) if value.isdigit() else value
25
+ if 'transfer-encoding' in headers:
26
+ dta = read_encoding(s,dta,headers['transfer-encoding'])
27
+ elif 'content-length' in headers:
28
+ toread = headers['content-length']
29
+ toread = int(toread) - len(dta)
30
+ while toread > 0:
31
+ chunk = s.recv(toread if toread < 65500 else 65500)
32
+ dta += chunk
33
+ toread -= len(chunk)
34
+ else:
35
+ s.settimeout(0.5)
36
+ while True:
37
+ try: chunk = s.recv(65500)
38
+ except: break
39
+ if chunk == b'':
40
+ break
41
+ dta += chunk
42
+ s.settimeout(5.0)
43
+ return answer, dta, headers
44
+ else:
45
+ return [None, None], None, {}
46
+
47
+ def send(s, method='GET', path='/', ver='1.1', data=b'', headers={}, without_len=False, encoding='utf-8'):
48
+ if type(data)==str:
49
+ data = data.encode(encoding=encoding)
50
  result = f"{method} {path} HTTP/{ver}\x0d\x0a"
51
+ for key, value in headers.items():
1
52
  result += f"{key}: {value}\x0d\x0a"
2
53
  if not without_len:
3
54
  if not "\ncontent-length: " in result.lower():
4
55
  result += f"Content-Length: {len(data)}\x0d\x0a"
5
56
  result += "\x0d\x0a"
6
57
  data = result.encode() + data
7
58
  s.sendall(data)
59
+
60
+ def send_raw(conn, data):
61
+ if type(data)==bytes:
62
+ conn.sendall(data)
63
+ else:
64
+ raise ValueError("data must be bytes!")
65
+
66
+ def read_chunk(conn):
67
+ try:
68
+ chunk_size_hex = b''
69
+ while True:
70
+ char = conn.recv(1)
71
+ if not char:
72
+ return None
73
+ if char == b'\n':
74
+ break
75
+ chunk_size_hex += char
76
+ if chunk_size_hex.endswith(b'\r'):
77
+ chunk_size_hex = chunk_size_hex[:-1]
78
+ try:
79
+ chunk_size = int(chunk_size_hex, 16)
80
+ except ValueError:
81
+ return None
82
+ if chunk_size == 0:
83
+ conn.recv(2)
84
+ return (b'', 0)
85
+ data = b''
86
+ while len(data) < chunk_size:
87
+ remaining = chunk_size - len(data)
88
+ chunk = conn.recv(min(remaining, 8192))
89
+ if not chunk:
90
+ return None
91
+ data += chunk
92
+ conn.recv(2)
93
+ return data, chunk_size
94
+ except Exception:
95
+ return None, None
96
+
97
+ def send_chunk(conn, data):
98
+ if type(data)==bytes:
99
+ conn.sendall(hex(len(data))[2:].encode() + b'\r\n' + data + b'\r\n')
100
+ else:
101
+ raise ValueError("data must be bytes!")
102
+
103
+ def read_raw(s, data=None):
104
+ try: return s.recv(data)
105
+ except: return None
106
+
107
+ def close(s):
108
+ try: s.close()
109
+ except: pass
110
+
111
+ class client:
112
+ def __init__(self, VER='1.1'):
113
+ self.ver = VER
114
+ self.s = None
115
+ pass
116
+ def connect(self, IP='localhost', PORT=80, CUSTOM_SOCK=None):
117
+ try: self.s.close()
118
+ except: pass
119
+ try:
120
+ if CUSTOM_SOCK is None:
121
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
122
+ else:
123
+ s = CUSTOM_SOCK
124
+ s.settimeout(5.0)
125
+ s.connect((IP,PORT))
126
+ self.s = s
127
+ except: pass
128
+ def read(self):
129
+ return read(self.s)
130
+ def send(self, method='GET', path='/', data=b'', headers={}, without_len=False):
131
+ send(self.s, method, path, self.ver, data, headers, without_len)
132
+ def send_raw(self, data):
133
+ send_raw(self.s, data)
134
+ def send_chunk(self, data):
135
+ send_chunk(self.s, data)
136
+ def read_chunk(self):
137
+ return read_chunk(self.s)
138
+ def read_raw(self, data=None):
139
+ return read_raw(self.s, data)
140
+ def close(self):
141
+ close(self.s)
142
+
ghttp/parsearg.py ADDED
@@ -0,0 +1,43 @@
1
+ def parse(data, safe='1234567890-+_.~qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'):
2
+ value = ''
3
+ for i in data.encode():
4
+ if bytes([i]) in safe.encode():
5
+ value += bytes([i]).decode()
6
+ else:
7
+ value += f'%{bytes([i]).hex().upper()}'
8
+ return value
9
+ def unparse(data):
10
+ for i in range(0,256):
11
+ data = data.replace(f'%{bytes([i]).hex()}',chr(i)).replace(f'%{bytes([i]).hex().upper()}',chr(i))
12
+ return data
13
+ def parsearg(data,continius=None,encoding='utf-8',safe='1234567890-+_.~qwertyuiopasdfghjklzxcvbnm'):
14
+ if isinstance(continius, dict):
15
+ toret = '?'
16
+ for key, _value in continius.items():
17
+ value = parse(_value)
18
+ toret += f'{key}={value}&'
19
+ return data+toret[:-1] if toret[-1] == '&' else data
20
+ if isinstance(data, str) and continius is None:
21
+ if '?' in data:
22
+ link, args = data.split('?',1)
23
+ args_dict = {}
24
+ for i in args.split('&'):
25
+ key, value = i.split('=',1)
26
+ for i in range(0,256):
27
+ value = value.replace(f'%{bytes([i]).hex()}',chr(i)).replace(f'%{bytes([i]).hex().upper()}',chr(i))
28
+ value = value.encode('latin-1')
29
+ try: value = value.decode(encoding=encoding)
30
+ except: pass
31
+ if isinstance(value,str):
32
+ if value.lower() == 'true':
33
+ value = True
34
+ elif value.lower() == 'false':
35
+ value = False
36
+ elif value.isdigit():
37
+ value = int(value)
38
+ elif value.replace('.', '').isdigit():
39
+ value = float(value)
40
+ args_dict[key] = value
41
+ return link, args_dict
42
+ else:
43
+ return data, {}
ghttp/server.py ADDED
@@ -0,0 +1,119 @@
1
+ from .transfer_encoding import read as read_encoding
2
+ import threading
3
+ import socket
4
+
5
  if type(data)==str:
6
+ data = data.encode(encoding=encoding)
7
+ if type(status_code)==bytes:
8
+ status_code = status_code.decode('latin-1')
1
9
  close = False
2
10
  result = f"HTTP/{ver} {status_code}\x0d\x0a"
3
11
  for key, value in headers.items():
4
12
  if key.lower()=='connection' and value.lower() == 'close':
5
13
  close = True
6
14
  result += f"{key}: {value}\x0d\x0a"
15
+ if key.lower() == 'connection' and value.lower() == 'close':
16
+ close = True
17
+ if not without_con:
7
18
  if not "\nconnection: " in result.lower():
8
19
  result += f"Connection: close\x0d\x0a"
9
20
  close = True
10
21
  if not without_len:
11
22
  if not "\ncontent-length: " in result.lower():
12
23
  result += f"Content-Length: {len(data)}\x0d\x0a"
13
24
  result += "\x0d\x0a"
14
25
  data = result.encode() + data
15
26
  conn.sendall(data)
16
27
  if close:
17
28
  conn.close()
29
+
18
30
  try:
19
31
  data = conn.recv(65500)
20
32
  while b'\r\n\r\n' not in data:
21
33
  chunk = conn.recv(65500)
22
34
  if chunk == b'':
23
35
  break
24
36
  data += chunk
25
37
  except: data = b''
26
38
  if not data == b'':
27
39
  data_split = data.find(b'\r\n\r\n')
28
40
  header, dta = data[:data_split], data[data_split+4:]
29
41
  headers = {}
30
42
  h = header.split(b'\r\n')
31
43
  if not require_ver:
32
44
  question = h[0][:h[0].rfind(b' HTTP')].split(b' ',1)
33
45
  question.append(None)
34
46
  else:
35
47
  question = h[0].split(b' ',1)
36
48
  question = question[0], *question[1].rsplit(b' ',1)
37
49
  for i in h[1:]:
38
50
  key, value = i.split(b':',1)
39
51
  key = key.decode(errors='replace')
40
52
  value = value.lstrip(b' ').decode(errors='replace')
41
53
  headers[key.lower()] = int(value) if value.isdigit() else value
54
+ if 'transfer-encoding' in headers:
55
+ dta = read_encoding(s,dta,headers['transfer-encoding'])
56
+ elif 'content-length' in headers:
42
57
  toread = headers['content-length']
43
58
  toread = int(toread) - len(dta)
44
59
  while toread > 0:
45
60
  chunk = conn.recv(toread if toread < 65500 else 65500)
61
+ dta += chunk
62
+ toread -= len(chunk)
46
63
  if len(question) == 3:
47
64
  if not binary_question:
48
65
  question = question[0].decode(), question[1].decode(), question[2].decode()
49
66
  else:
50
67
  raise ValueError(f"Question is strange.. {question}")
51
68
  return question, dta, headers
52
69
  else:
53
70
  return [None, None, None], None, {}
71
+
72
+ def send_raw(conn, data):
73
+ if type(data)==bytes:
74
+ conn.sendall(data)
75
+ else:
76
+ raise ValueError("data must be bytes!")
77
+
78
+ def read_chunk(conn):
79
+ try:
80
+ chunk_size_hex = b''
81
+ while True:
82
+ char = conn.recv(1)
83
+ if not char:
84
+ return None
85
+ if char == b'\n':
86
+ break
87
+ chunk_size_hex += char
88
+ if chunk_size_hex.endswith(b'\r'):
89
+ chunk_size_hex = chunk_size_hex[:-1]
90
+ try:
91
+ chunk_size = int(chunk_size_hex, 16)
92
+ except ValueError:
93
+ return None
94
+ if chunk_size == 0:
95
+ conn.recv(2)
96
+ return (b'', 0)
97
+ data = b''
98
+ while len(data) < chunk_size:
99
+ remaining = chunk_size - len(data)
100
+ chunk = conn.recv(min(remaining, 8192))
101
+ if not chunk:
102
+ return None
103
+ data += chunk
104
+ conn.recv(2)
105
+ return data, chunk_size
106
+ except Exception:
107
+ return None, None
108
+
109
+ def read_raw(conn, data=None):
110
+ try: return conn.recv(data)
111
+ except: return None
112
+
113
+ def close(conn):
114
+ try: conn.close()
115
+ except: pass
116
+
117
+ def send_chunk(conn, data):
118
+ if type(data)==bytes:
119
+ conn.sendall(hex(len(data))[2:].encode() + b'\r\n' + data + b'\r\n')
120
+ else:
121
+ raise ValueError("data must be bytes!")
122
+
123
+ class tserver():
124
+ def __init__(self, HOST='127.0.0.1', PORT=80, VER='1.1', precon=None, preaddr=None):
125
+ if precon == None:
126
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
127
+ s.bind((HOST, PORT))
128
+ s.listen()
129
+ s.settimeout(5.0)
130
+ self.s = s
131
+ else:
132
+ self.s = None
133
+ self.conn = precon
134
+ self.addr = preaddr
135
+ self.ver = VER
136
+ def accept(self, get_raw=False):
137
+ if self.s == None:
138
+ raise ValueError("While using precon, not allowed method accept!")
139
+ else:
140
+ conn, addr = self.s.accept()
141
+ conn.settimeout(5.0)
142
+ self.conn, self.addr = conn, addr
143
+ if not get_raw:
144
+ return addr
145
+ else:
146
+ return conn, addr
147
+ def send(self, data, headers={}, status_code="200 OK", without_len=False, without_con=False):
148
+ send(self.conn, self.ver, data, headers, status_code, without_len, without_con)
149
+ def read(self, binary_question=False, require_ver=True):
150
+ return read(self.conn, binary_question, require_ver)
151
+ def send_raw(self, data):
152
+ send_raw(self.conn, data)
153
+ def send_chunk(self, data):
154
+ send_chunk(self.conn, data)
155
+ def read_chunk(self):
156
+ return read_chunk(self.conn)
157
+ def read_raw(self, count=None):
158
+ return read_raw(self.conn, count)
159
+ def close(self):
160
+ close(self.conn)
161
+
162
+ def server(WRAPPER, HOST='127.0.0.1', PORT=80, VER='1.1', CUSTOM_SOCK=None, ARGS=()):
163
+ serv = tserver(HOST, PORT, VER)
164
+ if CUSTOM_SOCK is not None:
165
+ try: serv.close()
166
+ except: pass
167
+ serv.s = CUSTOM_SOCK
168
+ while True:
169
+ serv.s.settimeout(None)
170
+ conn, addr = serv.accept(get_raw=True)
171
+ s = tserver(HOST, PORT, VER, precon=conn, preaddr=addr)
172
+ t = threading.Thread(target=WRAPPER, daemon=True, args=(s,addr,*ARGS))
173
+ t.start()
ghttp/simple_client.py ADDED
@@ -0,0 +1,196 @@
1
+ from .parsearg import parsearg
2
+ from .client import client
3
+ import json as json_mod
4
+ import socket
5
+ import ssl
6
+
7
+ class _clresponse:
8
+ def __init__(self,data,code,text,headers):
9
+ self.text = data.decode(encoding='latin-1')
10
+ self.binary = data
11
+ self.status = code
12
+ self.text_status = text
13
+ self.headers = headers
14
+ def json(self):
15
+ return json.loads(self.text)
16
+
17
+ def _send_with_retry(s, method, path, headers, payload, ip, port, getsock, max_retries=2):
18
+ for attempt in range(max_retries):
19
+ try:
20
+ s.send(method, path, headers=headers, without_len=True, data=payload)
21
+ return s
22
+ except Exception:
23
+ if attempt == max_retries - 1:
24
+ raise ValueError('Timeout')
25
+ s.close()
26
+ if getsock:
27
+ s = client()
28
+ s.connect(ip, port)
29
+ else:
30
+ s = client()
31
+ s.connect(ip, port)
32
+ return s
33
+
34
+ def _receive_with_retry(s, ip, port, getsock, max_retries=2):
35
+ for attempt in range(max_retries):
36
+ try:
37
+ answer, data, headers = s.read()
38
+ return answer, data, headers
39
+ except Exception:
40
+ if attempt == max_retries - 1:
41
+ raise ValueError('Timeout')
42
+ s.close()
43
+ if getsock:
44
+ s = client()
45
+ s.connect(ip, port)
46
+ else:
47
+ s = client()
48
+ s.connect(ip, port)
49
+ return None, None, None
50
+
51
+ def parseurl(url):
52
+ https = False
53
+ if 'https://' in url:
54
+ https = True
55
+ address = url.replace('http://', '').replace('https://', '')
56
+ address = address.split('/', 1)
57
+ addr, path = address[0], '/' if len(address) == 1 else '/' + address[1]
58
+ ip, port = addr.split(':', 1) if len(addr.split(':', 1)) == 2 else (addr, 80 if not https else 443)
59
+ port = int(port)
60
+ return ip, port, https, path
61
+
62
+ def _get(url, data, json, headers, params, method, sock=None, close=True, getsock=False, usehost=True):
63
+ ip, port, https, path = parseurl(url)
64
+ if data is not None:
65
+ payload = data
66
+ elif json is not None:
67
+ payload = json_mod.dumps(json).encode()
68
+ else:
69
+ payload = b''
70
+ dfheaders = {}
71
+ if usehost:
72
+ dfheaders['Host'] = ip
73
+ if not payload == b'':
74
+ dfheaders['Content-Length'] = str(len(payload))
75
+ dfheaders.update(headers)
76
+ if sock is None:
77
+ s = client()
78
+ if https:
79
+ context = ssl.create_default_context()
80
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
81
+ ssl_sock = context.wrap_socket(sock, server_hostname=ip)
82
+ s.connect(ip, port, ssl_sock)
83
+ else:
84
+ s.connect(ip, port)
85
+ else:
86
+ s = sock
87
+ s = _send_with_retry(s, method, path, dfheaders, payload, ip, port, getsock)
88
+ answer, data, headers = _receive_with_retry(s, ip, port, getsock)
89
+ if data is None:
90
+ s.close()
91
+ s = client()
92
+ s.connect(ip, port)
93
+ s = _send_with_retry(s, method, path, dfheaders, payload, ip, port, getsock)
94
+ answer, data, headers = _receive_with_retry(s, ip, port, getsock)
95
+ if data is None:
96
+ raise ValueError('Timeout')
97
+ status = answer[1].split(' ', 1)
98
+ status_code = int(status[0])
99
+ status_text = status[1] if len(status) == 2 else None
100
+ if close:
101
+ s.close()
102
+ response = _clresponse(data, status_code, status_text, headers)
103
+ return (s, response) if getsock else response
104
+
105
+ def get(url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
106
+ return _get(url,data,json,headers,params,'GET', usehost=usehost)
107
+
108
+ def post(url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
109
+ return _get(url,data,json,headers,params,'POST', usehost=usehost)
110
+
111
+ def delete(url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
112
+ return _get(url,data,json,headers,params,'DELETE', usehost=usehost)
113
+
114
+ def put(url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
115
+ return _get(url,data,json,headers,params,'PUT', usehost=usehost)
116
+
117
+ def head(url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
118
+ return _get(url,data,json,headers,params,'HEAD', usehost=usehost)
119
+
120
+ def connect(url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
121
+ return _get(url,data,json,headers,params,'CONNECT', usehost=usehost)
122
+
123
+ def options(url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
124
+ return _get(url,data,json,headers,params,'OPTIONS', usehost=usehost)
125
+
126
+ def trace(url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
127
+ return _get(url,data,json,headers,params,'TRACE', usehost=usehost)
128
+
129
+ def patch(url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
130
+ return _get(url,data,json,headers,params,'PATCH', usehost=usehost)
131
+
132
+ class Session:
133
+ def __init__(self):
134
+ self.s = None
135
+ self.url = None
136
+
137
+ def reload(self):
138
+ self.close()
139
+ self.s = None
140
+ self.url = None
141
+
142
+ def close(self):
143
+ try: self.s.close()
144
+ except: pass
145
+
146
+ def check_url(self, url):
147
+ ip, port, https, path = parseurl(url)
148
+ _url = f'{ip}:{port}'
149
+ if not _url == self.url:
150
+ self.url = _url
151
+ self.reload()
152
+
153
+ def get(self,url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
154
+ self.check_url(url)
155
+ self.s, answer = _get(url,data,json,headers,params,'GET',self.s,False,True,usehost)
156
+ return answer
157
+
158
+ def post(self,url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
159
+ self.check_url(url)
160
+ self.s, answer = _get(url,data,json,headers,params,'POST',self.s,False,True,usehost)
161
+ return answer
162
+
163
+ def delete(self,url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
164
+ self.check_url(url)
165
+ self.s, answer = _get(url,data,json,headers,params,'DELETE',self.s,False,True,usehost)
166
+ return answer
167
+
168
+ def put(self,url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
169
+ self.check_url(url)
170
+ self.s, answer = _get(url,data,json,headers,params,'PUT',self.s,False,True,usehost)
171
+ return answer
172
+
173
+ def head(self,url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
174
+ self.check_url(url)
175
+ self.s, answer = _get(url,data,json,headers,params,'HEAD',self.s,False,True,usehost)
176
+ return answer
177
+
178
+ def connect(self,url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
179
+ self.check_url(url)
180
+ self.s, answer = _get(url,data,json,headers,params,'CONNECT',self.s,False,True,usehost)
181
+ return answer
182
+
183
+ def options(self,url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
184
+ self.check_url(url)
185
+ self.s, answer = _get(url,data,json,headers,params,'OPTIONS',self.s,False,True,usehost)
186
+ return answer
187
+
188
+ def trace(self,url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
189
+ self.check_url(url)
190
+ self.s, answer = _get(url,data,json,headers,params,'TRACE',self.s,False,True,usehost)
191
+ return answer
192
+
193
+ def patch(self,url='localhost:80', data=None, json=None, headers={}, params={}, usehost=True):
194
+ self.check_url(url)
195
+ self.s, answer = _get(url,data,json,headers,params,'PATCH',self.s,False,True,usehost)
196
+ return answer
ghttp/simple_server.py ADDED
@@ -0,0 +1,85 @@
1
+ from .parsearg import parsearg
2
+ from .server import server
3
+ import subprocess
4
+ import json
5
+ import ssl
6
+ import os
7
+
8
+ def serve(IP='localhost',PORT=80,PATH='./',silent=False,keep_alive=False,SSL=False,ssl_cert='cert.pem', ssl_key='key.pem'):
9
+ if SSL:
10
+ context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
11
+ context.load_cert_chain(ssl_cert, ssl_key)
12
+ if PATH.endswith('/'):
13
+ PATH = PATH[:-1]
14
+ def wrapper(cl,addr,SSL,context):
15
+ if SSL:
16
+ try: cl.conn = context.wrap_socket(cl.conn, server_side=True)
17
+ except: pass
18
+ cl.conn.settimeout(30.0)
19
+ while True:
20
+ try:
21
+ question, data, headers = cl.read()
22
+ if data == None:
23
+ if not silent:
24
+ print(f'[{':'.join([str(i) for i in addr])}]: Closed',flush=True)
25
+ cl.close()
26
+ break
27
+ if question[0] == 'GET':
28
+ cur_dir, args = parsearg(question[1])
29
+ while cur_dir.find('..') != -1:
30
+ cur_dir = cur_dir.replace( '..', '.')
31
+ directory = PATH+cur_dir
32
+ if not directory.endswith('/') and os.path.exists(directory) and os.path.isdir(directory):
33
+ directory += '/'
34
+ hasindex = False
35
+ if os.path.exists(directory) and os.path.isdir(directory):
36
+ files = os.listdir(directory)
37
+ for i in files:
38
+ if i == 'index.html' or i == 'index.py':
39
+ hasindex = True
40
+ if hasindex:
41
+ if os.path.exists(f'{directory}index.py'):
42
+ try:
43
+ output = subprocess.check_output([f'{os.path.abspath(directory)}/index.py', json.dumps(args)], shell=True, stderr=subprocess.DEVNULL)
44
+ cl.send(output,headers={"Connection": "keep-alive" if keep_alive else "close"})
45
+ except:
46
+ cl.send(b'',status_code='500 Internal Server Error',headers={"Connection": "keep-alive" if keep_alive else "close"})
47
+ elif os.path.exists(f'{directory}index.html'):
48
+ with open(f'{directory}index.html','rb') as f:
49
+ cl.send(f.read(),headers={"Content-Type": "text/html; charset=utf-8", "Connection": "keep-alive" if keep_alive else "close"})
50
+ if not silent:
51
+ print(f'[{':'.join([str(i) for i in addr])}]: 200 OK {cur_dir}',flush=True)
52
+ elif os.path.exists(directory):
53
+ if not silent:
54
+ print(f'[{':'.join([str(i) for i in addr])}]: 200 OK {cur_dir}',flush=True)
55
+ if os.path.isdir(directory):
56
+ result = f'<h1>List of {cur_dir}</h1><a href="..">↩</a><br>'
57
+ for i in files:
58
+ result += f'<a href="{directory}{i}">{i}</a><br>'
59
+ cl.send(result.encode(),headers={"Content-Type": "text/html; charset=utf-8", "Connection": "keep-alive" if keep_alive else "close"})
60
+ else:
61
+ if directory.rsplit('.',1)[-1] == 'py':
62
+ try:
63
+ output = subprocess.check_output([f'{os.path.abspath(directory)}', json.dumps(args)], shell=True, stderr=subprocess.DEVNULL)
64
+ cl.send(output,headers={"Connection": "keep-alive" if keep_alive else "close"})
65
+ except:
66
+ cl.send(b'',status_code='500 Internal Server Error',headers={"Connection": "keep-alive" if keep_alive else "close"})
67
+ else:
68
+ with open(directory,'rb') as f:
69
+ cl.send(f.read(),{"Connection": "keep-alive" if keep_alive else "close"})
70
+ else:
71
+ if not silent:
72
+ cl.send(b'',status_code='404 Not Found', headers={"Connection": "keep-alive" if keep_alive else "close"})
73
+ print(f'[{':'.join([str(i) for i in addr])}]: 404 Not Found {cur_dir}',flush=True)
74
+ else:
75
+ if not silent:
76
+ cl.send(b'',status_code='400 Method Not Support', headers={"Connection": "keep-alive" if keep_alive else "close"})
77
+ print(f'[{':'.join([str(i) for i in addr])}]: 400 Method Not Support',flush=True)
78
+ except:
79
+ if not silent:
80
+ print(f'[{':'.join([str(i) for i in addr])}]: Error',flush=True)
81
+ cl.close()
82
+ break
83
+ if not silent:
84
+ print(f"Runned server at {IP}:{PORT}",flush=True)
85
+ server(wrapper,IP,PORT,ARGS=(SSL,context if SSL else None))
@@ -0,0 +1,26 @@
1
+ def _read_chunked(data, full_data, s):
2
+ header_end = data.find(b'\r\n')
3
+ if header_end == -1:
4
+ return data, full_data, False
5
+ header = data[:header_end]
6
+ try: toread = int(header.decode(), 16)
7
+ except: pass
8
+ if toread == 0:
9
+ return b'', full_data, True
10
+ rest = data[header_end + 2:]
11
+ while len(rest) < toread + 2:
12
+ rest += s.recv(min(65500, toread + 2 - len(rest)))
13
+ full_data += rest[:toread]
14
+ return rest[toread + 2:], full_data, False
15
+
16
+ def read(s, data=b'', encoding='chunked'):
17
+ if encoding == 'chunked':
18
+ full_data = b''
19
+ while True:
20
+ header_end = data.find(b'\r\n')
21
+ if header_end != -1:
22
+ data, full_data, done = _read_chunked(data, full_data, s)
23
+ if done:
24
+ return full_data
25
+ else:
26
+ data += s.recv(65500)
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: ghttp-tool
3
+ Version: 0.0.1
4
+ Summary: Multifunctional HTTP Lib
5
+ Author-email: GPGStudio <gpgstudio.original@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/GPGSTUDIO/GHTTP
8
+ Project-URL: Repository, https://github.com/GPGSTUDIO/GHTTP
9
+ Project-URL: Bug Reports, https://github.com/GPGSTUDIO/GHTTP/issues
10
+ Keywords: HTTP,TCP,Ethernet,Internet,Lan,Wan,
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=7.0; extra == "dev"
23
+ Requires-Dist: black; extra == "dev"
24
+ Requires-Dist: flake8; extra == "dev"
25
+ Dynamic: license-file
26
+
27
+ # **Multifunction HTTP lib!**!
28
+
29
+ Installing:
30
+ ```python
31
+ pip install ghttp
32
+ ```
33
+
34
+ See examples in https://www.gpgstudio.ru/pypi/ghttp/examples
35
+
36
+ In latest version:
37
+ > Created
@@ -0,0 +1,12 @@
1
+ ghttp/__init__.py,sha256=jY2T6M4ThweV6kFF0qjAuqHRGFUrZMhS1RcieVxli4U,619
2
+ ghttp/client.py,sha256=L64nw63zKk-n5mKhfR_EeYVVh_mmFJ7PiS0ON8C1dLk,4584
3
+ ghttp/parsearg.py,sha256=vU1d2gkBu87asrpclvTQmLzse3fY7ZACwiNnAeLJ7fM,1892
4
+ ghttp/server.py,sha256=xXgFx5l-PVHXRXNKmAHhBqktr6MZM6u4IzeWEEcYF1o,6202
5
+ ghttp/simple_client.py,sha256=MAyw7YC8PSGWhlJxeb1Sw3wnCG4odbw-_vkEJsdfubo,7794
6
+ ghttp/simple_server.py,sha256=MbndQlobpUBbbC3ZP4P8_-OASs5DrICB9ZFnNUPHXmk,5129
7
+ ghttp/transfer_encoding.py,sha256=uQrYaAMUkrS84uI7DlCtnAf4sPq_8bQAIwbc2eruEoM,900
8
+ ghttp_tool-0.0.1.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ ghttp_tool-0.0.1.dist-info/METADATA,sha256=g1HOmApis7R-pFQKfbsanD63RuPG7kxJ-r3nvAfLTxY,1210
10
+ ghttp_tool-0.0.1.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
11
+ ghttp_tool-0.0.1.dist-info/top_level.txt,sha256=WAMhcBd3d2OyGry1vK6PI28CY45g-p5Xg6GaR27LXP0,6
12
+ ghttp_tool-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
File without changes
@@ -0,0 +1 @@
1
+ ghttp