Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions https.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import sys
import os.path
import uuid
from glob import glob
import socket
import threading
from datetime import datetime

class HttpServer:
def __init__(self):
self.sessions={}
self.types={}
self.types['.pdf']='application/pdf'
self.types['.jpg']='image/jpeg'
self.types['.txt']='text/plain'
self.types['.html']='text/html'

def response(self,kode=404,message='Not Found',messagebody=bytes(),headers={}):
tanggal = datetime.now().strftime('%c')
resp=[]
resp.append("HTTP/1.0 {} {}\r\n" . format(kode,message))
resp.append("Date: {}\r\n" . format(tanggal))
resp.append("Connection: close\r\n")
resp.append("Server: myserver/1.0\r\n")
resp.append("Content-Length: {}\r\n" . format(len(messagebody)))
for kk in headers:
resp.append("{}:{}\r\n" . format(kk,headers[kk]))
resp.append("\r\n")

response_headers=''
for i in resp:
response_headers="{}{}" . format(response_headers,i)
#menggabungkan resp menjadi satu string dan menggabungkan dengan messagebody yang berupa bytes
#response harus berupa bytes
#message body harus diubah dulu menjadi bytes
if (type(messagebody) is not bytes):
messagebody = messagebody.encode()

response = response_headers.encode() + messagebody
#response adalah bytes
return response

def proses(self,data):

requests = data.split("\r\n")
#print(requests)

baris = requests[0]
#print(baris)

all_headers = [n for n in requests[1:] if n!='']

j = baris.split(" ")
try:
method=j[0].upper().strip()
if (method=='GET'):
object_address = j[1].strip()
return self.http_get(object_address, all_headers)
if (method=='POST'):
object_address = j[1].strip()
return self.http_post(object_address, all_headers)
else:
return self.response(400,'Bad Request','',{})
except IndexError:
return self.response(400,'Bad Request','',{})
def http_get(self,object_address,headers):
files = glob('./*')
#print(files)
thedir='./'
if (object_address == '/'):
return self.response(200,'OK','Ini Adalah web Server percobaan',dict())

if (object_address == '/video'):
return self.response(302,'Found','',dict(location='https://youtu.be/katoxpnTf04'))
if (object_address == '/santai'):
return self.response(200,'OK','santai saja',dict())


object_address=object_address[1:]
if thedir+object_address not in files:
return self.response(404,'Not Found','',{})
fp = open(thedir+object_address,'rb') #rb => artinya adalah read dalam bentuk binary
#harus membaca dalam bentuk byte dan BINARY
isi = fp.read()

fext = os.path.splitext(thedir+object_address)[1]
content_type = self.types[fext]

headers={}
headers['Content-type']=content_type

return self.response(200,'OK',isi,headers)
def http_post(self,object_address,headers):
headers ={}
isi = "kosong"
return self.response(200,'OK',isi,headers)

def start_http_server(self, host='localhost', port=8000):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((host, port))
server_socket.listen(5)
print(f"[HTTP] Server listening on {host}:{port}")

while True:
conn, addr = server_socket.accept()
threading.Thread(target=self.handle_connection, args=(conn, addr), daemon=True).start()

def handle_connection(self, conn, addr):
try:
data = conn.recv(1024).decode()
if data:
response = self.proses(data)
conn.sendall(response)
except Exception as e:
print(f"[HTTP ERROR] {e}")
finally:
conn.close()

#>>> import os.path
#>>> ext = os.path.splitext('/ak/52.png')

if __name__=="__main__":
httpserver = HttpServer()
d = httpserver.proses('GET testing.txt HTTP/1.0')
print(d)
d = httpserver.proses('GET donalbebek.jpg HTTP/1.0')
print(d)
#d = httpserver.http_get('testing2.txt',{})
#print(d)
# d = httpserver.http_get('testing.txt')
# print(d)
164 changes: 69 additions & 95 deletions server.py → server_thread_pool_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import string
import random
import time
from concurrent.futures import ProcessPoolExecutor
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
from enum import Enum
from typing import Dict, List
import logging
from https import HttpServer

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -178,37 +179,50 @@ def broadcast_game_update(self):
logger.error(f"Error sending update to player {player.id}: {e}")

class GameServer:
def __init__(self, host='localhost', port=8888):
def __init__(self, host='0.0.0.0', port=8888):
self.host = host
self.port = port
self.games: Dict[str, GameSession] = {}
self.httpserver = HttpServer()
self.client_to_game: Dict[str, str] = {}
self.executor = ProcessPoolExecutor(max_workers=4)
self.running = False

def create_room(self, level="normal") -> str:
room_id = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
self.games[room_id] = GameSession(room_id, level=level)
logger.info(f"Created room: {room_id} with level: {level}")
return room_id
self.games: Dict[str, GameSession] = {}
self.running = True

def join_room(self, room_id: str, player: Player) -> bool:
if room_id in self.games:
success = self.games[room_id].add_player(player)
if success:
self.client_to_game[player.id] = room_id
logger.info(f"Player {player.id} joined room {room_id}")
return success
return False
def handle_client(self, connection, address):
rcv = ""
player_id = str(uuid.uuid4())
logger.info(f"New client connected: {address} (ID: {player_id})")
try:
while self.running:
data = connection.recv(1024)
if not data:
break
decoded = data.decode().strip()
if decoded.startswith("GET") or decoded.startswith("POST"):
response = self.httpserver.proses(decoded)
response += "\r\r".encode()
connection.sendall(response)
break
else:
try:
message = json.loads(decoded)
response = self.handle_game_message(connection, player_id, message)
self.send_message(connection, response)
except json.JSONDecodeError:
self.send_message(connection, {"success": False, "message": "Invalid JSON"})
except Exception as e:
logger.error(f"Client error: {e}")
finally:
self.cleanup_client(player_id)
connection.close()
logger.info(f"Client {address} disconnected")

def handle_client_message(self, client_socket, player_id: str, message: Dict):
def handle_game_message(self, client_socket, player_id, message):
try:
action = message.get('action')
response = {"success": False, "message": "Unknown action"}

if action == 'create_room':
level = message.get('level', 'normal')
room_id = self.create_room(level=level)
room_id = self.create_room(level)
player = Player(player_id, client_socket, message.get('player_name', ''))
self.join_room(room_id, player)
response = {
Expand All @@ -217,7 +231,6 @@ def handle_client_message(self, client_socket, player_id: str, message: Dict):
"player_id": player_id,
"game_state": self.games[room_id].get_game_state()
}

elif action == 'join_room':
room_id = message.get('room_id')
if room_id in self.games:
Expand All @@ -237,40 +250,50 @@ def handle_client_message(self, client_socket, player_id: str, message: Dict):
response = {"success": False, "message": "Room is full"}
else:
response = {"success": False, "message": "Room not found"}

elif action == 'reveal_card':
room_id = self.client_to_game.get(player_id)
if room_id and room_id in self.games:
card_id = message.get('card_id')
result = self.games[room_id].reveal_card(card_id, player_id)
result["game_state"] = self.games[room_id].get_game_state()
response = result
response["game_state"] = self.games[room_id].get_game_state()
self.broadcast_to_room(room_id, {
"type": "game_update",
"result": result,
"game_state": self.games[room_id].get_game_state()
})
else:
response = {"success": False, "message": "Not in a game"}

elif action == 'get_game_state':
room_id = self.client_to_game.get(player_id)
if room_id and room_id in self.games:
response = {
"success": True,
"game_state": self.games[room_id].get_game_state()
}

return response

except Exception as e:
logger.error(f"Error handling message: {e}")
return {"success": False, "message": "Server error"}

def create_room(self, level="normal") -> str:
room_id = ''.join(random.choices("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", k=6))
self.games[room_id] = GameSession(room_id, level=level)
logger.info(f"Created room: {room_id} with level: {level}")
return room_id

def join_room(self, room_id: str, player: Player) -> bool:
if room_id in self.games:
success = self.games[room_id].add_player(player)
if success:
self.client_to_game[player.id] = room_id
logger.info(f"Player {player.id} joined room {room_id}")
return success
return False

def broadcast_to_room(self, room_id: str, message: Dict):
if room_id in self.games:
game = self.games[room_id]
for player in game.players.values():
for player in self.games[room_id].players.values():
try:
self.send_message(player.socket, message)
except Exception as e:
Expand All @@ -283,83 +306,34 @@ def send_message(self, client_socket, message: Dict):
except Exception as e:
logger.error(f"Error sending message: {e}")

@staticmethod
def send_message_static(client_socket, message: Dict):
try:
json_message = json.dumps(message)
client_socket.send(f"{json_message}\n".encode())
except Exception as e:
logger.error(f"Error sending message: {e}")

def handle_client(self, client_socket, client_address):
player_id = str(uuid.uuid4())
logger.info(f"New client connected: {client_address} (ID: {player_id})")

try:
while self.running:
data = client_socket.recv(1024).decode().strip()
if not data:
break
try:
message = json.loads(data)
response = self.handle_client_message(client_socket, player_id, message)
self.send_message(client_socket, response)
except json.JSONDecodeError:
self.send_message(client_socket, {"success": False, "message": "Invalid JSON"})

except Exception as e:
logger.error(f"Error with client {client_address}: {e}")
finally:
self.cleanup_client(player_id)
client_socket.close()
logger.info(f"Client {client_address} disconnected")

def cleanup_client(self, player_id: str):
if player_id in self.client_to_game:
room_id = self.client_to_game[player_id]
if room_id in self.games:
game = self.games[room_id]
if player_id in game.players:
del game.players[player_id]
if len(game.players) == 0:
if player_id in self.games[room_id].players:
del self.games[room_id].players[player_id]
if not self.games[room_id].players:
del self.games[room_id]
logger.info(f"Removed empty room: {room_id}")
del self.client_to_game[player_id]

def start_server(self):
self.running = True
def start(self):
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind((self.host, self.port))
server_socket.listen(5)
logger.info(f"Unified Server listening on {self.host}:{self.port}")

logger.info(f"Memory Card Game Server started on {self.host}:{self.port}")

try:
while self.running:
client_socket, client_address = server_socket.accept()
client_thread = threading.Thread(
target=self.handle_client,
args=(client_socket, client_address)
)
client_thread.daemon = True
client_thread.start()
except KeyboardInterrupt:
logger.info("Server shutting down...")
finally:
self.running = False
server_socket.close()
self.executor.shutdown(wait=True)

def main():
import argparse
parser = argparse.ArgumentParser(description='Memory Card Game Server')
parser.add_argument('--host', default='localhost', help='Server host')
parser.add_argument('--port', type=int, default=8888, help='Server port')
args = parser.parse_args()

server = GameServer(args.host, args.port)
server.start_server()
with ThreadPoolExecutor(max_workers=20) as executor:
try:
while self.running:
conn, addr = server_socket.accept()
executor.submit(self.handle_client, conn, addr)
except KeyboardInterrupt:
logger.info("Server shutting down...")
finally:
server_socket.close()
self.running = False

if __name__ == "__main__":
main()
GameServer().start()