Path: blob/main/external/curl/tests/http/test_20_websockets.py
2066 views
#!/usr/bin/env python31# -*- coding: utf-8 -*-2#***************************************************************************3# _ _ ____ _4# Project ___| | | | _ \| |5# / __| | | | |_) | |6# | (__| |_| | _ <| |___7# \___|\___/|_| \_\_____|8#9# Copyright (C) Daniel Stenberg, <[email protected]>, et al.10#11# This software is licensed as described in the file COPYING, which12# you should have received as part of this distribution. The terms13# are also available at https://curl.se/docs/copyright.html.14#15# You may opt to use, copy, modify, merge, publish, distribute and/or sell16# copies of the Software, and permit persons to whom the Software is17# furnished to do so, under the terms of the COPYING file.18#19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY20# KIND, either express or implied.21#22# SPDX-License-Identifier: curl23#24###########################################################################25#26import logging27import os28import shutil29import socket30import subprocess31import time32from datetime import datetime, timedelta33from typing import Dict34import pytest3536from testenv import Env, CurlClient, LocalClient37from testenv.ports import alloc_ports_and_do383940log = logging.getLogger(__name__)414243@pytest.mark.skipif(condition=not Env.curl_has_protocol('ws'),44reason='curl lacks ws protocol support')45class TestWebsockets:4647PORT_SPECS = {48'ws': socket.SOCK_STREAM,49}5051def check_alive(self, env, port, timeout=Env.SERVER_TIMEOUT):52curl = CurlClient(env=env)53url = f'http://localhost:{port}/'54end = datetime.now() + timedelta(seconds=timeout)55while datetime.now() < end:56r = curl.http_download(urls=[url])57if r.exit_code == 0:58return True59time.sleep(.1)60return False6162def _mkpath(self, path):63if not os.path.exists(path):64return os.makedirs(path)6566def _rmrf(self, path):67if os.path.exists(path):68return shutil.rmtree(path)6970@pytest.fixture(autouse=True, scope='class')71def ws_echo(self, env):72self.run_dir = os.path.join(env.gen_dir, 'ws-echo-server')73err_file = os.path.join(self.run_dir, 'stderr')74self._rmrf(self.run_dir)75self._mkpath(self.run_dir)76self.cmd = os.path.join(env.project_dir,77'tests/http/testenv/ws_echo_server.py')78self.wsproc = None79self.cerr = None8081def startup(ports: Dict[str, int]) -> bool:82wargs = [self.cmd, '--port', str(ports['ws'])]83log.info(f'start_ {wargs}')84self.wsproc = subprocess.Popen(args=wargs,85cwd=self.run_dir,86stderr=self.cerr,87stdout=self.cerr)88if self.check_alive(env, ports['ws']):89env.update_ports(ports)90return True91log.error(f'not alive {wargs}')92self.wsproc.terminate()93self.wsproc = None94return False9596with open(err_file, 'w') as self.cerr:97assert alloc_ports_and_do(TestWebsockets.PORT_SPECS, startup,98env.gen_root, max_tries=3)99assert self.wsproc100yield101self.wsproc.terminate()102103def test_20_01_basic(self, env: Env, ws_echo):104curl = CurlClient(env=env)105url = f'http://localhost:{env.ws_port}/'106r = curl.http_download(urls=[url])107r.check_response(http_status=426)108109def test_20_02_pingpong_small(self, env: Env, ws_echo):110payload = 125 * "x"111client = LocalClient(env=env, name='ws-pingpong')112if not client.exists():113pytest.skip(f'example client not built: {client.name}')114url = f'ws://localhost:{env.ws_port}/'115r = client.run(args=[url, payload])116r.check_exit_code(0)117118# the python websocket server does not like 'large' control frames119def test_20_03_pingpong_too_large(self, env: Env, ws_echo):120payload = 127 * "x"121client = LocalClient(env=env, name='ws-pingpong')122if not client.exists():123pytest.skip(f'example client not built: {client.name}')124url = f'ws://localhost:{env.ws_port}/'125r = client.run(args=[url, payload])126r.check_exit_code(100) # CURLE_TOO_LARGE127128def test_20_04_data_small(self, env: Env, ws_echo):129client = LocalClient(env=env, name='ws-data')130if not client.exists():131pytest.skip(f'example client not built: {client.name}')132url = f'ws://localhost:{env.ws_port}/'133r = client.run(args=['-m', str(0), '-M', str(10), url])134r.check_exit_code(0)135136def test_20_05_data_med(self, env: Env, ws_echo):137client = LocalClient(env=env, name='ws-data')138if not client.exists():139pytest.skip(f'example client not built: {client.name}')140url = f'ws://localhost:{env.ws_port}/'141r = client.run(args=['-m', str(120), '-M', str(130), url])142r.check_exit_code(0)143144def test_20_06_data_large(self, env: Env, ws_echo):145client = LocalClient(env=env, name='ws-data')146if not client.exists():147pytest.skip(f'example client not built: {client.name}')148url = f'ws://localhost:{env.ws_port}/'149r = client.run(args=['-m', str(65535 - 5), '-M', str(65535 + 5), url])150r.check_exit_code(0)151152def test_20_07_data_large_small_recv(self, env: Env, ws_echo):153run_env = os.environ.copy()154run_env['CURL_WS_CHUNK_SIZE'] = '1024'155client = LocalClient(env=env, name='ws-data', run_env=run_env)156if not client.exists():157pytest.skip(f'example client not built: {client.name}')158url = f'ws://localhost:{env.ws_port}/'159r = client.run(args=['-m', str(65535 - 5), '-M', str(65535 + 5), url])160r.check_exit_code(0)161162# Send large frames and simulate send blocking on 8192 bytes chunks163# Simlates error reported in #15865164def test_20_08_data_very_large(self, env: Env, ws_echo):165run_env = os.environ.copy()166run_env['CURL_WS_CHUNK_EAGAIN'] = '8192'167client = LocalClient(env=env, name='ws-data', run_env=run_env)168if not client.exists():169pytest.skip(f'example client not built: {client.name}')170url = f'ws://localhost:{env.ws_port}/'171count = 10172large = 20000173r = client.run(args=['-c', str(count), '-m', str(large), url])174r.check_exit_code(0)175176177