Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions embed/rayforce_q.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,22 @@
#include "lang/env.h" /* ray_env_bind, ray_env_bind_flat */
#include "lang/eval.h" /* ray_fn_*, RAY_FN_NONE */

#include <stdlib.h> /* atoi */
#include <errno.h>
#include <stdlib.h>
#include <string.h>

static int q_parse_port(const char *s, int *out) {
if (s == NULL || *s == '\0')
return 0;
errno = 0;
char *end = NULL;
long v = strtol(s, &end, 10);
if (errno != 0 || end == s || *end != '\0' || v < 1 || v > 65535)
return 0;
*out = (int)v;
return 1;
}

static int64_t q_atom_i64(ray_t *a, int *ok) {
*ok = 1;
if (a == NULL) {
Expand Down Expand Up @@ -81,6 +94,8 @@ static ray_t *qb_connect(ray_t **args, int64_t n) {
int64_t port = q_atom_i64(args[1], &ok);
if (!ok)
return ray_error("type", ".q.connect: port must be an integer");
if (port < 1 || port > 65535)
return ray_error("range", ".q.connect: port must be in 1..65535");

char host[256];
size_t hn = ray_str_len(args[0]);
Expand Down Expand Up @@ -172,8 +187,14 @@ void q_env_register(void) {
int64_t q_serve_from_args(ray_poll_t *poll, int argc, char **argv) {
for (int i = 1; i < argc; i++) {
if ((strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--q-serve") == 0) &&
i + 1 < argc)
return q_serve(poll, atoi(argv[++i]));
i + 1 < argc) {
int port = 0;
if (!q_parse_port(argv[++i], &port)) {
fprintf(stderr, "q: invalid port %s (expected 1..65535)\n", argv[i]);
return -1;
}
return q_serve(poll, port);
}
}
return -1;
}
11 changes: 10 additions & 1 deletion q.c
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,6 @@ static ray_t *q_des_vec_i(uint8_t **buf, int64_t *len, int8_t ray_type,
return vec;
}


static inline int64_t q_kz_days_to_nanos(double days) {
if (isnan(days))
return NULL_I64;
Expand Down Expand Up @@ -980,6 +979,8 @@ static ray_t *q_des_obj(uint8_t **buf, int64_t *len) {
i++;
if (i == *len)
return ray_error("q: malformed error frame", NULL);
*buf += i + 1;
*len -= i + 1;
/* Put the q error text in both the code (short, shown by ray_fmt) and the
* message (full), so bindings reading the message field get the whole
* string even though the displayed code is length-capped. */
Expand Down Expand Up @@ -1067,6 +1068,9 @@ static int q_decompress(const uint8_t *src, int64_t src_len, uint8_t **out_buf,
/* Public API */
int q_connect(const char *host, int port, const char *user,
const char *password, int timeout_ms) {
if (host == NULL || host[0] == '\0' || port < 1 || port > 65535)
return Q_ERR_SOCKET;

int timed_out = 0;
int fd = q_open_socket(host, port, timeout_ms, &timed_out);
if (fd < 0)
Expand Down Expand Up @@ -1228,6 +1232,11 @@ ray_t *q_decode(uint8_t *resp, int64_t resp_len, int compressed, char *err,
free(decompressed);
if (result == NULL)
q_set_err(err, errlen, "q: deserialization returned null");
else if (remaining != 0) {
ray_release(result);
q_set_err(err, errlen, "q: trailing bytes after object");
return NULL;
}
return result;
}

Expand Down
4 changes: 4 additions & 0 deletions q_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@ static void q_on_close(ray_poll_t *poll, ray_selector_t *sel) {
int64_t q_serve(ray_poll_t *poll, int port) {
if (poll == NULL)
return -1;
if (port < 1 || port > 65535) {
fprintf(stderr, "q: invalid port %d (expected 1..65535)\n", port);
return -1;
}
ray_sock_t fd = ray_sock_listen((uint16_t)port);
if (fd == RAY_INVALID_SOCK) {
fprintf(stderr, "q: cannot listen on port %d (in use?)\n", port);
Expand Down
62 changes: 62 additions & 0 deletions test/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include "core/poll.h" /* ray_poll_create / run / destroy */
#include "core/runtime.h" /* ray_runtime_set_poll */
#include "q.h" /* q_decode / q_connect */
#include "q_server.h" /* q_serve */

#include <stdio.h>
Expand Down Expand Up @@ -275,7 +276,68 @@ static void inject_server(const char *host, const char *port,
eval_setup("(set qpass \"%s\")", pass);
}

static void release_any(ray_t *r) {
if (r == NULL)
return;
if (RAY_IS_ERR(r))
ray_error_free(r);
else
ray_release(r);
}

static int run_codec_selftest(void) {
int failures = 0;
ray_runtime_t *rt = ray_runtime_create(0, NULL);
if (rt == NULL) {
fprintf(stderr, "codec selftest: failed to create rayforce runtime\n");
return 1;
}

char err[128] = {0};
uint8_t int_with_tail[] = {250, 42, 0, 0, 0, 0xff};
ray_t *r = q_decode(int_with_tail, (int64_t)sizeof int_with_tail, 0, err,
sizeof err);
if (r != NULL || strstr(err, "trailing bytes") == NULL) {
fprintf(stderr, "codec selftest: trailing body bytes were not rejected\n");
failures++;
}
release_any(r);

err[0] = '\0';
uint8_t qerr[] = {128, 'b', 'a', 'd', 0};
r = q_decode(qerr, (int64_t)sizeof qerr, 0, err, sizeof err);
if (r == NULL || !RAY_IS_ERR(r)) {
fprintf(stderr, "codec selftest: Q error frame did not decode as error\n");
failures++;
}
release_any(r);

if (q_connect("127.0.0.1", 70000, "", "", 1) != Q_ERR_SOCKET) {
fprintf(stderr, "codec selftest: client accepted out-of-range port\n");
failures++;
}

ray_poll_t *poll = ray_poll_create();
if (poll == NULL) {
fprintf(stderr, "codec selftest: failed to create poll\n");
failures++;
} else {
if (q_serve(poll, -1) >= 0 || q_serve(poll, 70000) >= 0) {
fprintf(stderr, "codec selftest: server accepted out-of-range port\n");
failures++;
}
ray_poll_destroy(poll);
}

ray_runtime_destroy(rt);
printf("codec selftest: %s\n", failures ? "FAIL" : "ok");
return failures ? 1 : 0;
}

int main(int argc, char **argv) {
if (argc >= 2 && strcmp(argv[1], "--codec-selftest") == 0)
return run_codec_selftest();

/* Server role: `driver --serve PORT`. */
if (argc >= 3 && strcmp(argv[1], "--serve") == 0)
return run_server(atoi(argv[2]));
Expand Down
3 changes: 3 additions & 0 deletions test/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ cleanup() {
}
trap cleanup EXIT

echo "running codec selftest..."
"$DRIVER" --codec-selftest

# ---- Leg 1: Rayforce server
SERVERPORT="${SERVERPORT:-$(free_port)}"
"$DRIVER" --serve "$SERVERPORT" &
Expand Down
Loading