Skip to content

Commit

Permalink
Add Date header to responses (#100)
Browse files Browse the repository at this point in the history
* Implement date header with optimized cache

* Version update
  • Loading branch information
JoshCap20 authored Sep 29, 2024
1 parent 567f533 commit 47420b0
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 10 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Areion Server

[![PyPi][pypi-shield]][pypi-url] [![PyPi][pypiversion-shield]][pypi-url] [![License][license-shield]][license-url]

[![License][license-shield]][license-url] [![Downloads](https://static.pepy.tech/badge/areion)](https://pepy.tech/project/areion)
[![Downloads](https://static.pepy.tech/badge/areion/month)](https://pepy.tech/project/areion)

[![PyPi][pypi-shield]][pypi-url] [![PyPi][pypiversion-shield]][pypi-url]

Welcome to the Areion HTTP Server documentation. Areion is a lightweight, asynchronous HTTP server written in Python, designed for simplicity and extensibility. This documentation provides a comprehensive guide to using Areion, covering everything from getting started to advanced usage and component details.

Expand Down
2 changes: 1 addition & 1 deletion areion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
create_xml_response,
)

__version__ = "v1.1.8"
__version__ = "v1.1.9"

__all__ = [
# Main classes
Expand Down
56 changes: 56 additions & 0 deletions areion/core/date_header_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
Why can't we just do headers["Date"] = datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT") and be done with it?
Performance.
"""

import threading
import datetime
import time


class DateHeaderCache:
"""
Singleton class to cache the Date header value, updating it once per second.
"""

_instance = None
_lock = threading.Lock()

def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super(DateHeaderCache, cls).__new__(cls)
cls._instance._initialize()
return cls._instance

def _initialize(self):
self.date_str = self._get_current_date()
self.update_lock = threading.Lock()
self.updater_thread = threading.Thread(target=self._update_date, daemon=True)
self.updater_thread.start()

def _get_current_date(self) -> str:
"""
Get the current UTC date formatted per HTTP standards.
Example: 'Wed, 21 Oct 2015 07:28:00 GMT'
"""
return datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT")

def _update_date(self):
"""
Continuously update the date string every second.
"""
while True:
new_date = self._get_current_date()
with self.update_lock:
self.date_str = new_date
time.sleep(1)

def get_date(self) -> str:
"""
Retrieve the current cached date string.
"""
with self.update_lock:
return self.date_str
8 changes: 4 additions & 4 deletions areion/core/response.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import orjson

from .date_header_cache import DateHeaderCache

HTTP_STATUS_CODES: dict[int, str] = {
100: "Continue",
Expand Down Expand Up @@ -161,6 +161,9 @@ def _format_headers(self) -> str:
Returns:
str: The formatted headers.
"""
self.headers["Date"] = DateHeaderCache().get_date()
self.headers["Server"] = "Areion"

return "".join(f"{key}: {value}\r\n" for key, value in self.headers.items())

def format_response(self) -> bytes:
Expand All @@ -171,9 +174,6 @@ def format_response(self) -> bytes:
bytes: The formatted HTTP response.
"""
body = self._format_body()
self.headers["Server"] = (
"Areion" # Looks cooler with version in response but security risk
)
self.headers["Content-Length"] = str(len(body))
response_line = self._get_response_line()
headers = self._format_headers()
Expand Down
2 changes: 1 addition & 1 deletion areion/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/ / /-----| | | |
/ / / | | | |
__/___/__/_______|_|__\\_\\___
// v1.1.8 \\
// v1.1.9 \\
// A R E I O N \\
// joshcap20/areion \\
//________________________________\\
Expand Down
5 changes: 3 additions & 2 deletions areion/tests/core/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,9 @@ def assert_responses_equal(self, response1: HttpResponse, response2: HttpRespons
self.assertIsInstance(response2, HttpResponse)

# Remove content length header
response1.headers.pop("Content-Length", None)
response2.headers.pop("Content-Length", None)
for header in ["Content-Length", "Date"]:
response1.headers.pop(header, None)
response2.headers.pop(header, None)

self.assertEqual(response1.status_code, response2.status_code)
self.assertEqual(response1.body, response2.body)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "pdm.backend"

[project]
name = "areion"
version = "1.1.8"
version = "1.1.9"
authors = [
{ name="Josh Caponigro", email="joshcaponigro@gmail.com" },
]
Expand Down

0 comments on commit 47420b0

Please sign in to comment.