<commit_before>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using EdjCase.JsonRpc.Core;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;

namespace EdjCase.JsonRpc.Client
{

	/// <summary>
	/// A client for making rpc requests and receiving rpc responses
	/// </summary>
	public class RpcClient
	{
		/// <summary>
		/// Base url for the rpc server
		/// </summary>
		public Uri BaseUrl { get; }
		/// <summary>
		/// Authentication header value factory for the rpc request being sent. If the server requires
		/// authentication this requires a value. Otherwise it can be null
		/// </summary>
		public Func<Task<AuthenticationHeaderValue>> AuthHeaderValueFactory { get; set; }
		/// <summary>
		/// Json serialization settings that will be used in serialization and deserialization
		/// for rpc requests
		/// </summary>
		public JsonSerializerSettings JsonSerializerSettings { get; set; }
		/// <summary>
		/// Request encoding type for json content. If null, will default to UTF-8 
		/// </summary>
		public Encoding Encoding { get; set; }
		/// <summary>
		/// Request content type for json content. If null, will default to application/json
		/// </summary>
		public string ContentType { get; set; }
		/// <summary>
		/// Add headers to the underlying http client
		/// </summary>
		public List<KeyValuePair<string, string>> Headers { get; set; }

		private JsonSerializer jsonSerializerCache { get; set; }

		/// <param name="baseUrl">Base url for the rpc server</param>
		/// <param name="authHeaderValue">Http authentication header for rpc request</param>
		/// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param>
		/// <param name="encoding">(Optional)Encoding type for request. Defaults to UTF-8</param>
		/// <param name="contentType">(Optional)Content type header for the request. Defaults to application/json</param>
		/// <param name="headers">(Optional)Extra headers</param>
		public RpcClient(Uri baseUrl, AuthenticationHeaderValue authHeaderValue = null, JsonSerializerSettings jsonSerializerSettings = null,
			Encoding encoding = null, string contentType = null, List<KeyValuePair<string, string>> headers = null)
		{
			this.BaseUrl = baseUrl;
			this.AuthHeaderValueFactory = () => Task.FromResult(authHeaderValue);
			this.JsonSerializerSettings = jsonSerializerSettings;
			this.Encoding = encoding;
			this.ContentType = contentType;
			this.Headers = headers;
		}

		/// <param name="baseUrl">Base url for the rpc server</param>
		/// <param name="authHeaderValueFactory">Http authentication header factory for rpc request</param>
		/// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param>
		/// <param name="encoding">(Optional)Encoding type for request. Defaults to UTF-8</param>
		/// <param name="contentType">(Optional)Content type header for the request. Defaults to application/json</param>
		/// <param name="headers">(Optional)Extra headers</param>
		public RpcClient(Uri baseUrl, Func<Task<AuthenticationHeaderValue>> authHeaderValueFactory = null, JsonSerializerSettings jsonSerializerSettings = null,
			Encoding encoding = null, string contentType = null, List<KeyValuePair<string, string>> headers = null)
		{
			this.BaseUrl = baseUrl;
			this.AuthHeaderValueFactory = authHeaderValueFactory;
			this.JsonSerializerSettings = jsonSerializerSettings;
			this.Encoding = encoding;
			this.ContentType = contentType;
			this.Headers = headers;
		}


		/// <summary>
		/// Sends the specified rpc request to the server
		/// </summary>
		/// <param name="request">Single rpc request that will goto the rpc server</param>
		/// <param name="route">(Optional) Route that will append to the base url if the request method call is not located at the base route</param>
		/// <returns>The rpc response for the sent request</returns>
		public async Task<RpcResponse> SendRequestAsync(RpcRequest request, string route = null)
		{
			if (request == null)
			{
				throw new ArgumentNullException(nameof(request));
			}
			return await this.SendAsync(request, this.DeserilizeSingleRequest, route).ConfigureAwait(false);
		}

		private RpcResponse DeserilizeSingleRequest(string json)
		{
			JToken token = JToken.Parse(json);
			JsonSerializer serializer = JsonSerializer.Create(this.JsonSerializerSettings);
			switch (token.Type)
			{
				case JTokenType.Object:
					RpcResponse response = token.ToObject<RpcResponse>(serializer);
					return response;
				case JTokenType.Array:
					return token.ToObject<List<RpcResponse>>(serializer).SingleOrDefault();
				default:
					throw new Exception("Cannot parse rpc response from server.");
			}<commit_msg>Better error messaging with bad response parsing<commit_after>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using EdjCase.JsonRpc.Core;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;

namespace EdjCase.JsonRpc.Client
{

	/// <summary>
	/// A client for making rpc requests and receiving rpc responses
	/// </summary>
	public class RpcClient
	{
		/// <summary>
		/// Base url for the rpc server
		/// </summary>
		public Uri BaseUrl { get; }
		/// <summary>
		/// Authentication header value factory for the rpc request being sent. If the server requires
		/// authentication this requires a value. Otherwise it can be null
		/// </summary>
		public Func<Task<AuthenticationHeaderValue>> AuthHeaderValueFactory { get; set; }
		/// <summary>
		/// Json serialization settings that will be used in serialization and deserialization
		/// for rpc requests
		/// </summary>
		public JsonSerializerSettings JsonSerializerSettings { get; set; }
		/// <summary>
		/// Request encoding type for json content. If null, will default to UTF-8 
		/// </summary>
		public Encoding Encoding { get; set; }
		/// <summary>
		/// Request content type for json content. If null, will default to application/json
		/// </summary>
		public string ContentType { get; set; }
		/// <summary>
		/// Add headers to the underlying http client
		/// </summary>
		public List<KeyValuePair<string, string>> Headers { get; set; }

		private JsonSerializer jsonSerializerCache { get; set; }

		/// <param name="baseUrl">Base url for the rpc server</param>
		/// <param name="authHeaderValue">Http authentication header for rpc request</param>
		/// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param>
		/// <param name="encoding">(Optional)Encoding type for request. Defaults to UTF-8</param>
		/// <param name="contentType">(Optional)Content type header for the request. Defaults to application/json</param>
		/// <param name="headers">(Optional)Extra headers</param>
		public RpcClient(Uri baseUrl, AuthenticationHeaderValue authHeaderValue = null, JsonSerializerSettings jsonSerializerSettings = null,
			Encoding encoding = null, string contentType = null, List<KeyValuePair<string, string>> headers = null)
		{
			this.BaseUrl = baseUrl;
			this.AuthHeaderValueFactory = () => Task.FromResult(authHeaderValue);
			this.JsonSerializerSettings = jsonSerializerSettings;
			this.Encoding = encoding;
			this.ContentType = contentType;
			this.Headers = headers;
		}

		/// <param name="baseUrl">Base url for the rpc server</param>
		/// <param name="authHeaderValueFactory">Http authentication header factory for rpc request</param>
		/// <param name="jsonSerializerSettings">Json serialization settings that will be used in serialization and deserialization for rpc requests</param>
		/// <param name="encoding">(Optional)Encoding type for request. Defaults to UTF-8</param>
		/// <param name="contentType">(Optional)Content type header for the request. Defaults to application/json</param>
		/// <param name="headers">(Optional)Extra headers</param>
		public RpcClient(Uri baseUrl, Func<Task<AuthenticationHeaderValue>> authHeaderValueFactory = null, JsonSerializerSettings jsonSerializerSettings = null,
			Encoding encoding = null, string contentType = null, List<KeyValuePair<string, string>> headers = null)
		{
			this.BaseUrl = baseUrl;
			this.AuthHeaderValueFactory = authHeaderValueFactory;
			this.JsonSerializerSettings = jsonSerializerSettings;
			this.Encoding = encoding;
			this.ContentType = contentType;
			this.Headers = headers;
		}


		/// <summary>
		/// Sends the specified rpc request to the server
		/// </summary>
		/// <param name="request">Single rpc request that will goto the rpc server</param>
		/// <param name="route">(Optional) Route that will append to the base url if the request method call is not located at the base route</param>
		/// <returns>The rpc response for the sent request</returns>
		public async Task<RpcResponse> SendRequestAsync(RpcRequest request, string route = null)
		{
			if (request == null)
			{
				throw new ArgumentNullException(nameof(request));
			}
			return await this.SendAsync(request, this.DeserilizeSingleRequest, route).ConfigureAwait(false);
		}

		private RpcResponse DeserilizeSingleRequest(string json)
		{
			if (string.IsNullOrWhiteSpace(json))
			{
				throw new RpcClientParseException("Server did not return a rpc response, just an empty body.");
			}
			try
			{
				JToken token = JToken.Parse(json);
				JsonSerializer serializer = JsonSerializer.Create(this.JsonSerializerSettings);
				switch (token.Type)
				{
					case JTokenType.Object:
						RpcResponse response = token.ToObject<RpcResponse>(serializer);
						return response;
					case JTokenType.Array:
						return token.ToObject<List<RpcResponse>>(serializer).SingleOrDefault();
					default:
						throw new Exception("Cannot parse rpc response from server.");
				}
			}catch(Exception ex)
			{
				throw new RpcClientParseException($"Unable to parse response from server: '{json}'", ex);
			}
{'fraction_non_alphanumeric': 0.11031784841075795, 'fraction_numerical': 0.00058679706601467, 'mean_word_length': 4.0977068793619145, 'pattern_counts': {'":': 0, '<': 123, '<?xml version=': 0, '>': 125, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2352084', 'n_tokens_mistral': 3087, 'n_tokens_neox': 2773, 'n_words': 886}