@@ -182,7 +182,7 @@ def test_server_sends_do_and_will_charset():
182182def test_client_do_will_then_server_will_allows_client_request ():
183183 """Test scenario from logfile: DO->WILL then server WILL allows client to send SB REQUEST."""
184184 wc , tc , _ = new_writer (server = False , client = True )
185- wc .set_ext_send_callback (CHARSET , lambda : ["UTF-8" ])
185+ wc .set_ext_offer_callback (CHARSET , lambda : ["UTF-8" ])
186186
187187 # Simulate server DO CHARSET
188188 # Note: handle_do() returns True but local_option[...] is set by the caller
@@ -208,11 +208,11 @@ def test_bidirectional_charset_both_sides_can_request():
208208 """Test that both server and client can initiate CHARSET REQUEST when both have WILL/DO."""
209209 # Server side
210210 ws , ts , _ = new_writer (server = True )
211- ws .set_ext_send_callback (CHARSET , lambda : ["UTF-8" , "ASCII" ])
211+ ws .set_ext_offer_callback (CHARSET , lambda : ["UTF-8" , "ASCII" ])
212212
213213 # Client side
214214 wc , tc , _ = new_writer (server = False , client = True )
215- wc .set_ext_send_callback (CHARSET , lambda : ["UTF-8" ])
215+ wc .set_ext_offer_callback (CHARSET , lambda : ["UTF-8" ])
216216
217217 # Simulate full negotiation: server DO, client WILL, server WILL, client DO
218218 ws .remote_option [CHARSET ] = True # client sent WILL
@@ -234,7 +234,7 @@ def test_charset_request_response_cycle():
234234 # Server initiates REQUEST
235235 ws , ts , _ = new_writer (server = True )
236236 ws .remote_option [CHARSET ] = True
237- ws .set_ext_send_callback (CHARSET , lambda : ["UTF-8" , "ASCII" ])
237+ ws .set_ext_offer_callback (CHARSET , lambda : ["UTF-8" , "ASCII" ])
238238
239239 assert ws .request_charset () is True
240240 request_frame = ts .writes [- 1 ]
@@ -265,7 +265,7 @@ def test_server_sends_will_charset_after_client_will():
265265 # Verify server also called request_charset as usual
266266 # (this is tested by checking if it would send a request,
267267 # but we need to set up the callback first)
268- ws .set_ext_send_callback (CHARSET , lambda : ["UTF-8" ])
268+ ws .set_ext_offer_callback (CHARSET , lambda : ["UTF-8" ])
269269 # Clear previous writes to test just the request
270270 ts .writes .clear ()
271271
@@ -401,3 +401,67 @@ def test_charset_accepted_sets_force_binary_on_accepting_side():
401401
402402 assert w .environ_encoding == "UTF-8"
403403 assert p .force_binary is True
404+
405+
406+ def test_client_request_charset_uses_offer_callback ():
407+ """Client request_charset() must use offer callback, not send callback."""
408+ wc , tc , _ = new_writer (server = False , client = True )
409+ wc .local_option [CHARSET ] = True
410+ wc .remote_option [CHARSET ] = True
411+
412+ wc .set_ext_offer_callback (CHARSET , lambda : ["UTF-8" , "CP437" ])
413+ wc .set_ext_send_callback (CHARSET , lambda offered : offered [0 ])
414+
415+ assert wc .request_charset () is True
416+ frame = tc .writes [- 1 ]
417+ assert frame .startswith (IAC + SB + CHARSET + REQUEST )
418+ assert b"UTF-8" in frame
419+ assert b"CP437" in frame
420+
421+
422+ def test_server_request_charset_uses_offer_callback ():
423+ """Server request_charset() must use offer callback, not send callback."""
424+ ws , ts , _ = new_writer (server = True )
425+ ws .remote_option [CHARSET ] = True
426+
427+ ws .set_ext_offer_callback (CHARSET , lambda : ["UTF-8" , "ASCII" ])
428+ ws .set_ext_send_callback (CHARSET , lambda offered : offered [0 ])
429+
430+ assert ws .request_charset () is True
431+ frame = ts .writes [- 1 ]
432+ assert frame .startswith (IAC + SB + CHARSET + REQUEST )
433+ assert b"UTF-8" in frame
434+
435+
436+ def test_handle_sb_charset_request_uses_send_callback ():
437+ """Receiving SB CHARSET REQUEST must use send callback (not offer)."""
438+ wc , tc , _ = new_writer (server = False , client = True )
439+ wc .local_option [CHARSET ] = True
440+ wc .remote_option [CHARSET ] = True
441+
442+ offer_called = []
443+ wc .set_ext_offer_callback (CHARSET , lambda : offer_called .append (True ) or ["NOPE" ])
444+ wc .set_ext_send_callback (CHARSET , lambda offered : "UTF-8" )
445+
446+ sep = b" "
447+ buf = collections .deque ([CHARSET , REQUEST , sep , b"UTF-8" ])
448+ wc ._handle_sb_charset (buf )
449+
450+ assert not offer_called
451+ assert wc .environ_encoding == "UTF-8"
452+
453+
454+ def test_server_handle_sb_charset_request_uses_send_callback ():
455+ """Server receiving SB CHARSET REQUEST from client uses send callback."""
456+ ws , ts , _ = new_writer (server = True )
457+ ws .remote_option [CHARSET ] = True
458+ ws .local_option [CHARSET ] = True
459+
460+ ws .set_ext_offer_callback (CHARSET , lambda : ["SHOULD-NOT-USE" ])
461+ ws .set_ext_send_callback (CHARSET , lambda offered : "CP437" if "CP437" in offered else "" )
462+
463+ sep = b" "
464+ buf = collections .deque ([CHARSET , REQUEST , sep , b"CP437" ])
465+ ws ._handle_sb_charset (buf )
466+
467+ assert ws .environ_encoding == "CP437"
0 commit comments