Skip to content
Open
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
13 changes: 13 additions & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
[[release-4-0-0]]
=== TinkerPop 4.0.0 (Release Date: NOT OFFICIALLY RELEASED YET)

* Standardized `gremlin-dotnet` connection options per the TinkerPop 4.x GLV proposal:
** Renamed the `Auth.BasicAuth`/`Auth.SigV4Auth` factory methods to `Auth.Basic`/`Auth.Sigv4` (breaking; the old methods have been removed). *(breaking)*
** Renamed `MaxConnectionsPerServer` to `MaxConnections`, `IdleConnectionTimeout` to `IdleTimeout`, and `KeepAliveInterval` to `KeepAliveTime` (now wired to a real TCP keep-alive socket option rather than the inert HTTP/2 ping timeout, enabling `SO_KEEPALIVE` and setting the per-socket idle time on Windows, Linux, and macOS, with a no-op fallback to the OS default idle time on other platforms); the old property names have been removed. *(breaking)*
** Renamed `ConnectionTimeout` to `ConnectTimeout` (default lowered from 15s to 5s; old name removed). *(breaking)*
** Renamed `EnableCompression` to `Compression`, now a `{None, Deflate}` enum defaulting to `Deflate` (compression on by default; set `None` to disable). The old `EnableCompression` `bool` has been removed. *(breaking)*
** Added `Ssl` (an `SslClientAuthenticationOptions`; `SkipCertificateValidation` is applied to an internal copy rather than mutating the caller's options).
** Added `BatchSize` (default 64), a connection-level default that fills the per-request `batchSize` when unset.
** Added `MaxResponseHeaderBytes`, exposing the handler's maximum response header size.
** Added `ReadTimeout`, a per-read idle timeout applied to each read of the response stream.
** Each timeout option is also settable in milliseconds via an `int` companion property (`ConnectTimeoutMillis`, `IdleTimeoutMillis`, `ReadTimeoutMillis`, `KeepAliveTimeMillis`); the unsuffixed `TimeSpan` property remains the idiomatic form and both reflect the same value.
** Added `Proxy`, routing connections through an `IWebProxy`.
* Fixed `gremlin-dotnet` deflate response decompression, which threw on the server's zlib-framed output because it used `DeflateStream` (raw DEFLATE, RFC 1951) instead of `ZLibStream` (zlib, RFC 1950); the bug was previously masked because compression was off by default.
* Fixed `gremlin-dotnet` SSL options cloning (used on the skip-certificate-validation path) to copy `ClientCertificateContext` and `AllowTlsResume`, which were previously dropped, breaking mTLS client certificates and silently re-enabling TLS resumption.
* Added configurable CORS `allowedOrigins` setting to Gremlin Server; warns when wildcard origin is used alongside authentication.
* Fixed `ByteBuf` leak in `GraphBinaryMessageSerializerV4` when serialization throws an `IOException`.
* Changed `Tree` to no longer extend `HashMap`; it is now a final class with a tree-shaped API (`childAt`, `hasChild`, `contains`, `findSubtree`, `getOrCreateChild`, `getNodesAtDepth`, `getLeafNodes`, `nodeCount`) and is no longer a `Map`.
Expand Down
42 changes: 30 additions & 12 deletions docs/src/reference/gremlin-variants.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -2454,20 +2454,20 @@ include::../../../gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Docs/Reference

Authentication is handled through request interceptors. Interceptors are functions that modify the outgoing HTTP
request before it is sent — they are used for authentication, custom headers, or request signing. The `Auth` class
provides `BasicAuth()` and `SigV4Auth()` as built-in interceptors:
provides `Basic()` and `SigV4()` as built-in interceptors:

[source,csharp]
----
// Basic authentication
var server = new GremlinServer("localhost", 8182, enableSsl: true);
using var client = new GremlinClient(server,
connectionSettings: new ConnectionSettings { SkipCertificateValidation = true },
interceptors: new[] { Auth.BasicAuth("username", "password") });
interceptors: new[] { Auth.Basic("username", "password") });

// SigV4 authentication
var server = new GremlinServer("localhost", 8182, enableSsl: true);
using var client = new GremlinClient(server,
interceptors: new[] { Auth.SigV4Auth("service-region", "service-name") });
interceptors: new[] { Auth.Sigv4("service-region", "service-name") });
----

If you authenticate to a remote <<connecting-gremlin-server,Gremlin Server>> or
Expand Down Expand Up @@ -2523,18 +2523,36 @@ constructor:
[width="100%",cols="3,10,^2",options="header"]
|=========================================================
|Key |Description |Default
|ConnectionTimeout |The TCP connection timeout. |15 s
|IdleConnectionTimeout |How long idle connections stay in the pool before being closed. |180 s
|MaxConnectionsPerServer |The maximum concurrent connections to a single server. |128
|KeepAliveInterval |The TCP keep-alive probe interval. |30 s
|EnableCompression |Whether to request deflate compression. |false
|ConnectTimeoutMillis |The TCP connect timeout in milliseconds (transport establishment, i.e. TCP connect plus TLS handshake where applicable, not an HTTP request timeout). Also settable as `ConnectTimeout` (a `TimeSpan`). |5000
|IdleTimeoutMillis |How long in milliseconds idle connections stay in the pool before being closed. Also settable as `IdleTimeout` (a `TimeSpan`). |180000
|MaxConnections |The maximum concurrent connections to a single server. |128
|KeepAliveTimeMillis |Idle time in milliseconds before TCP keep-alive probes begin on an otherwise idle connection. Enables `SO_KEEPALIVE` on the socket and sets the per-socket idle time on Windows, Linux, and macOS; on other platforms keep-alive stays enabled at the OS default idle time. Also settable as `KeepAliveTime` (a `TimeSpan`). |30000
|Compression |The response compression algorithm (`Compression.None` or `Compression.Deflate`). |`Compression.Deflate`
|BatchSize |The connection-level default batch size used to fill the per-request batch size when it is unset. |64
|Ssl |The `SslClientAuthenticationOptions` used for HTTPS connections (client certificates, custom CA, protocols, etc.). `SkipCertificateValidation` is applied to an internal copy of these options rather than mutating the object you provide. |`null`
|MaxResponseHeaderBytes |The maximum allowed size, in bytes, of the response headers. `0` leaves the handler default unchanged (converted internally to the handler's native kilobyte unit). |0
|ReadTimeoutMillis |The idle-read timeout in milliseconds applied to each individual read of the response stream. It resets per chunk. `0` (the default) disables it. Also settable as `ReadTimeout` (a `TimeSpan`; `Timeout.InfiniteTimeSpan` disables). |0
|Proxy |The `IWebProxy` used for connections. |`null`
|EnableUserAgentOnConnect |Enables sending a user agent to the server on requests.
More details can be found in provider docs
link:https://tinkerpop.apache.org/docs/x.y.z/dev/provider/#_graph_driver_provider_requirements[here].|true
|BulkResults |Whether to send the bulkResults header on all requests. |false
|SkipCertificateValidation |Whether to skip SSL certificate validation. Only use for testing with self-signed certificates. |false
|SkipCertificateValidation |Whether to skip SSL certificate validation. Only use for testing with self-signed certificates. When `Ssl` is also provided, the accept-all callback is set on an internal copy so the supplied options object is never mutated. |false
|=========================================================

Note that no driver timeout bounds the *total* duration of a request once it is under way. `ReadTimeout` only bounds
the gap between response chunks, so a response that keeps producing chunks will not time out no matter how long it
runs overall, and there is no client-side "overall" request timeout. If you need an absolute deadline, impose it in
your application by passing a cancellation token that cancels after the deadline:

[source,csharp]
----
// bound the entire request (submit plus full result iteration) to 30 seconds
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var results = await client.SubmitAsync<Vertex>(
"g.V().out().out()", cancellationToken: cts.Token);
----

==== GremlinClient Settings

The following options can be passed to the `GremlinClient` constructor:
Expand Down Expand Up @@ -2564,7 +2582,7 @@ A request interceptor is a `Func<HttpRequestContext, Task>` that mutates the `Ht
`Task` for async support but does not produce a value). A list of these is maintained and will be run sequentially for
each request. When creating a `GremlinClient`, the `interceptors` parameter accepts an ordered collection of
interceptors. Order matters, so if one interceptor depends on another's output, ensure they are added in the correct
order. Note that authentication (e.g. `Auth.BasicAuth()`, `Auth.SigV4Auth()`) is also implemented using interceptors.
order. Note that authentication (e.g. `Auth.Basic()`, `Auth.Sigv4()`) is also implemented using interceptors.
These factory methods return interceptor delegates that can be included in the `interceptors` list. Alternatively, the
`auth` parameter on `GremlinClient` appends the auth interceptor to the end of the list so it runs last.

Expand All @@ -2574,7 +2592,7 @@ var server = new GremlinServer("localhost", 8182);
using var client = new GremlinClient(server,
interceptors: new Func<HttpRequestContext, Task>[]
{
Auth.BasicAuth("username", "password"),
Auth.Basic("username", "password"),
context =>
{
context.Headers["X-Custom-Header"] = "value";
Expand Down Expand Up @@ -2721,7 +2739,7 @@ order and are useful for authentication, custom headers, or request signing.
[source,csharp]
----
var client = new GremlinClient(new GremlinServer("localhost", 8182),
interceptors: new[] { Auth.BasicAuth("username", "password") });
interceptors: new[] { Auth.Basic("username", "password") });
----

When `requestSerializer` is set to `null`, the request body is passed as a `RequestMessage` to interceptors, and an
Expand Down
40 changes: 40 additions & 0 deletions docs/src/upgrade/release-4.x.x.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,46 @@ complete list of all the modifications that are part of this release.

=== Upgrading for Users

==== Standardizing .NET Connection Options

TinkerPop 4.x standardizes connection option names and defaults across the GLVs. In `gremlin-dotnet`, several
`ConnectionSettings` properties and the `Auth` factory methods have been renamed for consistency. Because this is a
major version, the old names have been removed rather than retained as aliases, and a number of new options have been
added. The notes below describe the .NET changes. See <<glv-driver-changes, GLV Driver Changes>> for the
equivalent changes in the other drivers.

Renames (breaking). The following members have been renamed and the old names removed. Migrate to the new names:

- `ConnectionTimeout` is now `ConnectTimeout`.
- `MaxConnectionsPerServer` is now `MaxConnections`.
- `IdleConnectionTimeout` is now `IdleTimeout`.
- `KeepAliveInterval` is now `KeepAliveTime`.
- `EnableCompression` is now `Compression`.
- The `Auth.BasicAuth` and `Auth.SigV4Auth` factory methods are now `Auth.Basic` and `Auth.Sigv4`.

Behavior changes. These change runtime behavior on upgrade, even if you do not change your configuration:

- `ConnectTimeout` now defaults to 5s (lowered from 15s).
- `KeepAliveTime` is now wired to a real TCP keep-alive socket option rather than the inert HTTP/2 ping timeout. It
enables `SO_KEEPALIVE` and sets the per-socket idle time on Windows, Linux, and macOS; on other platforms keep-alive
stays enabled at the OS default idle time.
- `Compression` is now a `{None, Deflate}` enum defaulting to `Deflate` (compression on by default), so the driver
sends `Accept-Encoding: deflate` by default. Set `Compression.None` to disable. The old `EnableCompression` `bool`
has been removed.

New options:

- `Ssl` (an `SslClientAuthenticationOptions` for client certificates and custom CAs; `SkipCertificateValidation` is
applied to an internal copy rather than mutating the supplied options).
- `BatchSize` (default 64): a connection-level default that fills the per-request batch size when unset.
- `MaxResponseHeaderBytes`: the maximum allowed size, in bytes, of the response headers.
- `ReadTimeout`: a per-read idle timeout applied to each individual read of the response stream.
- Each timeout is also settable in milliseconds via an `int` companion property (`ConnectTimeoutMillis`, `IdleTimeoutMillis`,
`ReadTimeoutMillis`, `KeepAliveTimeMillis`); the unsuffixed `TimeSpan` property is the idiomatic form.
- `Proxy`: routes connections through an `IWebProxy`.

See: link:https://lists.apache.org/thread/yqtr2wnb1kq2pqqq4002cz511q5o0bkg[[DISCUSS] Standardizing GLV connection options in TinkerPop 4].

===== Declarative Pattern Matching

Gremlin has always offered both imperative and declarative styles to writing graph queries. While the imperative style
Expand Down
4 changes: 2 additions & 2 deletions gremlin-dotnet/Examples/Connections/Connections.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ static void WithConf()
var server = new GremlinServer(ServerHost, ServerPort);
var settings = new ConnectionSettings
{
ConnectionTimeout = TimeSpan.FromSeconds(30),
ConnectTimeout = TimeSpan.FromSeconds(30),
};
using var remoteConnection = new DriverRemoteConnection(
new GremlinClient(server, connectionSettings: settings), "g");
Expand All @@ -71,7 +71,7 @@ static void WithBasicAuth()
var server = new GremlinServer(ServerHost, SecureServerPort, enableSsl: true);
var client = new GremlinClient(server,
connectionSettings: new ConnectionSettings { SkipCertificateValidation = true },
interceptors: new[] { Auth.BasicAuth("stephen", "password") });
interceptors: new[] { Auth.Basic("stephen", "password") });
using var remoteConnection = new DriverRemoteConnection(client, "g");
var g = Traversal().With(remoteConnection);

Expand Down
5 changes: 3 additions & 2 deletions gremlin-dotnet/src/Gremlin.Net/Driver/Auth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static class Auth
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
/// <returns>A request interceptor delegate.</returns>
public static Func<HttpRequestContext, Task> BasicAuth(string username, string password)
public static Func<HttpRequestContext, Task> Basic(string username, string password)
{
var encoded = Convert.ToBase64String(
Encoding.UTF8.GetBytes(username + ":" + password));
Expand All @@ -68,7 +68,7 @@ public static Func<HttpRequestContext, Task> BasicAuth(string username, string p
/// Optional AWS credentials. When null, the default credential chain is used.
/// </param>
/// <returns>A request interceptor delegate.</returns>
public static Func<HttpRequestContext, Task> SigV4Auth(
public static Func<HttpRequestContext, Task> Sigv4(
string region, string service, AWSCredentials? credentials = null)
{
// Cache the credential provider once when using the default chain.
Expand Down Expand Up @@ -105,6 +105,7 @@ public static Func<HttpRequestContext, Task> SigV4Auth(
};
}


private static void SignRequest(HttpRequestContext context,
ImmutableCredentials credentials, AWS4Signer signer, SigningClientConfig clientConfig)
{
Expand Down
107 changes: 107 additions & 0 deletions gremlin-dotnet/src/Gremlin.Net/Driver/Compression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#region License

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

#endregion

using System;

namespace Gremlin.Net.Driver
{
/// <summary>
/// The compression algorithm requested for server responses. The server currently
/// supports <see cref="Deflate"/> only; additional members are reserved for when the
/// server adds support for them (server-side first).
/// </summary>
public enum CompressionType
{
/// <summary>
/// No compression.
/// </summary>
None,

/// <summary>
/// Deflate compression.
/// </summary>
Deflate
}

/// <summary>
/// Configures response compression. A <see cref="CompressionType"/> is implicitly
/// convertible (<see cref="CompressionType.Deflate"/> = <see cref="Deflate"/>,
/// <see cref="CompressionType.None"/> = <see cref="None"/>).
/// </summary>
public readonly struct Compression : IEquatable<Compression>
{
/// <summary>
/// Gets the configured compression algorithm.
/// </summary>
public CompressionType Type { get; }

/// <summary>
/// Initializes a new instance of the <see cref="Compression"/> struct.
/// </summary>
/// <param name="type">The compression algorithm.</param>
public Compression(CompressionType type)
{
Type = type;
}

/// <summary>
/// No compression.
/// </summary>
public static Compression None => new Compression(CompressionType.None);

/// <summary>
/// Deflate compression.
/// </summary>
public static Compression Deflate => new Compression(CompressionType.Deflate);

/// <summary>
/// Gets whether compression is enabled (i.e. the algorithm is not <see cref="CompressionType.None"/>).
/// </summary>
public bool Enabled => Type != CompressionType.None;

/// <summary>
/// Implicitly converts a <see cref="CompressionType"/> to a <see cref="Compression"/>.
/// </summary>
/// <param name="type">The compression algorithm.</param>
public static implicit operator Compression(CompressionType type) =>
new Compression(type);

/// <inheritdoc />
public bool Equals(Compression other) => Type == other.Type;

/// <inheritdoc />
public override bool Equals(object? obj) => obj is Compression other && Equals(other);

/// <inheritdoc />
public override int GetHashCode() => (int)Type;

/// <summary>Determines whether two <see cref="Compression"/> values are equal.</summary>
public static bool operator ==(Compression left, Compression right) => left.Equals(right);

/// <summary>Determines whether two <see cref="Compression"/> values are not equal.</summary>
public static bool operator !=(Compression left, Compression right) => !left.Equals(right);

/// <inheritdoc />
public override string ToString() => Type.ToString();
}
}
Loading
Loading