Skip to content

Commit f54f842

Browse files
committed
Allow a richer set of transformations by passing a mutable
structure into in2out and out2in and allowing handler to move or replace the pointer and/or length.
1 parent d6daf79 commit f54f842

4 files changed

Lines changed: 59 additions & 24 deletions

File tree

python/AsyncProxy.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424

2525
from ctypes import cdll, c_int, c_char_p, c_ushort, c_void_p, CFUNCTYPE, \
26-
POINTER, pointer, Structure, Union, byref
26+
POINTER, pointer, Structure, Union, byref, c_size_t
2727

2828
from sysconfig import get_config_var
2929
from site import getsitepackages
@@ -58,7 +58,13 @@ class asyncproxy_ctor_args(Structure):
5858
("_anon_union", _AnonUnion),
5959
]
6060

61-
_asp_data_cb = CFUNCTYPE(None, c_void_p, c_int)
61+
class transform_res(Structure):
62+
_fields_ = [
63+
("buf", c_void_p),
64+
("len", c_size_t),
65+
]
66+
67+
_asp_data_cb = CFUNCTYPE(None, POINTER(transform_res))
6268

6369
_esuf = get_config_var('EXT_SUFFIX')
6470
if not _esuf:

src/asyncproxy.c

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ struct asyncproxy {
7474
socklen_t alen;
7575
} destaddr;
7676
int last_seen_alive;
77-
void (*transform[2])(void *, int);
77+
void (*transform[2])(struct transform_res *);
7878
int needsjoin;
7979
char addrbuf[FILENAME_MAX];
8080
};
@@ -163,7 +163,7 @@ asp_sock_setnonblock(int fd)
163163

164164
struct io_buf {
165165
unsigned char data[16 * 1024];
166-
int len;
166+
size_t len;
167167
};
168168

169169
#define BUF_FREE(ibp) (sizeof((ibp)->data) - (ibp)->len)
@@ -278,17 +278,32 @@ asyncproxy_run(void *args)
278278
goto out;
279279
}
280280
pthread_mutex_lock(&ap->mutex);
281-
void (*transform)(void *, int) = ap->transform[i];
281+
__typeof(ap->transform[i]) transform = ap->transform[i];
282282
pthread_mutex_unlock(&ap->mutex);
283283
if (transform != NULL) {
284284
#if defined(PYTHON_AWARE)
285285
PyGILState_STATE gstate;
286286
gstate = PyGILState_Ensure();
287287
#endif
288-
transform(BUF_P(&bufs[i]), r.len);
288+
struct transform_res tr = {BUF_P(&bufs[i]), r.len};
289+
transform(&tr);
289290
#if defined(PYTHON_AWARE)
290291
PyGILState_Release(gstate);
291292
#endif
293+
if ((ssize_t)tr.len != r.len) {
294+
assert(BUF_FREE(&bufs[i]) >= tr.len);
295+
r.len = tr.len;
296+
}
297+
if (tr.buf != BUF_P(&bufs[i])) {
298+
if (tr.len > 0) {
299+
assert(BUF_FREE(&bufs[i]) >= tr.len);
300+
memmove(BUF_P(&bufs[i]), tr.buf, tr.len);
301+
}
302+
r.len = tr.len;
303+
} else if ((ssize_t)tr.len != r.len) {
304+
assert((ssize_t)tr.len < r.len);
305+
r.len = tr.len;
306+
}
292307
}
293308
bufs[i].len += r.len;
294309
if (BUF_FREE(&bufs[i]) == 0) {
@@ -308,12 +323,12 @@ asyncproxy_run(void *args)
308323
fprintf(stderr, "asyncproxy_run(%p): sent %ld bytes to %d\n", ap, rlen, pfds[j].fd);
309324
fflush(stderr);
310325
}
311-
if (rlen < bufs[i].len) {
326+
if (rlen < (ssize_t)bufs[i].len) {
312327
pfds[j].events |= POLLOUT;
313328
}
314329
if (rlen <= 0)
315330
continue;
316-
if (rlen < bufs[i].len) {
331+
if (rlen < (ssize_t)bufs[i].len) {
317332
memmove(bufs[i].data, bufs[i].data + rlen, bufs[i].len - rlen);
318333
bufs[i].len -= rlen;
319334
} else {
@@ -552,7 +567,7 @@ asyncproxy_isalive(void *_ap)
552567
}
553568

554569
void
555-
asyncproxy_set_i2o(void *_ap, void (*i2ofp)(void *, int))
570+
asyncproxy_set_i2o(void *_ap, void (*i2ofp)(struct transform_res *))
556571
{
557572
struct asyncproxy *ap;
558573

@@ -564,7 +579,7 @@ asyncproxy_set_i2o(void *_ap, void (*i2ofp)(void *, int))
564579
}
565580

566581
void
567-
asyncproxy_set_o2i(void *_ap, void (*o2ifp)(void *, int))
582+
asyncproxy_set_o2i(void *_ap, void (*o2ifp)(struct transform_res *))
568583
{
569584
struct asyncproxy *ap;
570585

src/asyncproxy.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,16 @@ struct asyncproxy_ctor_args {
2424
};
2525
};
2626

27+
struct transform_res {
28+
void *buf;
29+
size_t len;
30+
};
31+
2732
void * asyncproxy_ctor(const struct asyncproxy_ctor_args *);
2833
int asyncproxy_start(void *);
2934
int asyncproxy_isalive(void *);
30-
void asyncproxy_set_i2o(void *, void (*)(void *, int));
31-
void asyncproxy_set_o2i(void *, void (*)(void *, int));
35+
void asyncproxy_set_i2o(void *, void (*)(struct transform_res *));
36+
void asyncproxy_set_o2i(void *, void (*)(struct transform_res *));
3237
void asyncproxy_join(void *, int);
3338
void asyncproxy_dtor(void *);
3439
const char * asyncproxy_describe(void *);

tests/AsyncProxy2FD_test.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
11
import socket
22
import unittest
33
from ctypes import string_at, memmove
4-
from libasyncproxy.AsyncProxy import AsyncProxy2FD
4+
from libasyncproxy.AsyncProxy import AsyncProxy2FD, transform_res
55

66
class NosyProxy(AsyncProxy2FD):
7-
def in2out(self, ptr, length):
8-
# Read original data from the pointer
7+
def in2out(self, res_p):
8+
# unpack the struct
9+
tr = res_p.contents
10+
ptr, length = tr.buf, tr.len
11+
# read, transform, write back
912
original = string_at(ptr, length)
10-
# Transform data to uppercase (note: transformation must retain length)
11-
transformed = original.upper()
13+
# Transform data to uppercase
14+
length -= 1
15+
transformed = original.upper()[:length]
1216
memmove(ptr, transformed, length)
1317
print("in2out hook: transformed", original, "to", transformed)
18+
tr.len = length
1419

15-
def out2in(self, ptr, length):
16-
# Read original data from the pointer
17-
original = string_at(ptr, length)
18-
# Reverse the data bytes (again, ensuring the length remains unchanged)
19-
transformed = original[::-1]
20+
def out2in(self, res_p):
21+
# unpack the struct
22+
tr = res_p.contents
23+
ptr, length = tr.buf, tr.len
24+
# read, transform, write back
25+
original = string_at(ptr, length)
26+
length -= 1
27+
transformed = original[::-1][1:]
2028
memmove(ptr, transformed, length)
2129
print("out2in hook: transformed", original, "to", transformed)
30+
tr.len = length
2231

2332
class AsyncProxy2FDTest(unittest.TestCase):
2433
def test_AsyncProxy(self):
@@ -45,7 +54,7 @@ def test_AsyncProxy(self):
4554

4655
server_received = server_socket.recv(1024)
4756
print("Server received:", server_received.decode())
48-
self.assertEqual(client_message.upper(), server_received)
57+
self.assertEqual(client_message.upper()[:-1], server_received)
4958

5059
# Now send a message from server back to client.
5160
server_message = b"Hello from Server!"
@@ -54,7 +63,7 @@ def test_AsyncProxy(self):
5463

5564
client_received = client_socket.recv(1024)
5665
print("Client received:", client_received.decode())
57-
self.assertEqual(server_message[::-1], client_received)
66+
self.assertEqual(server_message[::-1][1:], client_received)
5867

5968
# Shutdown the proxy worker and cleanup.
6069
proxy_fd.join(shutdown=True)

0 commit comments

Comments
 (0)