Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/websocket/_core.py: 19%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

302 statements  

1import socket 

2import struct 

3import threading 

4import time 

5from typing import Any, Callable, Optional, Type, Union 

6from urllib.parse import urlparse 

7 

8# websocket modules 

9from ._abnf import ABNF, STATUS_NORMAL, continuous_frame, frame_buffer 

10from ._exceptions import ( 

11 WebSocketProtocolException, 

12 WebSocketConnectionClosedException, 

13 WebSocketTimeoutException, 

14 WebSocketException, 

15) 

16from ._handshake import SUPPORTED_REDIRECT_STATUSES, handshake, handshake_response 

17from ._http import connect, proxy_info 

18from ._logging import debug, error, trace, isEnabledForError, isEnabledForTrace 

19from ._socket import _validate_timeout, getdefaulttimeout, recv, send, sock_opt 

20from ._ssl_compat import ssl 

21from ._utils import NoLock 

22from ._dispatcher import DispatcherBase, WrappedDispatcher 

23 

24""" 

25_core.py 

26websocket - WebSocket client library for Python 

27 

28Copyright 2026 engn33r 

29 

30Licensed under the Apache License, Version 2.0 (the "License"); 

31you may not use this file except in compliance with the License. 

32You may obtain a copy of the License at 

33 

34 http://www.apache.org/licenses/LICENSE-2.0 

35 

36Unless required by applicable law or agreed to in writing, software 

37distributed under the License is distributed on an "AS IS" BASIS, 

38WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

39See the License for the specific language governing permissions and 

40limitations under the License. 

41""" 

42 

43__all__ = ["WebSocket", "create_connection"] 

44 

45 

46def _normalize_close_reason(reason: Union[str, bytes, None]) -> bytes: 

47 """Convert a close reason into the UTF-8 bytes for a close-frame payload.""" 

48 if reason is None: 

49 return b"" 

50 if isinstance(reason, str): 

51 return reason.encode("utf-8") 

52 if isinstance(reason, bytes): 

53 return reason 

54 return bytes(reason) 

55 

56 

57def _handshake_authority(url: str, addrs: tuple) -> tuple: 

58 """(scheme, lowercase host, effective port) identifying a handshake target.""" 

59 scheme = url.split(":", 1)[0].lower() 

60 hostname, port, _ = addrs 

61 return (scheme, hostname.lower() if hostname else None, port) 

62 

63 

64def _url_origin(url: str) -> str: 

65 """scheme://host[:port] of url, for error messages. The userinfo, path and 

66 query can carry credentials or tokens and must not end up in logs.""" 

67 parsed = urlparse(url) 

68 host = parsed.hostname or "" 

69 if ":" in host: 

70 host = f"[{host}]" 

71 try: 

72 port = parsed.port 

73 except ValueError: # a malformed port in a server-supplied Location 

74 port = None 

75 return f"{parsed.scheme}://{host}" + (f":{port}" if port is not None else "") 

76 

77 

78# Headers tied to the authority the caller targeted. They must not follow a 

79# redirect to a different scheme/host/port. 

80_AUTHORITY_SCOPED_HEADERS = ("authorization", "proxy-authorization", "cookie", "host") 

81 

82 

83def _without_credentials(options: dict) -> dict: 

84 """Copy of options without the Authorization, Proxy-Authorization, Cookie 

85 and Host headers and the cookie and host options, for a redirect whose 

86 target authority differs from the previous one.""" 

87 options = dict(options) 

88 header = options.get("header") 

89 if header: 

90 if isinstance(header, dict): 

91 header = { 

92 k: v 

93 for k, v in header.items() 

94 if k.strip().lower() not in _AUTHORITY_SCOPED_HEADERS 

95 } 

96 else: 

97 header = [ 

98 h 

99 for h in header 

100 if h.split(":", 1)[0].strip().lower() not in _AUTHORITY_SCOPED_HEADERS 

101 ] 

102 options["header"] = header 

103 options.pop("cookie", None) 

104 options.pop("host", None) 

105 return options 

106 

107 

108class WebSocket: 

109 """ 

110 Low level WebSocket interface. 

111 

112 This class is based on the WebSocket protocol `draft-hixie-thewebsocketprotocol-76 <http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76>`_ 

113 

114 We can connect to the websocket server and send/receive data. 

115 The following example is an echo client. 

116 

117 >>> import websocket 

118 >>> ws = websocket.WebSocket() 

119 >>> ws.connect("ws://websockets.chilkat.io/wsChilkatEcho.ashx") 

120 >>> ws.send("Hello, Server") 

121 19 

122 >>> ws.recv() 

123 'Hello, Server' 

124 >>> ws.close() 

125 

126 Parameters 

127 ---------- 

128 get_mask_key: func 

129 A callable function to get new mask keys, see the 

130 WebSocket.set_mask_key's docstring for more information. 

131 sockopt: tuple 

132 Values for socket.setsockopt. 

133 sockopt must be tuple and each element is argument of sock.setsockopt. 

134 sslopt: dict 

135 Optional dict object for ssl socket options. See FAQ for details. 

136 fire_cont_frame: bool 

137 Fire recv event for each cont frame. Default is False. 

138 enable_multithread: bool 

139 If set to True, lock send method. 

140 skip_utf8_validation: bool 

141 Skip utf8 validation. 

142 """ 

143 

144 def __init__( 

145 self, 

146 get_mask_key: Optional[Callable] = None, 

147 sockopt: Optional[list] = None, 

148 sslopt: Optional[dict] = None, 

149 fire_cont_frame: bool = False, 

150 enable_multithread: bool = True, 

151 skip_utf8_validation: bool = False, 

152 dispatcher: Optional[Union[DispatcherBase, WrappedDispatcher]] = None, 

153 **_: Any, 

154 ) -> None: 

155 """ 

156 Initialize WebSocket object. 

157 

158 Parameters 

159 ---------- 

160 sslopt: dict 

161 Optional dict object for ssl socket options. See FAQ for details. 

162 """ 

163 self.sock_opt = sock_opt(sockopt, sslopt) 

164 self.handshake_response: Optional[handshake_response] = None 

165 self.sock: Optional[socket.socket] = None 

166 

167 self.connected = False 

168 self.close_frame: Optional[ABNF] = None 

169 # True while close() waits for the peer's close frame, so a concurrent 

170 # close() does not tear the socket down underneath it. 

171 self._closing = False 

172 self.get_mask_key = get_mask_key 

173 # These buffer over the build-up of a single frame. 

174 self.frame_buffer = frame_buffer(self._recv, skip_utf8_validation) 

175 self.cont_frame = continuous_frame(fire_cont_frame, skip_utf8_validation) 

176 self.dispatcher = dispatcher 

177 

178 if enable_multithread: 

179 self.lock = threading.Lock() 

180 self.readlock = threading.Lock() 

181 else: 

182 self.lock = NoLock() # type: ignore[assignment] 

183 self.readlock = NoLock() # type: ignore[assignment] 

184 

185 def __iter__(self): 

186 """ 

187 Allow iteration over websocket, implying sequential `recv` executions. 

188 """ 

189 while True: 

190 yield self.recv() 

191 

192 def __next__(self): 

193 return self.recv() 

194 

195 def next(self): 

196 return self.__next__() 

197 

198 def fileno(self): 

199 if self.sock is None: 

200 raise WebSocketException("Connection not established") 

201 return self.sock.fileno() 

202 

203 def set_mask_key(self, func): 

204 """ 

205 Set function to create mask key. You can customize mask key generator. 

206 Mainly, this is for testing purpose. 

207 

208 Parameters 

209 ---------- 

210 func: func 

211 callable object. the func takes 1 argument as integer. 

212 The argument means length of mask key. 

213 This func must return string(byte array), 

214 which length is argument specified. 

215 """ 

216 self.get_mask_key = func 

217 

218 def gettimeout(self) -> Optional[Union[float, int]]: 

219 """ 

220 Get the websocket timeout (in seconds) as an int or float 

221 

222 Returns 

223 ---------- 

224 timeout: int or float 

225 returns timeout value (in seconds). This value could be either float/integer. 

226 """ 

227 return self.sock_opt.timeout 

228 

229 def settimeout(self, timeout: Optional[Union[float, int]]) -> None: 

230 """ 

231 Set the timeout to the websocket. 

232 

233 Parameters 

234 ---------- 

235 timeout: int or float 

236 timeout time (in seconds). This value could be either float/integer. 

237 None disables the timeout (blocking mode). 

238 """ 

239 _validate_timeout(timeout) 

240 self.sock_opt.timeout = timeout 

241 if self.sock: 

242 self.sock.settimeout(timeout) 

243 

244 timeout = property(gettimeout, settimeout) 

245 

246 def getsubprotocol(self) -> Optional[str]: 

247 """ 

248 Get subprotocol 

249 """ 

250 if self.handshake_response: 

251 return self.handshake_response.subprotocol 

252 else: 

253 return None 

254 

255 subprotocol = property(getsubprotocol) 

256 

257 def getstatus(self) -> Optional[int]: 

258 """ 

259 Get handshake status 

260 """ 

261 if self.handshake_response: 

262 return self.handshake_response.status 

263 else: 

264 return None 

265 

266 status = property(getstatus) 

267 

268 def getheaders(self) -> Optional[dict]: 

269 """ 

270 Get handshake response header 

271 """ 

272 if self.handshake_response: 

273 return self.handshake_response.headers 

274 else: 

275 return None 

276 

277 def is_ssl(self): 

278 try: 

279 return isinstance(self.sock, ssl.SSLSocket) 

280 except (AttributeError, NameError): 

281 return False 

282 

283 headers = property(getheaders) 

284 

285 def connect(self, url, **options): 

286 """ 

287 Connect to url. url is websocket url scheme. 

288 ie. ws://host:port/resource 

289 You can customize using 'options'. 

290 If you set "header" list object, you can set your own custom header. 

291 

292 >>> ws = WebSocket() 

293 >>> ws.connect("ws://websockets.chilkat.io/wsChilkatEcho.ashx", 

294 ... header=["User-Agent: MyProgram", 

295 ... "x-custom: header"]) 

296 

297 Parameters 

298 ---------- 

299 header: list or dict 

300 Custom http header list or dict. 

301 cookie: str 

302 Cookie value. 

303 origin: str 

304 Custom origin url. 

305 connection: str 

306 Custom connection header value. 

307 Default value "Upgrade" set in _handshake.py 

308 suppress_origin: bool 

309 Suppress outputting origin header. 

310 suppress_host: bool 

311 Suppress outputting host header. 

312 host: str 

313 Custom host header string. 

314 timeout: int or float 

315 Socket timeout time. This value is an integer or float. 

316 If you set None for this value, it means "use default_timeout value" 

317 http_proxy_host: str 

318 HTTP proxy host name. 

319 http_proxy_port: str or int 

320 HTTP proxy port. Required when http_proxy_host is set. Proxies 

321 from environment variables default to port 80. 

322 http_no_proxy: list 

323 Whitelisted host names that don't use the proxy. 

324 http_proxy_auth: tuple 

325 HTTP proxy auth information. Tuple of username and password. Default is None. 

326 http_proxy_timeout: int or float 

327 HTTP proxy timeout, default is 60 sec as per python-socks. 

328 redirect_limit: int 

329 Number of redirects to follow. A redirect from wss:// to ws:// 

330 is refused with WebSocketException. 

331 subprotocols: list 

332 List of available subprotocols. Default is None. 

333 socket: socket 

334 Pre-initialized stream socket. With a wss:// URL, a socket that is 

335 not already an ssl.SSLSocket is wrapped in TLS using sslopt. 

336 """ 

337 if "timeout" in options: 

338 _validate_timeout(options["timeout"]) 

339 self.sock_opt.timeout = options.get("timeout", self.sock_opt.timeout) 

340 self.sock, addrs = connect( 

341 url, self.sock_opt, proxy_info(**options), options.pop("socket", None) 

342 ) 

343 

344 try: 

345 self.handshake_response = handshake(self.sock, url, *addrs, **options) 

346 authority = _handshake_authority(url, addrs) 

347 for _ in range(options.pop("redirect_limit", 3)): 

348 if ( 

349 self.handshake_response is not None 

350 and self.handshake_response.status in SUPPORTED_REDIRECT_STATUSES 

351 ): 

352 location = self.handshake_response.headers.get("location") 

353 if location is None: 

354 raise WebSocketException( 

355 "Redirect response without Location header, " 

356 f"status {self.handshake_response.status}" 

357 ) 

358 if ( 

359 urlparse(url).scheme.lower() == "wss" 

360 and urlparse(location).scheme.lower() == "ws" 

361 ): 

362 raise WebSocketException( 

363 f"Refusing redirect from {_url_origin(url)} to " 

364 f"{_url_origin(location)}: " 

365 "it would downgrade wss:// to unencrypted ws://" 

366 ) 

367 url = location 

368 self.sock.close() 

369 try: 

370 self.sock, addrs = connect( 

371 url, 

372 self.sock_opt, 

373 proxy_info(**options), 

374 options.pop("socket", None), 

375 ) 

376 except ValueError as e: 

377 raise WebSocketException( 

378 f"Invalid redirect target {url!r}: {e}" 

379 ) from e 

380 new_authority = _handshake_authority(url, addrs) 

381 if new_authority != authority: 

382 # Credentials supplied for the original authority must 

383 # not be replayed to a different scheme/host/port. 

384 options = _without_credentials(options) 

385 authority = new_authority 

386 self.handshake_response = handshake( 

387 self.sock, url, *addrs, **options 

388 ) 

389 if ( 

390 self.handshake_response is not None 

391 and self.handshake_response.status in SUPPORTED_REDIRECT_STATUSES 

392 ): 

393 raise WebSocketException("Redirect limit exhausted") 

394 self.connected = True 

395 except: 

396 if self.sock: 

397 self.sock.close() 

398 self.sock = None 

399 raise 

400 

401 def send(self, payload: Union[bytes, str], opcode: int = ABNF.OPCODE_TEXT) -> int: 

402 """ 

403 Send the data as string. 

404 

405 Parameters 

406 ---------- 

407 payload: str 

408 Payload must be utf-8 string or unicode, 

409 If the opcode is OPCODE_TEXT. 

410 Otherwise, it must be string(byte array). 

411 opcode: int 

412 Operation code (opcode) to send. 

413 """ 

414 

415 frame = ABNF.create_frame(payload, opcode) 

416 return self.send_frame(frame) 

417 

418 def send_text(self, text_data: str) -> int: 

419 """ 

420 Sends UTF-8 encoded text. 

421 """ 

422 return self.send(text_data, ABNF.OPCODE_TEXT) 

423 

424 def send_bytes(self, data: Union[bytes, bytearray]) -> int: 

425 """ 

426 Sends a sequence of bytes. 

427 """ 

428 return self.send(data, ABNF.OPCODE_BINARY) 

429 

430 def send_frame(self, frame: ABNF) -> int: 

431 """ 

432 Send the data frame. 

433 

434 >>> ws = create_connection("ws://websockets.chilkat.io/wsChilkatEcho.ashx") 

435 >>> frame = ABNF.create_frame("Hello", ABNF.OPCODE_TEXT) 

436 >>> ws.send_frame(frame) 

437 >>> cont_frame = ABNF.create_frame("My name is ", ABNF.OPCODE_CONT, 0) 

438 >>> ws.send_frame(frame) 

439 >>> cont_frame = ABNF.create_frame("Foo Bar", ABNF.OPCODE_CONT, 1) 

440 >>> ws.send_frame(frame) 

441 

442 Parameters 

443 ---------- 

444 frame: ABNF frame 

445 frame data created by ABNF.create_frame 

446 """ 

447 if self.get_mask_key: 

448 frame.get_mask_key = self.get_mask_key 

449 data = frame.format() 

450 length = len(data) 

451 if isEnabledForTrace(): 

452 trace(f"++Sent raw: {repr(data)}") 

453 trace(f"++Sent decoded: {frame.__str__()}") 

454 with self.lock: 

455 while data: 

456 bytes_sent = self._send(data) 

457 data = data[bytes_sent:] 

458 

459 return length 

460 

461 def send_binary(self, payload: bytes) -> int: 

462 """ 

463 Send a binary message (OPCODE_BINARY). 

464 

465 Parameters 

466 ---------- 

467 payload: bytes 

468 payload of message to send. 

469 """ 

470 return self.send(payload, ABNF.OPCODE_BINARY) 

471 

472 def ping(self, payload: Union[str, bytes] = "") -> None: 

473 """ 

474 Send ping data. 

475 

476 Parameters 

477 ---------- 

478 payload: str 

479 data payload to send server. 

480 """ 

481 if isinstance(payload, str): 

482 payload = payload.encode("utf-8") 

483 self.send(payload, ABNF.OPCODE_PING) 

484 

485 def pong(self, payload: Union[str, bytes] = "") -> None: 

486 """ 

487 Send pong data. 

488 

489 Parameters 

490 ---------- 

491 payload: str 

492 data payload to send server. 

493 """ 

494 if isinstance(payload, str): 

495 payload = payload.encode("utf-8") 

496 self.send(payload, ABNF.OPCODE_PONG) 

497 

498 def recv(self) -> Union[str, bytes]: 

499 """ 

500 Receive string data(byte array) from the server. 

501 

502 Returns 

503 ---------- 

504 data: string (byte array) value. 

505 """ 

506 with self.readlock: 

507 opcode, data = self.recv_data() 

508 if opcode == ABNF.OPCODE_TEXT: 

509 data_received: Union[bytes, str] = data 

510 if isinstance(data_received, bytes): 

511 return data_received.decode("utf-8") 

512 elif isinstance(data_received, str): 

513 return data_received 

514 elif opcode == ABNF.OPCODE_BINARY: 

515 data_binary: bytes = data 

516 return data_binary 

517 else: 

518 return "" 

519 

520 def recv_data(self, control_frame: bool = False) -> tuple: 

521 """ 

522 Receive data with operation code. 

523 

524 Parameters 

525 ---------- 

526 control_frame: bool 

527 a boolean flag indicating whether to return control frame 

528 data, defaults to False 

529 

530 Returns 

531 ------- 

532 opcode, frame.data: tuple 

533 tuple of operation code and string(byte array) value. 

534 """ 

535 opcode, frame = self.recv_data_frame(control_frame) 

536 return opcode, frame.data 

537 

538 def recv_data_frame(self, control_frame: bool = False) -> tuple: 

539 """ 

540 Receive data with operation code. 

541 

542 If a valid ping message is received, a pong response is sent. 

543 

544 Parameters 

545 ---------- 

546 control_frame: bool 

547 a boolean flag indicating whether to return control frame 

548 data, defaults to False 

549 

550 Returns 

551 ------- 

552 frame.opcode, frame: tuple 

553 tuple of operation code and string(byte array) value. 

554 """ 

555 while True: 

556 frame = self.recv_frame() 

557 if isEnabledForTrace(): 

558 trace(f"++Rcv raw: {repr(frame.format())}") 

559 trace(f"++Rcv decoded: {frame.__str__()}") 

560 if frame.opcode in ( 

561 ABNF.OPCODE_TEXT, 

562 ABNF.OPCODE_BINARY, 

563 ABNF.OPCODE_CONT, 

564 ): 

565 self.cont_frame.validate(frame) 

566 self.cont_frame.add(frame) 

567 

568 if self.cont_frame.is_fire(frame): 

569 return self.cont_frame.extract(frame) 

570 

571 elif frame.opcode == ABNF.OPCODE_CLOSE: 

572 self.send_close() 

573 return frame.opcode, frame 

574 elif frame.opcode == ABNF.OPCODE_PING: 

575 if len(frame.data) < 126: 

576 self.pong(frame.data) 

577 else: 

578 raise WebSocketProtocolException("Ping message is too long") 

579 if control_frame: 

580 return frame.opcode, frame 

581 elif frame.opcode == ABNF.OPCODE_PONG: 

582 if control_frame: 

583 return frame.opcode, frame 

584 

585 def recv_frame(self): 

586 """ 

587 Receive data as frame from server. 

588 

589 Returns 

590 ------- 

591 self.frame_buffer.recv_frame(): ABNF frame object 

592 """ 

593 return self.frame_buffer.recv_frame() 

594 

595 def send_close( 

596 self, status: int = STATUS_NORMAL, reason: Union[str, bytes] = b"" 

597 ) -> None: 

598 """ 

599 Send close data to the server. 

600 

601 Parameters 

602 ---------- 

603 status: int 

604 Status code to send. See STATUS_XXX. 

605 reason: str or bytes 

606 The reason to close. This must be string or UTF-8 bytes. 

607 """ 

608 if status < 0 or status >= ABNF.LENGTH_16: 

609 raise ValueError("code is invalid range") 

610 

611 reason_bytes = _normalize_close_reason(reason) 

612 

613 self.connected = False 

614 self.send(struct.pack("!H", status) + reason_bytes, ABNF.OPCODE_CLOSE) 

615 

616 def close( 

617 self, 

618 status: int = STATUS_NORMAL, 

619 reason: Union[str, bytes] = b"", 

620 timeout: Optional[Union[int, float]] = 3, 

621 ) -> None: 

622 """ 

623 Close Websocket object 

624 

625 Parameters 

626 ---------- 

627 status: int 

628 Status code to send. See VALID_CLOSE_STATUS in ABNF. 

629 reason: str or bytes 

630 The reason to close in UTF-8. 

631 timeout: int or float 

632 Timeout until receive a close frame. 

633 If None, it will wait forever until receive a close frame. 

634 """ 

635 if not self.connected: 

636 # The peer already disconnected: free the socket (#1066). Leave it 

637 # alone if another thread is mid-close; that thread shuts it down. 

638 if not self._closing: 

639 self.shutdown() 

640 return 

641 if status < 0 or status >= ABNF.LENGTH_16: 

642 raise ValueError("code is invalid range") 

643 

644 # Reset close_frame to avoid stale data from previous connections 

645 self.close_frame = None 

646 

647 self._closing = True 

648 try: 

649 self.connected = False 

650 self.send( 

651 struct.pack("!H", status) + _normalize_close_reason(reason), 

652 ABNF.OPCODE_CLOSE, 

653 ) 

654 if self.sock is None: 

655 return 

656 sock_timeout = self.sock.gettimeout() 

657 self.sock.settimeout(timeout) 

658 start_time = time.time() 

659 while timeout is None or time.time() - start_time < timeout: 

660 try: 

661 frame = self.recv_frame() 

662 if frame.opcode != ABNF.OPCODE_CLOSE: 

663 continue 

664 # Store the peer's close frame for access by higher-level APIs 

665 self.close_frame = frame 

666 if isEnabledForError(): 

667 recv_status = struct.unpack("!H", frame.data[0:2])[0] 

668 if recv_status >= 3000 and recv_status <= 4999: 

669 debug(f"close status: {repr(recv_status)}") 

670 elif recv_status != STATUS_NORMAL: 

671 error(f"close status: {repr(recv_status)}") 

672 break 

673 except ( 

674 WebSocketConnectionClosedException, 

675 WebSocketTimeoutException, 

676 struct.error, 

677 ): 

678 break 

679 if self.sock is not None: 

680 self.sock.settimeout(sock_timeout) 

681 self.sock.shutdown(socket.SHUT_RDWR) 

682 except: 

683 pass 

684 finally: 

685 self._closing = False 

686 

687 self.shutdown() 

688 

689 def abort(self): 

690 """ 

691 Low-level asynchronous abort, wakes up other threads that are waiting in recv_* 

692 """ 

693 if self.connected and self.sock is not None: 

694 try: 

695 self.sock.shutdown(socket.SHUT_RDWR) 

696 except (OSError, AttributeError): 

697 # Socket already closed or never connected 

698 # abort() is best-effort, like shutdown() 

699 debug("Socket already closed during abort") 

700 

701 def shutdown(self): 

702 """ 

703 close socket, immediately. 

704 """ 

705 if self.sock: 

706 try: 

707 # Check if socket is still open before closing 

708 if not self.sock._closed: 

709 self.sock.close() 

710 except (OSError, AttributeError): 

711 # Socket already closed or invalid file descriptor - this can happen 

712 # during reconnection scenarios when network failures occur 

713 debug("Socket already closed during shutdown") 

714 pass 

715 finally: 

716 self.sock = None 

717 self.connected = False 

718 

719 def _send(self, data: Union[str, bytes]) -> int: 

720 if self.sock is None: 

721 raise WebSocketConnectionClosedException("socket is already closed.") 

722 if self.dispatcher: 

723 return self.dispatcher.send(self.sock, data) 

724 return send(self.sock, data) 

725 

726 def _recv(self, bufsize): 

727 if self.sock is None: 

728 raise WebSocketConnectionClosedException("Connection is closed") 

729 try: 

730 return recv(self.sock, bufsize) 

731 except WebSocketConnectionClosedException: 

732 if self.sock: 

733 self.sock.close() 

734 self.sock = None 

735 self.connected = False 

736 raise 

737 

738 

739def create_connection( 

740 url: str, 

741 timeout: Optional[Union[float, int]] = None, 

742 class_: Type[WebSocket] = WebSocket, 

743 **options: Any, 

744) -> WebSocket: 

745 """ 

746 Connect to url and return websocket object. 

747 

748 Connect to url and return the WebSocket object. 

749 Passing optional timeout parameter will set the timeout on the socket. 

750 If no timeout is supplied, 

751 the global default timeout setting returned by getdefaulttimeout() is used. 

752 You can customize using 'options'. 

753 If you set "header" list object, you can set your own custom header. 

754 

755 >>> conn = create_connection("ws://websockets.chilkat.io/wsChilkatEcho.ashx", 

756 ... header=["User-Agent: MyProgram", 

757 ... "x-custom: header"]) 

758 

759 Parameters 

760 ---------- 

761 class_: class 

762 class to instantiate when creating the connection. It has to implement 

763 settimeout and connect. It's __init__ should be compatible with 

764 WebSocket.__init__, i.e. accept all of it's kwargs. 

765 header: list or dict 

766 custom http header list or dict. 

767 cookie: str 

768 Cookie value. 

769 origin: str 

770 custom origin url. 

771 suppress_origin: bool 

772 suppress outputting origin header. 

773 host: str 

774 custom host header string. 

775 timeout: int or float 

776 socket timeout time. This value could be either float/integer. 

777 If set to None, it uses the default_timeout value. 

778 http_proxy_host: str 

779 HTTP proxy host name. 

780 http_proxy_port: str or int 

781 HTTP proxy port. Required when http_proxy_host is set. Proxies 

782 from environment variables default to port 80. 

783 http_no_proxy: list 

784 Whitelisted host names that don't use the proxy. 

785 http_proxy_auth: tuple 

786 HTTP proxy auth information. tuple of username and password. Default is None. 

787 http_proxy_timeout: int or float 

788 HTTP proxy timeout, default is 60 sec as per python-socks. 

789 enable_multithread: bool 

790 Enable lock for multithread. 

791 redirect_limit: int 

792 Number of redirects to follow. A redirect from wss:// to ws:// 

793 is refused with WebSocketException. 

794 sockopt: tuple 

795 Values for socket.setsockopt. 

796 sockopt must be a tuple and each element is an argument of sock.setsockopt. 

797 sslopt: dict 

798 Optional dict object for ssl socket options. See FAQ for details. 

799 subprotocols: list 

800 List of available subprotocols. Default is None. 

801 skip_utf8_validation: bool 

802 Skip utf8 validation. 

803 socket: socket 

804 Pre-initialized stream socket. With a wss:// URL, a socket that is 

805 not already an ssl.SSLSocket is wrapped in TLS using sslopt. 

806 """ 

807 sockopt = options.pop("sockopt", []) 

808 sslopt = options.pop("sslopt", {}) 

809 fire_cont_frame = options.pop("fire_cont_frame", False) 

810 enable_multithread = options.pop("enable_multithread", True) 

811 skip_utf8_validation = options.pop("skip_utf8_validation", False) 

812 websock = class_( 

813 sockopt=sockopt, 

814 sslopt=sslopt, 

815 fire_cont_frame=fire_cont_frame, 

816 enable_multithread=enable_multithread, 

817 skip_utf8_validation=skip_utf8_validation, 

818 **options, 

819 ) 

820 websock.settimeout(timeout if timeout is not None else getdefaulttimeout()) 

821 websock.connect(url, **options) 

822 return websock