以下保留檔案原文,沒有改寫資料或結果。可使用「複製原始內容」取得完整檔案。
"""Small, real HTTP + SQLite teaching service. Never use for real payments."""
import argparse
import json
import sqlite3
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
def serve(database, ready_file, fault):
class Handler(BaseHTTPRequestHandler):
def log_message(self, *args):
pass
def reply(self, status, body):
encoded = json.dumps(body).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(encoded)))
self.end_headers()
self.wfile.write(encoded)
def do_POST(self):
if self.path != "/payments":
return self.reply(404, {"error": "NOT_FOUND"})
try:
length = int(self.headers.get("Content-Length", "0"))
if not 0 < length <= 4096:
raise ValueError("invalid body length")
payload = json.loads(self.rfile.read(length))
order_id = payload.get("order_id") if isinstance(payload, dict) else None
if not isinstance(order_id, str) or not order_id.strip():
raise ValueError("order_id is required")
except (ValueError, UnicodeDecodeError):
return self.reply(400, {"error": "INVALID_REQUEST"})
connection = sqlite3.connect(database, timeout=5)
try:
# Acquire the write transaction before reading: concurrent requests
# cannot both observe PENDING and both create a payment.
connection.execute("BEGIN IMMEDIATE")
row = connection.execute(
"SELECT status FROM orders WHERE id = ?", (order_id,)
).fetchone()
if row is None:
connection.rollback()
return self.reply(404, {"error": "ORDER_NOT_FOUND"})
if row[0] == "PAID":
if fault == "duplicate-write":
connection.execute(
"INSERT INTO payments(order_id) VALUES (?)", (order_id,)
)
connection.commit()
else:
connection.rollback()
error = "INVALID_STATE" if fault == "wrong-error" else "ORDER_ALREADY_PAID"
return self.reply(409, {"error": error})
connection.execute("INSERT INTO payments(order_id) VALUES (?)", (order_id,))
connection.execute("UPDATE orders SET status = 'PAID' WHERE id = ?", (order_id,))
connection.commit()
return self.reply(200, {"order_id": order_id, "status": "PAID"})
finally:
connection.close()
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
Path(ready_file).write_text(json.dumps({
"host": "127.0.0.1", "port": server.server_port,
"implementation": "quality-workflow-payment-demo", "fault": fault,
}), encoding="utf-8")
server.serve_forever()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--db", required=True)
parser.add_argument("--ready-file", required=True)
parser.add_argument("--fault", choices=["none", "duplicate-write", "wrong-error"], default="none")
args = parser.parse_args()
serve(args.db, args.ready_file, args.fault)