4
4
import datetime
5
5
import zlib
6
6
from logger import logger
7
+ from status import HTTP_STATUS_CODES
7
8
8
9
# Configuración del servidor
9
10
HOST = '0.0.0.0' # Escucha en todas las interfaces
@@ -22,7 +23,7 @@ def start(self):
22
23
server_socket .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEADDR , 1 ) # Permite reutilizar el puerto
23
24
server_socket .bind ((self .host , self .port ))
24
25
server_socket .listen (5 )
25
-
26
+
26
27
logger .info (f"Servidor HTTP en { self .host } :{ self .port } " )
27
28
28
29
while True :
@@ -44,6 +45,7 @@ def handle_client(self, client_socket):
44
45
45
46
except Exception as e :
46
47
logger .error (f"Error al procesar la solicitud: { e } " )
48
+ client_socket .sendall (self .http_response (500 , "Internal Server Error" , "An unexpected error occurred." ).encode ())
47
49
48
50
finally :
49
51
client_socket .close ()
@@ -79,41 +81,94 @@ def parse_headers(self, header_lines):
79
81
def build_response (self , method , path , headers , body ):
80
82
"""Genera respuestas HTTP según el método y la ruta solicitada."""
81
83
82
- logger .info (f"Method : { method } " )
84
+ logger .info (f"Method : { method } - Path: { path } " )
85
+
86
+ # 414 - URI Too Long
87
+ if len (path ) > 2048 :
88
+ return self .http_response (414 )
89
+
90
+ # Verificar si el cliente tiene una versión válida del recurso (usando If-Modified-Since o If-None-Match)
91
+ last_modified = self .get_last_modified (path ) # Esta función puede devolver la última fecha de modificación del recurso
92
+ if last_modified :
93
+ if "If-Modified-Since" in headers :
94
+ if_modified_since = headers ["If-Modified-Since" ]
95
+ if if_modified_since >= last_modified :
96
+ return self .http_response (304 , headers = headers ) # No se ha modificado, devolver 304
97
+
98
+ if "If-None-Match" in headers :
99
+ if_none_match = headers ["If-None-Match" ]
100
+ if if_none_match == self .get_etag (path ): # Comparar con el ETag
101
+ return self .http_response (304 , headers = headers ) # ETag coincide, devolver 304
102
+
103
+ # Redirecciones
104
+ if path == "/old-page" :
105
+ return self .http_response (301 , location = "/new-page" )
106
+
107
+ if path == "/temp-move" :
108
+ return self .http_response (302 , location = "/temporary-page" ) # 302 Found
109
+
110
+
111
+ # Si la ruta no existe
112
+ if path != "/" :
113
+ return self .http_response (404 )
114
+
115
+ # 411 - Length Required
116
+ if method in ["POST" , "PUT" ] and "Content-Length" not in headers :
117
+ return self .http_response (411 )
118
+
119
+ # 415 - Unsupported Media Type
120
+ if method in ["POST" , "PUT" ]:
121
+ supported_types = ["application/json" , "application/x-www-form-urlencoded" , "text/plain" ]
122
+ if "Content-Type" in headers and headers ["Content-Type" ] not in supported_types :
123
+ return self .http_response (415 )
124
+
83
125
if method == "GET" :
84
- return self .http_response (200 , "OK" , " Hello, GET!" )
126
+ return self .http_response (200 , "Hello, GET!" )
85
127
86
128
elif method == "POST" :
87
- return self .http_response (200 , "OK" , f"Received POST: { body } " )
129
+ return self .http_response (200 , f"Received POST: { body } " )
88
130
89
131
elif method == "HEAD" :
90
- return self .http_response (200 , "OK" , " " , include_body = False )
132
+ return self .http_response (200 , "" , include_body = False )
91
133
92
134
elif method == "PUT" :
93
- return self .http_response (200 , "OK" , " PUT request successful!" )
135
+ return self .http_response (200 , "PUT request successful!" )
94
136
95
137
elif method == "DELETE" :
96
- return self .http_response (200 , "OK" , " DELETE request successful!" )
138
+ return self .http_response (200 , "DELETE request successful!" )
97
139
98
140
elif method == "OPTIONS" :
99
- return self .http_response (200 , "OK" , " " , headers = {"Allow" : "OPTIONS, GET, POST, HEAD, PUT, DELETE, TRACE, CONNECT" })
141
+ return self .http_response (200 , "" , headers = {"Allow" : "OPTIONS, GET, POST, HEAD, PUT, DELETE, TRACE, CONNECT" })
100
142
101
143
elif method == "TRACE" :
102
- return self .http_response (200 , "OK" , f"TRACE received:\n { headers } " )
144
+ return self .http_response (200 , f"TRACE received:\n { headers } " )
103
145
104
146
else :
105
- return self .http_response (405 , "Method Not Allowed" , "This method is not supported." )
147
+ return self .http_response (405 )
106
148
107
- def http_response (self , status_code , reason , body , headers = None , include_body = True ):
149
+ def http_response (self , status_code , reason , body , headers = None ,location = None , include_body = True ):
108
150
"""Construye una respuesta HTTP válida con headers y cuerpo."""
109
151
headers = headers or {}
110
152
headers ["Server" ] = "CustomHTTPServer/1.0"
111
153
headers ["Date" ] = datetime .datetime .now (datetime .timezone .utc ).strftime ("%a, %d %b %Y %H:%M:%S GMT" )
112
154
155
+
156
+ # Obtener el mensaje de estado y la descripción
157
+ reason , default_body = HTTP_STATUS_CODES .get (status_code , ("Unknown" , "Unknown error" ))
158
+
159
+ # Manejo de redirecciones (301, 302, etc.)
160
+ if status_code in [301 , 302 , 304 ] and location :
161
+ headers ["Location" ] = location
162
+ body = f"Redirecting to { location } "
163
+
164
+ # Si no se especifica un body, usar el mensaje por defecto
165
+ if not body and include_body :
166
+ body = default_body
167
+
113
168
# Verificar si el cliente acepta gzip
114
- acceptes_encoding = headers .get ("accept-encoding " ,"" )
115
- accepts_gzip = "gzip" in acceptes_encoding
116
- accepts_deflate = "deflate" in acceptes_encoding
169
+ accept_encoding = headers .get ("Accept-Encoding " ,"" ). lower ( )
170
+ accepts_gzip = "gzip" in accept_encoding
171
+ accepts_deflate = "deflate" in accept_encoding
117
172
118
173
if include_body :
119
174
body_bytes = body .encode ()
@@ -135,7 +190,7 @@ def http_response(self, status_code, reason, body, headers=None, include_body=Tr
135
190
response += f"{ key } : { value } \r \n "
136
191
response += "\r \n "
137
192
138
- logger .debug (f"Respuesta generada: { status_code } { reason } " )
193
+ logger .debug (f"Respuesta generada: { status_code } { reason } { '-> ' + location if location else '' } " )
139
194
return response + body_bytes .decode ()
140
195
141
196
0 commit comments