websocket: fix curl_ws_recv()
- when data arrived in several chunks, the collection into the passed buffer always started at offset 0, overwriting the data already there. adding test_20_07 to verify fix - debug environment var CURL_WS_CHUNK_SIZE can be used to influence the buffer chunk size used for en-/decoding. Closes #12945
This commit is contained in:
parent
e3461bbd05
commit
f0c446ab57
@ -116,3 +116,8 @@ Debug-version of the *ntlm-wb* executable.
|
|||||||
|
|
||||||
OpenLDAP tracing is enabled if this variable exists and its value is 1 or
|
OpenLDAP tracing is enabled if this variable exists and its value is 1 or
|
||||||
greater. There is a number of debug levels, refer to *openldap.c* comments.
|
greater. There is a number of debug levels, refer to *openldap.c* comments.
|
||||||
|
|
||||||
|
## CURL_WS_CHUNK_SIZE
|
||||||
|
|
||||||
|
Used to influence the buffer chunk size used for WebSocket encoding and
|
||||||
|
decoding.
|
||||||
|
|||||||
21
lib/ws.c
21
lib/ws.c
@ -754,13 +754,26 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
|
|||||||
DEBUGASSERT(data->conn);
|
DEBUGASSERT(data->conn);
|
||||||
ws = data->conn->proto.ws;
|
ws = data->conn->proto.ws;
|
||||||
if(!ws) {
|
if(!ws) {
|
||||||
|
size_t chunk_size = WS_CHUNK_SIZE;
|
||||||
ws = calloc(1, sizeof(*ws));
|
ws = calloc(1, sizeof(*ws));
|
||||||
if(!ws)
|
if(!ws)
|
||||||
return CURLE_OUT_OF_MEMORY;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
data->conn->proto.ws = ws;
|
data->conn->proto.ws = ws;
|
||||||
Curl_bufq_init2(&ws->recvbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT,
|
#ifdef DEBUGBUILD
|
||||||
|
{
|
||||||
|
char *p = getenv("CURL_WS_CHUNK_SIZE");
|
||||||
|
if(p) {
|
||||||
|
long l = strtol(p, NULL, 10);
|
||||||
|
if(l > 0 && l <= (1*1024*1024)) {
|
||||||
|
chunk_size = (size_t)l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
DEBUGF(infof(data, "WS, using chunk size %zu", chunk_size));
|
||||||
|
Curl_bufq_init2(&ws->recvbuf, chunk_size, WS_CHUNK_COUNT,
|
||||||
BUFQ_OPT_SOFT_LIMIT);
|
BUFQ_OPT_SOFT_LIMIT);
|
||||||
Curl_bufq_init2(&ws->sendbuf, WS_CHUNK_SIZE, WS_CHUNK_COUNT,
|
Curl_bufq_init2(&ws->sendbuf, chunk_size, WS_CHUNK_COUNT,
|
||||||
BUFQ_OPT_SOFT_LIMIT);
|
BUFQ_OPT_SOFT_LIMIT);
|
||||||
ws_dec_init(&ws->dec);
|
ws_dec_init(&ws->dec);
|
||||||
ws_enc_init(&ws->enc);
|
ws_enc_init(&ws->enc);
|
||||||
@ -834,7 +847,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
|
|||||||
|
|
||||||
struct ws_collect {
|
struct ws_collect {
|
||||||
struct Curl_easy *data;
|
struct Curl_easy *data;
|
||||||
void *buffer;
|
unsigned char *buffer;
|
||||||
size_t buflen;
|
size_t buflen;
|
||||||
size_t bufidx;
|
size_t bufidx;
|
||||||
int frame_age;
|
int frame_age;
|
||||||
@ -886,7 +899,7 @@ static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
*err = CURLE_OK;
|
*err = CURLE_OK;
|
||||||
memcpy(ctx->buffer, buf, nwritten);
|
memcpy(ctx->buffer + ctx->bufidx, buf, nwritten);
|
||||||
ctx->bufidx += nwritten;
|
ctx->bufidx += nwritten;
|
||||||
}
|
}
|
||||||
return nwritten;
|
return nwritten;
|
||||||
|
|||||||
@ -129,3 +129,14 @@ class TestWebsockets:
|
|||||||
url = f'ws://localhost:{env.ws_port}/'
|
url = f'ws://localhost:{env.ws_port}/'
|
||||||
r = client.run(args=[url, str(65535 - 5), str(65535 + 5)])
|
r = client.run(args=[url, str(65535 - 5), str(65535 + 5)])
|
||||||
r.check_exit_code(0)
|
r.check_exit_code(0)
|
||||||
|
|
||||||
|
# the python websocket server does not like 'large' control frames
|
||||||
|
def test_20_07_data_large_small_recv(self, env: Env, ws_echo, repeat):
|
||||||
|
client = LocalClient(env=env, name='ws-data', run_env={
|
||||||
|
'CURL_WS_CHUNK_SIZE': '1024',
|
||||||
|
})
|
||||||
|
if not client.exists():
|
||||||
|
pytest.skip(f'example client not built: {client.name}')
|
||||||
|
url = f'ws://localhost:{env.ws_port}/'
|
||||||
|
r = client.run(args=[url, str(65535 - 5), str(65535 + 5)])
|
||||||
|
r.check_exit_code(0)
|
||||||
|
|||||||
@ -45,10 +45,12 @@ log = logging.getLogger(__name__)
|
|||||||
class LocalClient:
|
class LocalClient:
|
||||||
|
|
||||||
def __init__(self, name: str, env: Env, run_dir: Optional[str] = None,
|
def __init__(self, name: str, env: Env, run_dir: Optional[str] = None,
|
||||||
timeout: Optional[float] = None):
|
timeout: Optional[float] = None,
|
||||||
|
run_env: Optional[Dict[str,str]] = None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.path = os.path.join(env.project_dir, f'tests/http/clients/{name}')
|
self.path = os.path.join(env.project_dir, f'tests/http/clients/{name}')
|
||||||
self.env = env
|
self.env = env
|
||||||
|
self._run_env= run_env
|
||||||
self._timeout = timeout if timeout else env.test_timeout
|
self._timeout = timeout if timeout else env.test_timeout
|
||||||
self._curl = os.environ['CURL'] if 'CURL' in os.environ else env.curl
|
self._curl = os.environ['CURL'] if 'CURL' in os.environ else env.curl
|
||||||
self._run_dir = run_dir if run_dir else os.path.join(env.gen_dir, name)
|
self._run_dir = run_dir if run_dir else os.path.join(env.gen_dir, name)
|
||||||
@ -95,7 +97,7 @@ class LocalClient:
|
|||||||
with open(self._stderrfile, 'w') as cerr:
|
with open(self._stderrfile, 'w') as cerr:
|
||||||
p = subprocess.run(myargs, stderr=cerr, stdout=cout,
|
p = subprocess.run(myargs, stderr=cerr, stdout=cout,
|
||||||
cwd=self._run_dir, shell=False,
|
cwd=self._run_dir, shell=False,
|
||||||
input=None,
|
input=None, env=self._run_env,
|
||||||
timeout=self._timeout)
|
timeout=self._timeout)
|
||||||
exitcode = p.returncode
|
exitcode = p.returncode
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user