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 $
18 import os
, argparse
, gzip
, zlib
, bz2
;
39 __program_name__
= "PyHTTPServer";
40 __program_alt_name__
= "PyHTTPServer";
41 __program_small_name__
= "httpserver";
42 __project__
= __program_name__
;
43 __project_url__
= "https://github.com/GameMaker2k/PyWWW-Get";
44 __version_info__
= (2, 0, 2, "RC 1", 1);
45 __version_date_info__
= (2023, 10, 5, "RC 1", 1);
46 __version_date__
= str(__version_date_info__
[0])+"."+str(__version_date_info__
[1]).zfill(2)+"."+str(__version_date_info__
[2]).zfill(2);
47 __revision__
= __version_info__
[3];
48 __revision_id__
= "$Id$";
49 if(__version_info__
[4] is not None):
50 __version_date_plusrc__
= __version_date__
+"-"+str(__version_date_info__
[4]);
51 if(__version_info__
[4] is None):
52 __version_date_plusrc__
= __version_date__
;
53 if(__version_info__
[3] is not None):
54 __version__
= str(__version_info__
[0])+"."+str(__version_info__
[1])+"."+str(__version_info__
[2])+" "+str(__version_info__
[3]);
55 if(__version_info__
[3] is None):
56 __version__
= str(__version_info__
[0])+"."+str(__version_info__
[1])+"."+str(__version_info__
[2]);
58 parser
= argparse
.ArgumentParser(description
="Simple HTTP Server in Python.", conflict_handler
="resolve", add_help
=True);
59 parser
.add_argument("-V", "--version", action
="version", version
=__program_name__
+" "+__version__
);
60 parser
.add_argument("-e", "--enablessl", action
="store_true", help="Enable SSL");
61 parser
.add_argument("-k", "--sslkeypem", default
=None, help="specify a custom user agent");
62 parser
.add_argument("-c", "--sslcertpem", default
=None, help="specify a custom referer, use if the video access");
63 parser
.add_argument("-p", "--servport", type=int, default
=8080, help="specify a file name for output");
64 getargs
= parser
.parse_args();
66 enablessl
= getargs
.enablessl
;
67 sslkeypem
= getargs
.sslkeypem
;
68 sslcertpem
= getargs
.sslcertpem
;
69 servport
= int(getargs
.servport
);
70 if(isinstance(servport
, int)):
71 if(servport
<1 or servport
>65535):
73 elif(isinstance(servport
, str)):
74 if(servport
.isnumeric()):
75 servport
= int(servport
);
76 if(servport
<1 or servport
>65535):
83 if(sslkeypem
is not None and
84 (not os
.path
.exists(sslkeypem
) or not os
.path
.isfile(sslkeypem
))):
87 if(sslcertpem
is not None and
88 (not os
.path
.exists(sslkeypem
) or not os
.path
.isfile(sslkeypem
))):
93 from BaseHTTPServer
import HTTPServer
;
94 from SimpleHTTPServer
import SimpleHTTPRequestHandler
;
95 from urlparse
import parse_qs
;
96 from Cookie
import SimpleCookie
98 from http
.server
import SimpleHTTPRequestHandler
, HTTPServer
;
99 from urllib
.parse
import parse_qs
;
100 from http
.cookies
import SimpleCookie
;
103 (sslkeypem
is not None and (os
.path
.exists(sslkeypem
) and os
.path
.isfile(sslkeypem
))) and
104 (sslcertpem
is not None and (os
.path
.exists(sslkeypem
) and os
.path
.isfile(sslkeypem
)))):
106 # HTTP/HTTPS Server Class
107 class CustomHTTPRequestHandler(SimpleHTTPRequestHandler
):
108 def compress_content(self
, content
):
109 """Compress content using gzip or deflate depending on Accept-Encoding header."""
110 accept_encoding
= self
.headers
.get('Accept-Encoding', '');
111 accept_encoding
= None;
112 if 'gzip' in accept_encoding
:
113 self
.send_header('Content-Encoding', 'gzip');
114 compressed_content
= gzip
.compress(content
.encode('utf-8'));
115 self
.send_header('Content-Length', len(compressed_content
));
116 return compressed_content
;
117 elif 'deflate' in accept_encoding
:
118 self
.send_header('Content-Encoding', 'deflate');
119 compressed_content
= zlib
.compress(content
.encode('utf-8'));
120 self
.send_header('Content-Length', len(compressed_content
));
121 return compressed_content
;
122 elif 'br' in accept_encoding
:
123 self
.send_header('Content-Encoding', 'br');
124 compressed_content
= brotli
.compress(content
.encode('utf-8'));
125 self
.send_header('Content-Length', len(compressed_content
));
126 return compressed_content
;
127 elif 'zstd' in accept_encoding
:
128 self
.send_header('Content-Encoding', 'zstd');
129 compressed_content
= zstandard
.compress(content
.encode('utf-8'));
130 self
.send_header('Content-Length', len(compressed_content
));
131 return compressed_content
;
132 elif 'lzma' in accept_encoding
:
133 self
.send_header('Content-Encoding', 'lzma');
134 compressed_content
= lzma
.compress(content
.encode('utf-8'));
135 self
.send_header('Content-Length', len(compressed_content
));
136 return compressed_content
;
137 elif 'xz' in accept_encoding
:
138 self
.send_header('Content-Encoding', 'xz');
139 compressed_content
= lzma
.compress(content
.encode('utf-8'));
140 self
.send_header('Content-Length', len(compressed_content
));
141 return compressed_content
;
143 self
.send_header('Content-Length', len(content
));
144 return content
.encode('utf-8');
145 return content
.encode();
146 def display_info(self
):
147 # Setting headers for the response
148 self
.send_response(200);
149 self
.send_header('Content-type', 'text/plain');
150 # Set a sample cookie in the response;
151 self
.send_header('Set-Cookie', 'sample_cookie=sample_value; Path=/;');
153 # Displaying request method
154 response
= 'Method: {}\n'.format(self
.command
);
155 response
+= 'Path: {}\n'.format(self
.path
);
156 # Displaying all headers
157 headers_list
= ["{}: {}".format(key
.title(), self
.headers
[key
]) for key
in self
.headers
];
158 response
+= '\nHeaders:\n' + '\n'.join(headers_list
) + '\n';
159 # Extract and display cookies from headers
160 if 'Cookie' in self
.headers
:
161 response
+= '\nCookies:\n';
162 cookies
= SimpleCookie(self
.headers
['Cookie']);
163 for key
, morsel
in cookies
.items():
164 response
+= '{}: {}\n'.format(key
, morsel
.value
);
165 # Displaying GET parameters (if any)
166 if self
.command
== 'GET':
167 query
= self
.path
.split('?', 1)[-1];
168 params
= parse_qs(query
);
170 response
+= '\nGET Parameters:\n';
171 for key
, values
in params
.items():
172 response
+= '{}: {}\n'.format(key
, ', '.join(values
));
173 # Sending the response
174 self
.wfile
.write(self
.compress_content(response
));
180 if 'Transfer-Encoding' in self
.headers
and self
.headers
['Transfer-Encoding'] == 'chunked':
183 chunk_size_line
= self
.rfile
.readline().decode('utf-8');
184 chunk_size
= int(chunk_size_line
, 16);
186 self
.rfile
.readline();
188 chunk_data
= self
.rfile
.read(chunk_size
).decode('utf-8');
189 post_data
+= chunk_data
;
190 self
.rfile
.readline();
192 content_length
= int(self
.headers
['Content-Length']);
193 post_data
= self
.rfile
.read(content_length
).decode('utf-8');
194 params
= parse_qs(post_data
);
195 response
= 'POST Parameters:\n';
196 for key
, values
in params
.items():
197 response
+= '{}: {}\n'.format(key
, ', '.join(values
));
198 self
.send_response(200);
199 self
.send_header('Content-type', 'text/plain');
200 self
.send_header('Set-Cookie', 'sample_cookie=sample_value; Path=/;');
202 self
.wfile
.write(self
.compress_content(response
));
203 # Start Server Forever
204 if __name__
== "__main__":
205 server_address
= ('', int(servport
));
206 httpd
= HTTPServer(server_address
, CustomHTTPRequestHandler
);
207 if(enablessl
and sslkeypem
is not None and sslcertpem
is not None):
208 httpd
.socket
= ssl
.wrap_socket (httpd
.socket
,
209 keyfile
=sslkeypem
, certfile
=sslcertpem
, server_side
=True);
211 print("Server started at https://localhost:"+str(servport
));
213 print("Server started at http://localhost:"+str(servport
));
214 httpd
.serve_forever();