ソフトの説明
PythonでローカルむけSSL対応のローカルサーバーをたてます。
Pythonで作成した自己SSL証明書を読み込み指定のフォルダ以下をROOTとしたローカルサーバーをたてます。
プログラムの説明
参考にしたPythonのプログラムがTkinterによるGUI付きIP指定ローカルサーバーでしたのでこれを改造し127.0.0.1専用のローカルSSLサーバーとします。
QUEUEで起動する仕様でしたのでSSL証明書があれば自動で読み込みIcon化してポート443のSSLサーバーをたてる仕様としました。
SSL証明書がなければポート80のサーバーをたてます。
SSL証明書作成アプリ
プログラムのカレントフォルダの証明書を呼び込みますのでSSL証明書作成アプリの横に配置します。
ここに「root」フォルダを作成してください。起動時このフォルダをデフォルトのルートとして起動します。
アイコンが必要になりますのでこちらからどうぞ
この状態にて起動しブラウザのアドレス「https://127.0.0.1:443」を指定すると無事ルートに置いたJSONファイルが確認できました。
証明書がないと「http://127.0.0.1:80」で接続可能です。
ssl_server.py
#!/usr/bin/env python
import os
import sys
import threading
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from tkinter import filedialog
from http.server import HTTPServer, BaseHTTPRequestHandler
from http.server import SimpleHTTPRequestHandler
from socketserver import ThreadingMixIn
import queue as Queue
#import ssl
#from ipaddress import ip_address
#from urllib import request
#import tempfile
USE_HTTPS_PEM = False
USE_HTTPS_KEY = False
PORT = 80
PEM =""
PEM_KEY=""
public_key=""
Fpath = ""
class Pem:
def isthere(self):
cpath = os.getcwd()
#print(os.listdir(cpath))
global USE_HTTPS_PEM
global USE_HTTPS_KEY
global PORT
global PEM
global PEM_KEY
if not os.listdir(cpath):
USE_HTTPS_PEM = False
USE_HTTPS_KEY = False
PORT = 80
pathf = './127.0.0.1.pem'
if os.path.exists(pathf):
USE_HTTPS_PEM = True
USE_HTTPS_KEY = False
PORT = 443
PEM = os.getcwd() + '\\127.0.0.1.pem'
PEM_KEY = os.getcwd() + '\\127.0.0.1_key.pem'
#pathf = './127.0.0.1.crt'
#if os.path.exists(pathf):
# USE_HTTPS_PEM = False
# USE_HTTPS_KEY = True
# PORT = 443
# PEM = os.getcwd()+'\\127.0.0.1.crt'
# PEM_KEY = os.getcwd()+'\\127.0.0.1.key'
class CORSHTTPRequestHandler(SimpleHTTPRequestHandler,BaseHTTPRequestHandler):
#consle off for nuitka
def log_message(self, format, *args):
pass
def send_head(self):
"""Common code for GET and HEAD commands.
This sends the response code and MIME headers.
Return value is either a file object (which has to be copied
to the outputfile by the caller unless the command was HEAD,
and must be closed by the caller under all circumstances), or
None, in which case the caller has nothing further to do.
"""
path = self.translate_path(self.path)
#path = os.getcwd()
#print(path)
f = None
if os.path.isdir(path):
if not self.path.endswith('/'):
# redirect browser - doing basically what apache does
self.send_response(301)
self.send_header("Location", self.path + "/")
self.end_headers()
return None
for index in "index.html", "index.htm":
index = os.path.join(path, index)
if os.path.exists(index):
path = index
break
else:
return self.list_directory(path)
ctype = self.guess_type(path)
try:
# Always read in binary mode. Opening files in text mode may cause
# newline translations, making the actual size of the content
# transmitted *less* than the content-length!
f = open(path, 'rb')
except IOError:
self.send_error(404, "File not found")
return None
self.send_response(200)
self.send_header("Content-type", ctype)
fs = os.fstat(f.fileno())
self.send_header("Content-Length", str(fs[6]))
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
#self.send_header("Access-Control-Allow-Origin", "https://booleaneffect.info")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
return f
class ThreadingSimpleServer(ThreadingMixIn, HTTPServer):
pass
class ServerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.queue = server_queue
self.is_server_running = False
self.daemon = True
def create_server(self, port=PORT):
self.server = ThreadingSimpleServer(("127.0.0.1", port), CORSHTTPRequestHandler, HTTPServer)
if USE_HTTPS_PEM:
import ssl
self.server.socket = ssl.wrap_socket(self.server.socket,
server_side=True,
do_handshake_on_connect=True,
certfile=PEM,
keyfile=PEM_KEY)
if USE_HTTPS_KEY:
import ssl
self.server.socket = ssl.wrap_socket(self.server.socket,
server_side=True,
do_handshake_on_connect=True,
certfile=PEM,
keyfile=PEM_KEY)
self.serv_info = self.server.socket.getsockname()
def run(self):
while True:
if not self.queue.empty():
i = self.queue.get()
if i[0] == 'start':
self.create_server(int(i[1]))
self.is_server_running = True
#print('SERVING @: {}:{}'.format(self.serv_info[0], self.serv_info[1]))
self.server.serve_forever()
elif i[0] == 'stop':
self.is_server_running = False
#print('SERVER SHUTDOWN')
self.server = None
class Gui(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.server_running = False
#f0 = ttk.Frame(self)
#f1 = ttk.Frame(self)
f2 = ttk.Frame(self)
f3 = ttk.Frame(self)
f4 = ttk.Frame(self)
f5 = ttk.Frame(self)
#ttk.Label(f1, text='Your IP address: {}'.format(ip_address), font='Helvetica 10 bold').pack()
ttk.Label(f2, text='ポート: ', font='Helvetica 10 bold').pack(side='left')
self.port = ttk.Entry(f2, width=6)
self.port.insert('end', PORT)
self.port.pack(side='left', padx=0, pady=10)
ttk.Label(f3, text='フォルダパス: ', font='Helvetica 10 bold').pack(side='left')
self.path_entry = ttk.Entry(f3, width=55)
self.path_entry.insert('end', OWD)
self.path_entry.pack(side='left')
ttk.Button(f3, text='Browse', command=self.browse).pack(side='left', padx=10)
ttk.Label(f4, text='スタート/ストップ: ', font='Helvetica 10 bold').pack(side='left')
self.start_btn = ttk.Button(f4, text='Start server', command=self.start_clicked)
self.start_btn.pack(side='left', padx=10)
self.stop_btn = ttk.Button(f4, text='Stop server', command=self.stop_clicked, state='disabled')
self.stop_btn.pack(side='left', padx=10)
ttk.Label(f5, text='Server state: ', font='Helvetica 10 bold').pack(side='left')
self.serverstate_lbl = ttk.Label(f5, text='Stopped', font='Helvetica 15 bold', foreground='red')
self.serverstate_lbl.pack(side='left')
#f0.grid(column=1, row=0, columnspan=3, padx=10, pady=10, sticky='w')
#f1.grid(column=1, row=1, padx=10, pady=10, sticky='w')
f2.grid(column=1, row=2, padx=10, pady=10, sticky='w')
f3.grid(column=1, row=3, columnspan=3, padx=10, pady=10, sticky='w')
f4.grid(column=1, row=4, padx=10, pady=10, sticky='w')
f5.grid(column=2, row=4, padx=10, pady=10, sticky='w')
def browse(self):
path = filedialog.askdirectory()
if os.path.isdir(path):
self.path_entry.delete('0', tk.END)
self.path_entry.insert('end', path)
def start_clicked(self):
# if server_thread.is_server_running:
# messagebox.showerror('Already running','The server is already running. Please stop the server first!')
# return
if switch_dir(self.path_entry.get()):
#port = check_port(self.port.get().strip())
port = PORT
if not port:
#messagebox.showerror('Error', 'Please enter a valid port number between 1-65535')
return
server_queue.put(('start', port))
# wait for the server_thread to switch is_server_running = True
while not server_thread.is_server_running:
pass
self.toggle_state_widgets()
#messagebox.showinfo('NOW SERVING', 'Now serving {} @{}:{}'.format(os.getcwd(), ip_address, port))
else:
messagebox.showerror('Invalid path', 'The path you have provided does not exist!')
def stop_clicked(self, notify=True):
if server_thread.is_server_running:
server_queue.put(('stop', ''))
server_thread.server.shutdown()
# wait for the server thread to switch isruuning = False
while server_thread.is_server_running:
pass
self.toggle_state_widgets()
#if notify:
# messagebox.showinfo('SERVER SHUTDOWN', 'The server has been shutdown.')
def toggle_state_widgets(self):
if server_thread.is_server_running:
self.start_btn.configure(state='disabled')
self.stop_btn.configure(state='normal')
txt = 'Serving'
color = 'green'
else:
self.start_btn.configure(state='normal')
self.stop_btn.configure(state='disabled')
txt = 'Stopped'
color = 'red'
self.serverstate_lbl.configure(text=txt, foreground=color)
def switch_dir(path):
if os.path.isdir(path):
os.chdir(path)
return True
if path == '':
os.chdir(Fpath)
return True
else:
return
def check_port(port):
try:
p = int(port)
if p > 65535:
return
else:
return p
except Exception as e:
print(e)
return
if __name__ == '__main__':
OWD = os.path.dirname(sys.argv[0])
OWD = os.path.join(OWD, "root").replace('/', os.sep)
Fpath = OWD
pem = Pem()
pem.isthere()
# 起動時にlocalhostを立てる。最小化
root = Gui()
root.title('LocalServer')
root.iconbitmap(default='sk.ico')
root.iconify()
root.after(1, root.start_clicked)
server_queue = Queue.Queue()
server_thread = ServerThread()
server_thread.start()
root.mainloop()
参考リンク Spcial thanks
dojafoja / GUI-python-server