0
1

Apache アイドルタイムアウト 検証 (python)

Last updated at Posted at 2024-09-17

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("テスト失敗: 接続が維持されませんでした。")

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1