aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2025-10-07 20:03:32 -0700
committerGarrett D'Amore <garrett@damore.org>2025-10-07 20:03:32 -0700
commit3971d119c129bf5685f9fd14d0f1f785581c3565 (patch)
tree12b08c053d07fab5af699229ef52e311b8182d56
parent07191755f3a38cbac48d15523095136f69d8f772 (diff)
downloadnng-3971d119c129bf5685f9fd14d0f1f785581c3565.tar.gz
nng-3971d119c129bf5685f9fd14d0f1f785581c3565.tar.bz2
nng-3971d119c129bf5685f9fd14d0f1f785581c3565.zip
options: string options are passed by reference
This avoids needless allocations, and we offer for pipes (which need this because they might be ephemeral) the get_strdup, get_strcpy, and get_strlen forms. (Those do the copying or allocations while holding the pipe reference.)
-rw-r--r--docs/man/nng_dialer_get.3.adoc5
-rw-r--r--docs/man/nng_listener_get.3.adoc5
-rw-r--r--docs/ref/api/pipe.md21
-rw-r--r--docs/ref/api/stream.md14
-rw-r--r--include/nng/nng.h23
-rw-r--r--src/core/options.c12
-rw-r--r--src/core/stream.c8
-rw-r--r--src/nng.c66
-rw-r--r--src/supplemental/websocket/websocket_test.c10
9 files changed, 119 insertions, 45 deletions
diff --git a/docs/man/nng_dialer_get.3.adoc b/docs/man/nng_dialer_get.3.adoc
index 9b1e6577..575db971 100644
--- a/docs/man/nng_dialer_get.3.adoc
+++ b/docs/man/nng_dialer_get.3.adoc
@@ -29,7 +29,7 @@ int nng_dialer_get_size(nng_dialer d, const char *opt, size_t *zp);
int nng_dialer_get_addr(nng_dialer d, const char *opt, nng_sockaddr *sap);
-int nng_dialer_get_string(nng_dialer d, const char *opt, char **strp);
+int nng_dialer_get_string(nng_dialer d, const char *opt, const char **strp);
int nng_dialer_get_uint64(nng_dialer d, const char *opt, uint64_t *u64p);
@@ -77,9 +77,6 @@ into the value referenced by _sap_.
`nng_dialer_get_string()`::
This function is used to retrieve a string into _strp_.
-This string is created from the source using xref:nng_strdup.3.adoc[`nng_strdup()`]
-and consequently must be freed by the caller using
-xref:nng_strfree.3.adoc[`nng_strfree()`] when it is no longer needed.
`nng_dialer_get_uint64()`::
This function is used to retrieve a 64-bit unsigned value into the value
diff --git a/docs/man/nng_listener_get.3.adoc b/docs/man/nng_listener_get.3.adoc
index a5df826d..4c4de6b4 100644
--- a/docs/man/nng_listener_get.3.adoc
+++ b/docs/man/nng_listener_get.3.adoc
@@ -29,7 +29,7 @@ int nng_listener_get_size(nng_listener l, const char *opt, size_t *zp);
int nng_listener_get_addr(nng_listener l, const char *opt, nng_sockaddr *sap);
-int nng_listener_get_string(nng_listener l, const char *opt, char **strp);
+int nng_listener_get_string(nng_listener l, const char *opt, const char **strp);
int nng_listener_get_uint64(nng_listener l, const char *opt, uint64_t *u64p);
@@ -75,9 +75,6 @@ into the value referenced by _sap_.
`nng_listener_get_string()`::
This function is used to retrieve a string into _strp_.
-This string is created from the source using xref:nng_strdup.3.adoc[`nng_strdup()`]
-and consequently must be freed by the caller using
-xref:nng_strfree.3.adoc[`nng_strfree()`] when it is no longer needed.
`nng_listener_get_uint64()`::
This function is used to retrieve a 64-bit unsigned value into the value
diff --git a/docs/ref/api/pipe.md b/docs/ref/api/pipe.md
index fbd89120..4fae1d06 100644
--- a/docs/ref/api/pipe.md
+++ b/docs/ref/api/pipe.md
@@ -92,7 +92,10 @@ nng_err nng_pipe_get_int(nng_pipe p, const char *opt, int *valp);
nng_err nng_pipe_get_ms(nng_pipe p, const char *opt, nng_duration *valp);
nng_err nng_pipe_get_size(nng_pipe p, const char *opt, size_t *valp);
nng_err nng_pipe_get_addr(nng_pipe p, const char *opt, nng_sockaddr *valp);
-nng_err nng_pipe_get_string(nng_pipe p, const char *opt, char **valp);
+nng_err nng_pipe_get_string(nng_pipe p, const char *opt, const char **valp);
+nng_err nng_pipe_get_strcpy(nng_pipe p, const char *opt, char *val, size_t len);
+nng_err nng_pipe_get_strdup(nng_pipe p, const char *opt, char **valp);
+nng_err nng_pipe_get_strlen(nng_pipe p, const char *opt, size_t *lenp);
```
{{hi:`nng_pipe_get_bool`}}
@@ -101,14 +104,26 @@ nng_err nng_pipe_get_string(nng_pipe p, const char *opt, char **valp);
{{hi:`nng_pipe_get_size`}}
{{hi:`nng_pipe_get_addr`}}
{{hi:`nng_pipe_get_string`}}
+{{hi:`nng_pipe_get_strcpy`}}
+{{hi:`nng_pipe_get_strdup`}}
These functions are used to obtain value of an option named _opt_ from the pipe _p_, and store it in the location
referenced by _valp_.
These functions access an option as a specific type. The transport layer will have details about which options
are available, and which type they may be accessed using.
-In the case of `nng_pipe_get_string`, the string is created as if by [`nng_strdup`], and must be freed by
-the caller using [`nng_strfree`] when no longer needed.
+In the case of `nng_pipe_get_string`, the underlying string may only be valid for as long as the pipe is valid.
+Thus, this function can only be safely called in a pipe event callback set up with [`nng_pipe_notify`].
+
+The `nng_pipe_get_strdup` function is like `nng_pipe_get_string`, but makes a copy into a newly allocated buffer, so that the string must be freed by the caller using [`nng_strfree`].
+
+The `nng_pipe_get_strcpy` function is also like `nng_pipe_get_string`, but it makes a copy into a buffer
+supplied by the caller. The buffer is passed in _val_, and the size of the buffer is passed in _len_.
+The value of _len_ must be large enough to hold the string and the terminating zero byte.
+
+The `nng_pipe_get_strlen` function is used to obtain the length of the string. This can be useful
+to find the size of the buffer needed by the `nng_pipe_get_strcpy` function for a property.
+Note that like `strlen`, this size does not account for the zero byte to terminate the string.
## Pipe Notifications
diff --git a/docs/ref/api/stream.md b/docs/ref/api/stream.md
index 5715990f..a2427a70 100644
--- a/docs/ref/api/stream.md
+++ b/docs/ref/api/stream.md
@@ -84,7 +84,7 @@ nng_err nng_stream_get_int(nng_stream *s, const char *opt, int *valp);
nng_err nng_stream_get_ms(nng_stream *s, const char *opt, nng_duration *valp);
nng_err nng_stream_get_size(nng_stream *s, const char *opt, size_t *valp);
nng_err nng_stream_get_addr(nng_stream *s, const char *opt, nng_sockaddr *valp);
-nng_err nng_stream_get_string(nng_stream *s, const char *opt, char **valp);
+nng_err nng_stream_get_string(nng_stream *s, const char *opt, const char **valp);
```
{{hi:`nng_stream_get_bool`}}
@@ -99,8 +99,8 @@ referenced by _valp_.
These functions access an option as a specific type. The transport layer will have details about which options
are available, and which type they may be accessed using.
-In the case of `nng_stream_get_string`, the string is created as if by [`nng_strdup`], and must be freed by
-the caller using [`nng_strfree`] when no longer needed.
+In the case of `nng_stream_get_string`, the string pointer is only guaranteed to be valid while the
+stream exists. Callers should make a copy of the data if required before closing the stream.
## Stream Factories
@@ -261,14 +261,14 @@ nng_err nng_stream_dialer_get_bool(nng_stream_dialer *dialer, const char *opt, b
nng_err nng_stream_dialer_get_int(nng_stream_dialer *dialer, const char *opt, int *valp);
nng_err nng_stream_dialer_get_ms(nng_stream_dialer *dialer, const char *opt, nng_duration *valp);
nng_err nng_stream_dialer_get_size(nng_stream_dialer *dialer, const char *opt, size_t *valp);
-nng_err nng_stream_dialer_get_string(nng_stream_dialer *dialer, const char *opt, char **valp);
+nng_err nng_stream_dialer_get_string(nng_stream_dialer *dialer, const char *opt, const char **valp);
nng_err nng_stream_listener_get_addr(nng_stream_listener *listener, const char *opt, nng_sockaddr *valp);
nng_err nng_stream_listener_get_bool(nng_stream_listener *listener, const char *opt, bool *valp);
nng_err nng_stream_listener_get_int(nng_stream_listener *listener, const char *opt, int *valp);
nng_err nng_stream_listener_get_ms(nng_stream_listener *listener, const char *opt, nng_duration *valp);
nng_err nng_stream_listener_get_size(nng_stream_listener *listener, const char *opt, size_t *valp);
-nng_err nng_stream_listener_get_string(nng_stream_listener *listener, const char *opt, char **valp);
+nng_err nng_stream_listener_get_string(nng_stream_listener *listener, const char *opt, const char **valp);
nng_err nng_stream_dialer_set_addr(nng_stream_dialer *dialer, const char *opt, const nng_sockaddr *val);
nng_err nng_stream_dialer_set_bool(nng_stream_dialer *dialer, const char *opt, bool val);
@@ -316,8 +316,8 @@ The `nng_stream_dialer_set_` and `nng_stream_listener_set_` function families ch
These functions access an option as a specific type. The transport layer will have details about which options
are available, and which type they may be accessed using.
-In the case of `nng_stream_dialer_get_string` and `nng_stream_listener_get_string`, the string is created as if by [`nng_strdup`], and must be freed by
-the caller using [`nng_strfree`] when no longer needed.
+In the case of `nng_stream_dialer_get_string` and `nng_stream_listener_get_string`, the memory holding
+the string is only valid as long as the associated object remains open.
In the case of `nng_stream_dialer_set_string` and `nng_stream_listener_set_string`, the string contents are copied if necessary, so that the caller
need not retain the value referenced once the function returns.
diff --git a/include/nng/nng.h b/include/nng/nng.h
index 69c5ee89..4108f027 100644
--- a/include/nng/nng.h
+++ b/include/nng/nng.h
@@ -432,7 +432,7 @@ NNG_DECL int nng_dialer_get_bool(nng_dialer, const char *, bool *);
NNG_DECL int nng_dialer_get_int(nng_dialer, const char *, int *);
NNG_DECL int nng_dialer_get_size(nng_dialer, const char *, size_t *);
NNG_DECL int nng_dialer_get_uint64(nng_dialer, const char *, uint64_t *);
-NNG_DECL int nng_dialer_get_string(nng_dialer, const char *, char **);
+NNG_DECL int nng_dialer_get_string(nng_dialer, const char *, const char **);
NNG_DECL int nng_dialer_get_ms(nng_dialer, const char *, nng_duration *);
NNG_DECL int nng_dialer_get_addr(nng_dialer, const char *, nng_sockaddr *);
NNG_DECL int nng_dialer_get_tls(nng_dialer, nng_tls_config **);
@@ -454,7 +454,8 @@ NNG_DECL int nng_listener_get_bool(nng_listener, const char *, bool *);
NNG_DECL int nng_listener_get_int(nng_listener, const char *, int *);
NNG_DECL int nng_listener_get_size(nng_listener, const char *, size_t *);
NNG_DECL int nng_listener_get_uint64(nng_listener, const char *, uint64_t *);
-NNG_DECL int nng_listener_get_string(nng_listener, const char *, char **);
+NNG_DECL int nng_listener_get_string(
+ nng_listener, const char *, const char **);
NNG_DECL int nng_listener_get_ms(nng_listener, const char *, nng_duration *);
NNG_DECL int nng_listener_get_addr(nng_listener, const char *, nng_sockaddr *);
NNG_DECL int nng_listener_get_tls(nng_listener, nng_tls_config **);
@@ -770,7 +771,7 @@ NNG_DECL nng_err nng_pipe_get_bool(nng_pipe, const char *, bool *);
NNG_DECL nng_err nng_pipe_get_int(nng_pipe, const char *, int *);
NNG_DECL nng_err nng_pipe_get_ms(nng_pipe, const char *, nng_duration *);
NNG_DECL nng_err nng_pipe_get_size(nng_pipe, const char *, size_t *);
-NNG_DECL nng_err nng_pipe_get_string(nng_pipe, const char *, char **);
+NNG_DECL nng_err nng_pipe_get_string(nng_pipe, const char *, const char **);
NNG_DECL nng_err nng_pipe_get_addr(nng_pipe, const char *, nng_sockaddr *);
NNG_DECL nng_err nng_pipe_peer_cert(nng_pipe, nng_tls_cert **);
@@ -892,6 +893,15 @@ NNG_DECL nng_listener nng_pipe_listener(nng_pipe);
// headers from the peer on a pipe.
#define NNG_OPT_WS_HEADER "ws:header:"
+// NNG_OPT_WS_HEADER_KEY is also a prefix, but the value it contains
+// is the name (key) of the header at the index referenced. This
+// allows successive iteration through headers.
+#define NNG_OPT_WS_HEADER_KEY "ws:header_k:"
+
+// NNG_OPT_WS_HEADER_VAL is also a prefix, but the value it contains
+// is the value of the header at the index referenced.
+#define NNG_OPT_WS_HEADER_VAL "ws:header_v:"
+
// NNG_OPT_WS_REQUEST_URI is used to obtain the URI sent by the client.
// This can be useful when a handler supports an entire directory tree.
#define NNG_OPT_WS_REQUEST_URI "ws:request-uri"
@@ -1141,7 +1151,8 @@ NNG_DECL nng_err nng_stream_get_int(nng_stream *, const char *, int *);
NNG_DECL nng_err nng_stream_get_ms(nng_stream *, const char *, nng_duration *);
NNG_DECL nng_err nng_stream_get_size(nng_stream *, const char *, size_t *);
NNG_DECL nng_err nng_stream_get_uint64(nng_stream *, const char *, uint64_t *);
-NNG_DECL nng_err nng_stream_get_string(nng_stream *, const char *, char **);
+NNG_DECL nng_err nng_stream_get_string(
+ nng_stream *, const char *, const char **);
NNG_DECL nng_err nng_stream_get_addr(
nng_stream *, const char *, nng_sockaddr *);
NNG_DECL nng_err nng_stream_peer_cert(nng_stream *, nng_tls_cert **);
@@ -1164,7 +1175,7 @@ NNG_DECL nng_err nng_stream_dialer_get_size(
NNG_DECL nng_err nng_stream_dialer_get_uint64(
nng_stream_dialer *, const char *, uint64_t *);
NNG_DECL nng_err nng_stream_dialer_get_string(
- nng_stream_dialer *, const char *, char **);
+ nng_stream_dialer *, const char *, const char **);
NNG_DECL nng_err nng_stream_dialer_get_addr(
nng_stream_dialer *, const char *, nng_sockaddr *);
NNG_DECL nng_err nng_stream_dialer_set_bool(
@@ -1211,7 +1222,7 @@ NNG_DECL nng_err nng_stream_listener_get_size(
NNG_DECL nng_err nng_stream_listener_get_uint64(
nng_stream_listener *, const char *, uint64_t *);
NNG_DECL nng_err nng_stream_listener_get_string(
- nng_stream_listener *, const char *, char **);
+ nng_stream_listener *, const char *, const char **);
NNG_DECL nng_err nng_stream_listener_get_addr(
nng_stream_listener *, const char *, nng_sockaddr *);
NNG_DECL nng_err nng_stream_listener_set_bool(
diff --git a/src/core/options.c b/src/core/options.c
index 23e63d1c..f3843c0d 100644
--- a/src/core/options.c
+++ b/src/core/options.c
@@ -1,5 +1,5 @@
//
-// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2018 Devolutions <info@devolutions.net>
//
@@ -9,9 +9,9 @@
// found online at https://opensource.org/licenses/MIT.
//
-#include "core/defs.h"
-#include "core/nng_impl.h"
+#include "defs.h"
#include "nng/nng.h"
+#include "nng_impl.h"
#include <stdio.h>
#include <string.h>
@@ -174,16 +174,12 @@ nni_copyout_sockaddr(
nng_err
nni_copyout_str(const char *str, void *dst, size_t *szp, nni_type t)
{
- char *s;
NNI_ARG_UNUSED(szp);
if (t != NNI_TYPE_STRING) {
return (NNG_EBADTYPE);
}
- if ((s = nni_strdup(str)) == NULL) {
- return (NNG_ENOMEM);
- }
- *(char **) dst = s;
+ *(const char **) dst = str;
return (NNG_OK);
}
diff --git a/src/core/stream.c b/src/core/stream.c
index 61a8a3ba..572f28d1 100644
--- a/src/core/stream.c
+++ b/src/core/stream.c
@@ -368,7 +368,7 @@ nng_stream_get_size(nng_stream *s, const char *n, size_t *v)
}
nng_err
-nng_stream_get_string(nng_stream *s, const char *n, char **v)
+nng_stream_get_string(nng_stream *s, const char *n, const char **v)
{
return (nni_stream_get(s, n, v, NULL, NNI_TYPE_STRING));
}
@@ -413,7 +413,8 @@ nng_stream_dialer_get_size(nng_stream_dialer *d, const char *n, size_t *v)
}
nng_err
-nng_stream_dialer_get_string(nng_stream_dialer *d, const char *n, char **v)
+nng_stream_dialer_get_string(
+ nng_stream_dialer *d, const char *n, const char **v)
{
return (nni_stream_dialer_get(d, n, v, NULL, NNI_TYPE_STRING));
}
@@ -456,7 +457,8 @@ nng_stream_listener_get_size(nng_stream_listener *l, const char *n, size_t *v)
}
nng_err
-nng_stream_listener_get_string(nng_stream_listener *l, const char *n, char **v)
+nng_stream_listener_get_string(
+ nng_stream_listener *l, const char *n, const char **v)
{
return (nni_stream_listener_get(l, n, v, NULL, NNI_TYPE_STRING));
}
diff --git a/src/nng.c b/src/nng.c
index 39da87d4..423ddb64 100644
--- a/src/nng.c
+++ b/src/nng.c
@@ -780,7 +780,7 @@ nng_dialer_get_size(nng_dialer id, const char *n, size_t *v)
}
int
-nng_dialer_get_string(nng_dialer id, const char *n, char **v)
+nng_dialer_get_string(nng_dialer id, const char *n, const char **v)
{
return (dialer_get(id, n, v, NULL, NNI_TYPE_STRING));
}
@@ -909,7 +909,7 @@ nng_listener_get_size(nng_listener id, const char *n, size_t *v)
}
int
-nng_listener_get_string(nng_listener id, const char *n, char **v)
+nng_listener_get_string(nng_listener id, const char *n, const char **v)
{
return (listener_get(id, n, v, NULL, NNI_TYPE_STRING));
}
@@ -1358,12 +1358,72 @@ nng_pipe_get_size(nng_pipe id, const char *n, size_t *v)
}
nng_err
-nng_pipe_get_string(nng_pipe id, const char *n, char **v)
+nng_pipe_get_string(nng_pipe id, const char *n, const char **v)
{
return (pipe_get(id, n, v, NULL, NNI_TYPE_STRING));
}
nng_err
+nng_pipe_get_strcpy(nng_pipe p, const char *n, char *buf, size_t len)
+{
+ nng_err rv;
+ nni_pipe *pipe;
+ const char *s;
+
+ if ((rv = nni_pipe_find(&pipe, p.id)) != 0) {
+ return (rv);
+ }
+ rv = nni_pipe_getopt(pipe, n, &s, NULL, NNI_TYPE_STRING);
+ if (rv == NNG_OK) {
+ if (nni_strlcpy(buf, s != NULL ? s : "", len) >= len) {
+ rv = NNG_ENOSPC;
+ }
+ }
+ nni_pipe_rele(pipe);
+ return (rv);
+}
+
+nng_err
+nng_pipe_get_strdup(nng_pipe p, const char *n, char **v)
+{
+ nng_err rv;
+ nni_pipe *pipe;
+ const char *s;
+
+ if ((rv = nni_pipe_find(&pipe, p.id)) != 0) {
+ return (rv);
+ }
+ rv = nni_pipe_getopt(pipe, n, &s, NULL, NNI_TYPE_STRING);
+ if (rv == NNG_OK) {
+ if (s == NULL) {
+ *v = NULL;
+ } else if ((*v = nni_strdup(s)) == NULL) {
+ rv = NNG_ENOMEM;
+ }
+ }
+ nni_pipe_rele(pipe);
+ return (rv);
+}
+
+nng_err
+nng_pipe_get_strlen(nng_pipe p, const char *n, size_t *len)
+{
+ nng_err rv;
+ nni_pipe *pipe;
+ const char *s;
+
+ if ((rv = nni_pipe_find(&pipe, p.id)) != 0) {
+ return (rv);
+ }
+ rv = nni_pipe_getopt(pipe, n, &s, NULL, NNI_TYPE_STRING);
+ if (rv == NNG_OK) {
+ *len = s == NULL ? 0 : strlen(s);
+ }
+ nni_pipe_rele(pipe);
+ return (rv);
+}
+
+nng_err
nng_pipe_get_ms(nng_pipe id, const char *n, nng_duration *v)
{
return (pipe_get(id, n, v, NULL, NNI_TYPE_DURATION));
diff --git a/src/supplemental/websocket/websocket_test.c b/src/supplemental/websocket/websocket_test.c
index 98bcf658..cb502ef4 100644
--- a/src/supplemental/websocket/websocket_test.c
+++ b/src/supplemental/websocket/websocket_test.c
@@ -13,7 +13,7 @@
#include "sha1.h"
-#include <nuts.h>
+#include "../../testing/nuts.h"
void
test_websocket_wildcard(void)
@@ -138,7 +138,7 @@ test_websocket_conn_props(void)
nng_stream *c2 = NULL;
char uri[64];
bool on;
- char *str;
+ const char *str;
uint16_t port;
int rv;
@@ -208,18 +208,14 @@ test_websocket_conn_props(void)
NUTS_PASS(
nng_stream_get_string(c1, NNG_OPT_WS_HEADER "NNG-Req", &str));
NUTS_MATCH(str, "True");
- nng_strfree(str);
NUTS_PASS(
nng_stream_get_string(c2, NNG_OPT_WS_HEADER "NNG-Rep", &str));
NUTS_MATCH(str, "True");
- nng_strfree(str);
NUTS_PASS(nng_stream_get_string(
c1, NNG_OPT_WS_HEADER "Sec-WebSocket-Version", &str));
- NUTS_TRUE(str != NULL);
- NUTS_TRUE(strcmp(str, "13") == 0);
- nng_strfree(str);
+ NUTS_MATCH(str, "13");
nng_stream_close(c1);
nng_stream_close(c2);