Issues with new serviceID format
Closed this issue · 1 comments
nathancartlidge commented
Describe the bug
Huxley 2 appears to be unable to interpret the new-format Darwin Service IDs - as such, all queries that hit a new Darwin server (which now appears to be the majority?) will error out
To Reproduce
Query any previously valid route
Expected behaviour
Information Returned
Actual Behaviour
- Error 500 returned
In logs:
fail: Huxley2.Controllers.DeparturesController[0]
Open LDB DepartureBoard API call failed
System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
at System.Convert.FromBase64String(String s)
at ServiceIdGuidGetter(Object )
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.Converters.ArrayConverter`2.OnWriteResume(Utf8JsonWriter writer, TElement[] array, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryWrite(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.WriteCoreAsObject(Utf8JsonWriter writer, Object value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteCore[TValue](JsonConverter jsonConverter, Utf8JsonWriter writer, TValue& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteStream[TValue](Stream utf8Json, TValue& value, JsonTypeInfo jsonTypeInfo)
at System.Text.Json.JsonSerializer.Serialize(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options)
at Huxley2.Services.ChecksumGenerator.GenerateChecksumObj(Object obj) in /app/Services/ChecksumGenerator.cs:line 57
at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
at Huxley2.Services.ChecksumGenerator.GenerateChecksumImpl(Object response) in /app/Services/ChecksumGenerator.cs:line 43
at Huxley2.Services.ChecksumGenerator.GenerateChecksum(BaseStationBoard board) in /app/Services/ChecksumGenerator.cs:line 14
at Huxley2.Services.StationBoardService.GenerateChecksum(BaseStationBoard board) in /app/Services/StationBoardService.cs:line 103
at Huxley2.Controllers.DeparturesController.Get(StationBoardRequest request) in /app/Controllers/DeparturesController.cs:line 50
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "XXXXXXXXXXXXX", Request id "XXXXXXXXXXXXX:00000005": An unhandled exception was thrown by the application.
System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.
at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
at System.Convert.FromBase64String(String s)
at ServiceIdGuidGetter(Object )
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.Converters.ArrayConverter`2.OnWriteResume(Utf8JsonWriter writer, TElement[] array, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryWrite(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.WriteCoreAsObject(Utf8JsonWriter writer, Object value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteCore[TValue](JsonConverter jsonConverter, Utf8JsonWriter writer, TValue& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteStream[TValue](Stream utf8Json, TValue& value, JsonTypeInfo jsonTypeInfo)
at System.Text.Json.JsonSerializer.Serialize(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options)
at Huxley2.Services.ChecksumGenerator.GenerateChecksumObj(Object obj) in /app/Services/ChecksumGenerator.cs:line 57
at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
at Huxley2.Services.ChecksumGenerator.GenerateChecksumImpl(Object response) in /app/Services/ChecksumGenerator.cs:line 43
at Huxley2.Services.ChecksumGenerator.GenerateChecksum(BaseStationBoard board) in /app/Services/ChecksumGenerator.cs:line 14
at Huxley2.Services.StationBoardService.GenerateChecksum(BaseStationBoard board) in /app/Services/StationBoardService.cs:line 103
at Huxley2.Controllers.DeparturesController.Get(StationBoardRequest request) in /app/Controllers/DeparturesController.cs:line 50
at lambda_method18(Closure , Object )
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.ResponseCaching.ResponseCachingMiddleware.Invoke(HttpContext httpContext)
at ETagMiddleware.InvokeAsync(HttpContext context) in /app/ETagMiddleware.cs:line 31
at Microsoft.AspNetCore.ResponseCompression.ResponseCompressionMiddleware.InvokeCore(HttpContext context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
(Version 2.03)
Additional context
Open Rail Data discussion
nathancartlidge commented
In the short term, it appears this can be fixed with the following changes to BaseServiceItem.cs
and ServiceDetailsService.cs
:
--- a/Huxley2/Connected Services/OpenLDBWS/BaseServiceItem.cs
+++ b/Huxley2/Connected Services/OpenLDBWS/BaseServiceItem.cs
@@ -11,9 +11,9 @@ namespace OpenLDBWS
{
public partial class BaseServiceItem
{
public string ServiceIdPercentEncoded => WebUtility.UrlEncode(serviceIDField);
- public Guid ServiceIdGuid => new Guid(Convert.FromBase64String(serviceIDField));
+ // public Guid ServiceIdGuid => new Guid(serviceIDField);
[SuppressMessage("Design", "CA1056:Uri properties should not be strings", Justification = "Not a URL")]
- public string ServiceIdUrlSafe => WebEncoders.Base64UrlEncode(Convert.FromBase64String(serviceIDField));
+ public string ServiceIdUrlSafe => serviceIDField; // underscores are URL safe
}
}
--- a/Huxley2/Services/ServiceDetailsService.cs
+++ b/Huxley2/Services/ServiceDetailsService.cs
@@ -55,17 +55,27 @@ namespace Huxley2.Services
// If ID looks like a RID (15 decimal digit long base 10 integer) then use the staff API if configured
// This appears to be the date and the UID (with the first character in decimal representation)
- if (request.ServiceId.Length == 15 && long.TryParse(request.ServiceId, out _)
- && _accessTokenService.TryMakeStaffAccessToken(out var staffToken))
- {
- _logger.LogInformation($"Calling staff service details SOAP endpoint for {request.ServiceId}");
- var staffService = await _staffSoapClient.GetServiceDetailsByRIDAsync(
- new GetServiceDetailsByRIDRequest
+ if (request.ServiceId.Length == 15) {
+ if (long.TryParse(request.ServiceId, out _)
+ && _accessTokenService.TryMakeStaffAccessToken(out var staffToken)) {
+ _logger.LogInformation($"Calling staff service details SOAP endpoint for {request.ServiceId}");
+ var staffService = await _staffSoapClient.GetServiceDetailsByRIDAsync(
+ new GetServiceDetailsByRIDRequest
+ {
+ AccessToken = staffToken,
+ rid = request.ServiceId,
+ });
+ return staffService.GetServiceDetailsResult;
+ } else if (request.ServiceId.EndsWith("_")) {
+ _logger.LogInformation($"Calling service details SOAP endpoint for {request.ServiceId}");
+ var s = await _soapClient.GetServiceDetailsAsync(new GetServiceDetailsRequest
{
- AccessToken = staffToken,
- rid = request.ServiceId,
+ AccessToken = _accessTokenService.MakeAccessToken(request),
+ serviceID = request.ServiceId,
});
- return staffService.GetServiceDetailsResult;
+ return s.GetServiceDetailsResult;
+
+ }
}
However, this will break GUID support and percent encoding, so a more elegant overall solution is likely needed