1.目的
・apacheのアイドルタイムアウト値(timeout)を検証する
※timeout値の設定でアイドルセッションが切れてしまうのか?検証
2.環境
OS:Red Hat Enterprise Linux release 8.5 (Ootpa)
Python:Python 3.6.8
apche:2.4.37
ファイル名:apache_timeout_idle_test.py
3.実行方法
python3△apache_timeout_idle_test.py
4.実行結果
(1)timeout内
ApacheサーバーのURLを入力してください (デフォルト: http://192.168.56.132/):
入力がありませんでした。デフォルト値 'http://192.168.56.132/' を使用します。
アイドルタイムアウトをテストする秒数を入力してください (デフォルト: 120): 30
テスト開始時刻: 2024-09-28 22:19:26
最初のリクエストを送信しています: http://192.168.56.132/
接続が成功しました。サーバー応答: 200 OK
最初のリクエストで使用されたポート番号: 44004
30秒間アイドル状態になります...
待機時間: 00:00 残り
再度リクエストを送信して、接続が維持されているか確認します...
Success: 30秒後も接続が維持されています。サーバー応答: 200 OK
再度リクエストで使用されたポート番号: 44004
ポート番号が一致しています。接続は維持されています。
テスト終了時刻: 2024-09-28 22:19:56
テスト成功: 接続はアイドル状態後も維持されました。
(2)timeoutオーバ時
ApacheサーバーのURLを入力してください (デフォルト: http://192.168.56.132/):
入力がありませんでした。デフォルト値 'http://192.168.56.132/' を使用します。
アイドルタイムアウトをテストする秒数を入力してください (デフォルト: 120): 45
テスト開始時刻: 2024-09-28 22:05:07
最初のリクエストを送信しています: http://192.168.56.132/
接続が成功しました。サーバー応答: 200 OK
最初のリクエストで使用されたポート番号: 39294
45秒間アイドル状態になります...
待機時間: 00:00 残り
再度リクエストを送信して、接続が維持されているか確認します...
再度のリクエストでエラーが発生しました: Remote end closed connection without response
再度リクエストで使用されたポート番号: 不明
ポート番号が一致しません。接続が切断された可能性があります。
最初のリクエスト: 39294, 再リクエスト: 不明
テスト終了時刻: 2024-09-28 22:05:52
テスト失敗: 接続が維持されませんでした。
5.プログラム
import time
import http.client
import sys
from urllib.parse import urlparse
from datetime import datetime
def get_local_port_from_socket(conn):
"""
ソケット接続からローカルポート番号を取得します。
"""
try:
return conn.sock.getsockname()[1]
except AttributeError:
# ソケットが存在しない場合(接続が閉じられている場合)
return "不明"
def display_wait_time(idle_duration):
"""
待機時間をリアルタイムで表示します。
Args:
idle_duration (int): アイドル状態の秒数
"""
for remaining in range(idle_duration, 0, -1):
minutes, seconds = divmod(remaining, 60)
time_format = f"{minutes:02d}:{seconds:02d}"
sys.stdout.write(f"\r待機時間: {time_format} 残り")
sys.stdout.flush()
time.sleep(1)
sys.stdout.write("\r待機時間: 00:00 残り\n")
sys.stdout.flush()
def check_idle_timeout(url, idle_duration):
"""
Apacheのアイドルタイムアウトを検証するプログラム
Args:
url (str): ApacheサーバーのURL
idle_duration (int): アイドル状態の秒数
Returns:
bool: テストが成功したかどうか
"""
try:
# URLを解析し、ホストとポートを取得
parsed_url = urlparse(url)
hostname = parsed_url.hostname
port = parsed_url.port or (443 if parsed_url.scheme == 'https' else 80)
# 接続を管理するためのHTTPConnectionを作成
if parsed_url.scheme == 'https':
conn = http.client.HTTPSConnection(hostname, port)
else:
conn = http.client.HTTPConnection(hostname, port)
# 最初のリクエストを送信して、接続を確立
print(f"最初のリクエストを送信しています: {url}")
conn.request("GET", parsed_url.path or "/")
response = conn.getresponse()
if response.status == 200:
print("接続が成功しました。サーバー応答: 200 OK")
else:
print(f"接続が成功しましたが、ステータスコード: {response.status}")
# 使用されたポート番号を取得して表示
used_port = get_local_port_from_socket(conn)
print(f"最初のリクエストで使用されたポート番号: {used_port}")
# 応答ストリームを少しだけ消費する(全データを読み込まない)
response.read(1024) # 少しだけデータを読み込んで接続を維持
# アイドル状態を作る(指定された時間待機)
print(f"{idle_duration}秒間アイドル状態になります...")
display_wait_time(idle_duration)
# 同じ接続を使って再度リクエストを送信して、接続が維持されているか確認
print("再度リクエストを送信して、接続が維持されているか確認します...")
try:
conn.request("GET", parsed_url.path or "/")
response = conn.getresponse()
if response.status == 200:
print(f"Success: {idle_duration}秒後も接続が維持されています。サーバー応答: 200 OK")
else:
print(f"Warning: 接続が維持されていますが、ステータスコード: {response.status}")
# 再度リクエスト時のポート番号を取得して表示
used_port_after_idle = get_local_port_from_socket(conn)
print(f"再度リクエストで使用されたポート番号: {used_port_after_idle}")
# ポート番号の比較
if used_port == used_port_after_idle:
print(f"ポート番号が一致しています。接続は維持されています。")
success = True
else:
print(f"Error: ポート番号が一致しません。接続が切断された可能性があります。")
print(f"最初のリクエスト: {used_port}, 再リクエスト: {used_port_after_idle}")
success = False # ポート番号が異なる場合は失敗とみなす
except Exception as e:
# 再度リクエストが失敗した場合でもポート番号を表示
print(f"再度のリクエストでエラーが発生しました: {e}")
used_port_after_idle = get_local_port_from_socket(conn)
print(f"再度リクエストで使用されたポート番号: {used_port_after_idle}")
print(f"ポート番号が一致しません。接続が切断された可能性があります。")
print(f"最初のリクエスト: {used_port}, 再リクエスト: {used_port_after_idle}")
success = False
conn.close() # 接続を閉じる
except Exception as e:
# 例外処理(接続エラーなど)
print(f"Error: {e}")
success = False
return success
def get_input_with_default(prompt, default, is_integer=False):
"""
ユーザーからの入力を取得し、入力がない場合はデフォルト値を返します。
Args:
prompt (str): ユーザーに表示するプロンプトメッセージ
default (str or int): 入力がない場合に使用するデフォルト値
is_integer (bool): 入力が整数であるかどうかを指定
Returns:
str or int: ユーザーの入力、またはデフォルト値
"""
user_input = input(prompt).strip()
if not user_input:
print(f"入力がありませんでした。デフォルト値 '{default}' を使用します。")
return default
if is_integer:
if user_input.isdigit():
return int(user_input)
else:
print(f"無効な入力 '{user_input}' がありました。デフォルト値 '{default}' を使用します。")
return default
return user_input
if __name__ == "__main__":
# デフォルト値の設定
DEFAULT_URL = "http://localhost/"
DEFAULT_TIMEOUT = 120
# ユーザーからURLを入力(デフォルト値を設定)
target_url = get_input_with_default(
f"ApacheサーバーのURLを入力してください (デフォルト: {DEFAULT_URL}): ",
DEFAULT_URL,
is_integer=False
)
# ユーザーからアイドルタイムアウト秒数を入力(デフォルト値を設定)
idle_timeout_seconds = get_input_with_default(
f"アイドルタイムアウトをテストする秒数を入力してください (デフォルト: {DEFAULT_TIMEOUT}): ",
DEFAULT_TIMEOUT,
is_integer=True
)
# テストの開始時間を記録
start_time = datetime.now()
print(f"テスト開始時刻: {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
# アイドルタイムアウト検証を実行
success = check_idle_timeout(target_url, idle_timeout_seconds)
# テストの終了時間を記録
end_time = datetime.now()
print(f"テスト終了時刻: {end_time.strftime('%Y-%m-%d %H:%M:%S')}")
# テスト結果を表示
if success:
print("テスト成功: 接続はアイドル状態後も維持されました。")
else:
print("テスト失敗: 接続が維持されませんでした。")