4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the Revised BSD License.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 Revised BSD License for more details.
12 Copyright 2016-2023 Game Maker 2k - https://github.com/GameMaker2k
13 Copyright 2016-2023 Kazuki Przyborowski - https://github.com/KazukiPrzyborowski
15 $FileInfo: pyhttpserv.py - Last Update: 10/5/2023 Ver. 2.0.2 RC 1 - Author: cooldude2k $
43 __program_name__
= "PyHTTPServer"
44 __program_alt_name__
= "PyHTTPServer"
45 __program_small_name__
= "httpserver"
46 __project__
= __program_name__
47 __project_url__
= "https://github.com/GameMaker2k/PyWWW-Get"
48 __version_info__
= (2, 0, 2, "RC 1", 1)
49 __version_date_info__
= (2023, 10, 5, "RC 1", 1)
50 __version_date__
= str(__version_date_info__
[0])+"."+str(__version_date_info__
[
51 1]).zfill(2)+"."+str(__version_date_info__
[2]).zfill(2)
52 __revision__
= __version_info__
[3]
53 __revision_id__
= "$Id$"
54 if(__version_info__
[4] is not None):
55 __version_date_plusrc__
= __version_date__
+ \
56 "-"+str(__version_date_info__
[4])
57 if(__version_info__
[4] is None):
58 __version_date_plusrc__
= __version_date__
59 if(__version_info__
[3] is not None):
60 __version__
= str(__version_info__
[0])+"."+str(__version_info__
[1])+"."+str(
61 __version_info__
[2])+" "+str(__version_info__
[3])
62 if(__version_info__
[3] is None):
64 __version_info__
[0])+"."+str(__version_info__
[1])+"."+str(__version_info__
[2])
66 parser
= argparse
.ArgumentParser(
67 description
="Simple HTTP Server in Python.", conflict_handler
="resolve", add_help
=True)
68 parser
.add_argument("-V", "--version", action
="version",
69 version
=__program_name__
+" "+__version__
)
70 parser
.add_argument("-e", "--enablessl",
71 action
="store_true", help="Enable SSL")
72 parser
.add_argument("-k", "--sslkeypem", default
=None,
73 help="specify a custom user agent")
74 parser
.add_argument("-c", "--sslcertpem", default
=None,
75 help="specify a custom referer, use if the video access")
76 parser
.add_argument("-p", "--servport", type=int, default
=8080,
77 help="specify a file name for output")
78 getargs
= parser
.parse_args()
80 enablessl
= getargs
.enablessl
81 sslkeypem
= getargs
.sslkeypem
82 sslcertpem
= getargs
.sslcertpem
83 servport
= int(getargs
.servport
)
84 if(isinstance(servport
, int)):
85 if(servport
< 1 or servport
> 65535):
87 elif(isinstance(servport
, str)):
88 if(servport
.isnumeric()):
89 servport
= int(servport
)
90 if(servport
< 1 or servport
> 65535):
97 if(sslkeypem
is not None and
98 (not os
.path
.exists(sslkeypem
) or not os
.path
.isfile(sslkeypem
))):
101 if(sslcertpem
is not None and
102 (not os
.path
.exists(sslkeypem
) or not os
.path
.isfile(sslkeypem
))):
107 from BaseHTTPServer
import HTTPServer
108 from SimpleHTTPServer
import SimpleHTTPRequestHandler
109 from urlparse
import parse_qs
110 from Cookie
import SimpleCookie
112 from http
.server
import SimpleHTTPRequestHandler
, HTTPServer
113 from urllib
.parse
import parse_qs
114 from http
.cookies
import SimpleCookie
117 (sslkeypem
is not None and (os
.path
.exists(sslkeypem
) and os
.path
.isfile(sslkeypem
))) and
118 (sslcertpem
is not None and (os
.path
.exists(sslkeypem
) and os
.path
.isfile(sslkeypem
)))):
120 # HTTP/HTTPS Server Class
123 class CustomHTTPRequestHandler(SimpleHTTPRequestHandler
):
124 def compress_content(self
, content
):
125 """Compress content using gzip or deflate depending on Accept-Encoding header."""
126 accept_encoding
= self
.headers
.get('Accept-Encoding', '')
127 accept_encoding
= None
128 if 'gzip' in accept_encoding
:
129 self
.send_header('Content-Encoding', 'gzip')
130 compressed_content
= gzip
.compress(content
.encode('utf-8'))
131 self
.send_header('Content-Length', len(compressed_content
))
132 return compressed_content
133 elif 'deflate' in accept_encoding
:
134 self
.send_header('Content-Encoding', 'deflate')
135 compressed_content
= zlib
.compress(content
.encode('utf-8'))
136 self
.send_header('Content-Length', len(compressed_content
))
137 return compressed_content
138 elif 'br' in accept_encoding
:
139 self
.send_header('Content-Encoding', 'br')
140 compressed_content
= brotli
.compress(content
.encode('utf-8'))
141 self
.send_header('Content-Length', len(compressed_content
))
142 return compressed_content
143 elif 'zstd' in accept_encoding
:
144 self
.send_header('Content-Encoding', 'zstd')
145 compressed_content
= zstandard
.compress(content
.encode('utf-8'))
146 self
.send_header('Content-Length', len(compressed_content
))
147 return compressed_content
148 elif 'lzma' in accept_encoding
:
149 self
.send_header('Content-Encoding', 'lzma')
150 compressed_content
= lzma
.compress(content
.encode('utf-8'))
151 self
.send_header('Content-Length', len(compressed_content
))
152 return compressed_content
153 elif 'xz' in accept_encoding
:
154 self
.send_header('Content-Encoding', 'xz')
155 compressed_content
= lzma
.compress(content
.encode('utf-8'))
156 self
.send_header('Content-Length', len(compressed_content
))
157 return compressed_content
159 self
.send_header('Content-Length', len(content
))
160 return content
.encode('utf-8')
161 return content
.encode()
163 def display_info(self
):
164 # Setting headers for the response
165 self
.send_response(200)
166 self
.send_header('Content-type', 'text/plain')
167 # Set a sample cookie in the response;
168 self
.send_header('Set-Cookie', 'sample_cookie=sample_value; Path=/;')
170 # Displaying request method
171 response
= 'Method: {}\n'.format(self
.command
)
172 response
+= 'Path: {}\n'.format(self
.path
)
173 # Displaying all headers
174 headers_list
= ["{}: {}".format(
175 key
.title(), self
.headers
[key
]) for key
in self
.headers
]
176 response
+= '\nHeaders:\n' + '\n'.join(headers_list
) + '\n'
177 # Extract and display cookies from headers
178 if 'Cookie' in self
.headers
:
179 response
+= '\nCookies:\n'
180 cookies
= SimpleCookie(self
.headers
['Cookie'])
181 for key
, morsel
in cookies
.items():
182 response
+= '{}: {}\n'.format(key
, morsel
.value
)
183 # Displaying GET parameters (if any)
184 if self
.command
== 'GET':
185 query
= self
.path
.split('?', 1)[-1]
186 params
= parse_qs(query
)
188 response
+= '\nGET Parameters:\n'
189 for key
, values
in params
.items():
190 response
+= '{}: {}\n'.format(key
, ', '.join(values
))
191 # Sending the response
192 self
.wfile
.write(self
.compress_content(response
))
200 if 'Transfer-Encoding' in self
.headers
and self
.headers
['Transfer-Encoding'] == 'chunked':
203 chunk_size_line
= self
.rfile
.readline().decode('utf-8')
204 chunk_size
= int(chunk_size_line
, 16)
206 self
.rfile
.readline()
208 chunk_data
= self
.rfile
.read(chunk_size
).decode('utf-8')
209 post_data
+= chunk_data
210 self
.rfile
.readline()
212 content_length
= int(self
.headers
['Content-Length'])
213 post_data
= self
.rfile
.read(content_length
).decode('utf-8')
214 params
= parse_qs(post_data
)
215 response
= 'POST Parameters:\n'
216 for key
, values
in params
.items():
217 response
+= '{}: {}\n'.format(key
, ', '.join(values
))
218 self
.send_response(200)
219 self
.send_header('Content-type', 'text/plain')
220 self
.send_header('Set-Cookie', 'sample_cookie=sample_value; Path=/;')
222 self
.wfile
.write(self
.compress_content(response
))
225 # Start Server Forever
226 if __name__
== "__main__":
227 server_address
= ('', int(servport
))
228 httpd
= HTTPServer(server_address
, CustomHTTPRequestHandler
)
229 if(enablessl
and sslkeypem
is not None and sslcertpem
is not None):
230 httpd
.socket
= ssl
.wrap_socket(httpd
.socket
,
231 keyfile
=sslkeypem
, certfile
=sslcertpem
, server_side
=True)
233 print("Server started at https://localhost:"+str(servport
))
235 print("Server started at http://localhost:"+str(servport
))
236 httpd
.serve_forever()