Skip to content

Commit

Permalink
fix fiekd value escaping bug
Browse files Browse the repository at this point in the history
  • Loading branch information
MikaelGRA committed Apr 19, 2020
1 parent 3c7357f commit 478304c
Show file tree
Hide file tree
Showing 8 changed files with 1,081 additions and 963 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 4.0.3
* Fixed a bug where the character used for escaping in a field value was not properly escaped itself when writing to influxdb

## 4.0.2
* Fixed a bug that could occur when parsing a an empty string in a field that should be interpreted as a null

Expand Down
136 changes: 69 additions & 67 deletions src/Vibrant.InfluxDB.Client/Http/HttpContentExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,74 +12,76 @@

namespace Vibrant.InfluxDB.Client.Http
{
internal static class HttpContentExtensions
{
private static readonly JsonSerializer Serializer;

static HttpContentExtensions()
{
var settings = new JsonSerializerSettings();
settings.Converters.Add( new StringEnumConverter() );
settings.DateParseHandling = DateParseHandling.None;


Serializer = JsonSerializer.CreateDefault( settings );
}

internal async static Task<JsonStreamObjectIterator> GetObjectIteratorAsync( this HttpContent content, CancellationToken cancellationToken = default( CancellationToken ) )
{
if( content == null )
throw new ArgumentNullException( nameof( content ) );

var readOnlyStream = await content.ReadAsStreamAsync().ConfigureAwait( false );
return new JsonStreamObjectIterator( readOnlyStream, Serializer );
}

internal static Task<T> ReadAsJsonAsync<T>( this HttpContent content, CancellationToken cancellationToken = default( CancellationToken ) )
{
if( content == null )
throw new ArgumentNullException( nameof( content ) );

return ReadAsJsonAsyncCore<T>( content, cancellationToken );
}

internal static Task<List<T>> ReadMultipleAsJsonAsync<T>( this HttpContent content, CancellationToken cancellationToken = default( CancellationToken ) )
{
if( content == null )
throw new ArgumentNullException( nameof( content ) );

return ReadMultipleAsJsonAsyncCore<T>( content, cancellationToken );
}

private async static Task<T> ReadAsJsonAsyncCore<T>( HttpContent content, CancellationToken cancellationToken )
{
cancellationToken.ThrowIfCancellationRequested();
var readStream = await content.ReadAsStreamAsync().ConfigureAwait( false );
var reader = new JsonTextReader( new StreamReader( readStream, Encoding.UTF8 ) );
T result = Serializer.Deserialize<T>( reader );
return result;
}

private async static Task<List<T>> ReadMultipleAsJsonAsyncCore<T>( HttpContent content, CancellationToken cancellationToken )
{
cancellationToken.ThrowIfCancellationRequested();
var readOnlyStream = await content.ReadAsStreamAsync().ConfigureAwait( false );

List<T> results = new List<T>();
var reader = new JsonTextReader( new StreamReader( readOnlyStream, Encoding.UTF8 ) );
reader.SupportMultipleContent = true;
while( true )
{
if( !reader.Read() )
internal static class HttpContentExtensions
{
private static readonly JsonSerializer Serializer;

static HttpContentExtensions()
{
var settings = new JsonSerializerSettings();
settings.Converters.Add(new StringEnumConverter());
settings.DateParseHandling = DateParseHandling.None;


Serializer = JsonSerializer.CreateDefault(settings);
}

internal async static Task<JsonStreamObjectIterator> GetObjectIteratorAsync(this HttpContent content, CancellationToken cancellationToken = default(CancellationToken))
{
if (content == null)
throw new ArgumentNullException(nameof(content));

var readOnlyStream = await content.ReadAsStreamAsync().ConfigureAwait(false);
return new JsonStreamObjectIterator(readOnlyStream, Serializer);
}

internal static Task<T> ReadAsJsonAsync<T>(this HttpContent content, CancellationToken cancellationToken = default(CancellationToken))
{
if (content == null)
throw new ArgumentNullException(nameof(content));

return ReadAsJsonAsyncCore<T>(content, cancellationToken);
}

internal static Task<List<T>> ReadMultipleAsJsonAsync<T>(this HttpContent content, CancellationToken cancellationToken = default(CancellationToken))
{
if (content == null)
throw new ArgumentNullException(nameof(content));

return ReadMultipleAsJsonAsyncCore<T>(content, cancellationToken);
}

private async static Task<T> ReadAsJsonAsyncCore<T>(HttpContent content, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var readStream = await content.ReadAsStreamAsync().ConfigureAwait(false);
var reader = new JsonTextReader(new StreamReader(readStream, Encoding.UTF8));
T result = Serializer.Deserialize<T>(reader);
return result;
}

private async static Task<List<T>> ReadMultipleAsJsonAsyncCore<T>(HttpContent content, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var readOnlyStream = await content.ReadAsStreamAsync().ConfigureAwait(false);

List<T> results = new List<T>();
var reader = new JsonTextReader(new StreamReader(readOnlyStream, Encoding.UTF8));
reader.SupportMultipleContent = true;
while (true)
{
break;
}
if (!reader.Read())
{
break;
}

cancellationToken.ThrowIfCancellationRequested();

T result = Serializer.Deserialize<T>( reader );
results.Add( result );
}
T result = Serializer.Deserialize<T>(reader);
results.Add(result);
}

return results;
}
}
return results;
}
}
}
229 changes: 130 additions & 99 deletions src/Vibrant.InfluxDB.Client/Parsers/LineProtocolEscape.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,116 +9,147 @@

namespace Vibrant.InfluxDB.Client.Parsers
{
internal static class LineProtocolEscape
{
private static readonly IDictionary<string, string> EscapedKeys = new ConcurrentDictionary<string, string>();
internal static class LineProtocolEscape
{
private static readonly IDictionary<string, string> EscapedKeys = new ConcurrentDictionary<string, string>();

internal static string EscapeFieldValue( object valueAsObject )
{
if( valueAsObject is double )
{
return ( (double)valueAsObject ).ToString( CultureInfo.InvariantCulture );
}
else if ( valueAsObject is long || valueAsObject is int || valueAsObject is short || valueAsObject is byte || valueAsObject is ulong || valueAsObject is uint || valueAsObject is ushort || valueAsObject is sbyte )
{
return valueAsObject.ToString() + 'i';
}
else if ( valueAsObject is string )
{
return '\"' + ( (string)valueAsObject ).Replace( "\"", "\\\"" ) + '\"';
}
else if ( valueAsObject is bool )
{
return ( (bool)valueAsObject ) ? InfluxConstants.True : InfluxConstants.False;
}
else if( valueAsObject is float )
{
return ( (float)valueAsObject ).ToString( CultureInfo.InvariantCulture );
}
else if( valueAsObject is DateTime )
{
var valueAsDateTime = (DateTime)valueAsObject;
internal static string EscapeFieldValue(object valueAsObject)
{
if (valueAsObject is double)
{
return ((double)valueAsObject).ToString(CultureInfo.InvariantCulture);
}
else if (valueAsObject is long || valueAsObject is int || valueAsObject is short || valueAsObject is byte || valueAsObject is ulong || valueAsObject is uint || valueAsObject is ushort || valueAsObject is sbyte)
{
return valueAsObject.ToString() + 'i';
}
else if (valueAsObject is string)
{
return EscapeFieldStringValue((string)valueAsObject);
}
else if (valueAsObject is bool)
{
return ((bool)valueAsObject) ? InfluxConstants.True : InfluxConstants.False;
}
else if (valueAsObject is float)
{
return ((float)valueAsObject).ToString(CultureInfo.InvariantCulture);
}
else if (valueAsObject is DateTime)
{
var valueAsDateTime = (DateTime)valueAsObject;

return '\"' + valueAsDateTime.ToIso8601() + '\"';
}
else if (valueAsObject is DateTimeOffset)
{
var valueAsDateTimeOffset = (DateTimeOffset)valueAsObject;

return '\"' + valueAsDateTimeOffset.ToIso8601() + '\"';
}
else
{
return Convert.ToString(valueAsObject, CultureInfo.InvariantCulture);
}
}

return '\"' + valueAsDateTime.ToIso8601() + '\"';
}
else if( valueAsObject is DateTimeOffset )
{
var valueAsDateTimeOffset = (DateTimeOffset)valueAsObject;
internal static string EscapeFieldStringValue(string str)
{
if (str == null || str.Length == 0)
{
return "";
}

return '\"' + valueAsDateTimeOffset.ToIso8601() + '\"';
}
else
{
return Convert.ToString( valueAsObject, CultureInfo.InvariantCulture );
}
}
char c;
int len = str.Length;
StringBuilder sb = new StringBuilder(len + 4);
sb.Append("\"");
for (int i = 0; i < len; i++)
{
c = str[i];
switch (c)
{
case '\\':
sb.Append("\\\\");
break;
case '"':
sb.Append("\\\"");
break;
default:
sb.Append(c);
break;
}
}
sb.Append("\"");
return sb.ToString();
}

internal static string EscapeTagValue( string value )
{
return Escape( value );
}
internal static string EscapeTagValue(string value)
{
return Escape(value);
}

internal static string EscapeKey( string value )
{
// attempt to get a cached value here, as there will be a limited set of different tags
string cachedValue;
if ( !EscapedKeys.TryGetValue( value, out cachedValue ) )
{
cachedValue = Escape( value );
EscapedKeys[ value ] = cachedValue;
}
return cachedValue;
}
internal static string EscapeKey(string value)
{
// attempt to get a cached value here, as there will be a limited set of different tags
string cachedValue;
if (!EscapedKeys.TryGetValue(value, out cachedValue))
{
cachedValue = Escape(value);
EscapedKeys[value] = cachedValue;
}
return cachedValue;
}

private static string Escape( string value )
{
// https://docs.influxdata.com/influxdb/v0.9/write_protocols/line/
private static string Escape(string value)
{
// https://docs.influxdata.com/influxdb/v0.9/write_protocols/line/

StringBuilder builder = new StringBuilder( value.Length );
for ( int i = 0 ; i < value.Length ; i++ )
{
var c = value[ i ];
switch ( c )
StringBuilder builder = new StringBuilder(value.Length);
for (int i = 0; i < value.Length; i++)
{
case ',':
builder.Append( "\\," );
break;
case ' ':
builder.Append( "\\ " );
break;
case '=':
builder.Append( "\\=" );
break;
default:
builder.Append( c );
break;
var c = value[i];
switch (c)
{
case ',':
builder.Append("\\,");
break;
case ' ':
builder.Append("\\ ");
break;
case '=':
builder.Append("\\=");
break;
default:
builder.Append(c);
break;
}
}
}
return builder.ToString();
}
return builder.ToString();
}

public static string EscapeMeasurementName( string value )
{
// https://docs.influxdata.com/influxdb/v1.2/write_protocols/line_protocol_tutorial/#special-characters-and-keywords
public static string EscapeMeasurementName(string value)
{
// https://docs.influxdata.com/influxdb/v1.2/write_protocols/line_protocol_tutorial/#special-characters-and-keywords

StringBuilder builder = new StringBuilder( value.Length );
for( int i = 0 ; i < value.Length ; i++ )
{
var c = value[ i ];
switch( c )
StringBuilder builder = new StringBuilder(value.Length);
for (int i = 0; i < value.Length; i++)
{
case ',':
builder.Append( "\\," );
break;
case ' ':
builder.Append( "\\ " );
break;
default:
builder.Append( c );
break;
var c = value[i];
switch (c)
{
case ',':
builder.Append("\\,");
break;
case ' ':
builder.Append("\\ ");
break;
default:
builder.Append(c);
break;
}
}
}
return builder.ToString();
}
}
return builder.ToString();
}
}
}
Loading

0 comments on commit 478304c

Please sign in to comment.