PageRenderTime 69ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/Raven.Client.Lightweight/Connection/Async/AsyncServerClient.cs

http://github.com/ayende/ravendb
C# | 2727 lines | 2322 code | 386 blank | 19 comment | 323 complexity | 2c9f9bc948dccdd45e455eeb26ab532d MD5 | raw file
Possible License(s): GPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Apache-2.0, BSD-3-Clause, CC-BY-SA-3.0
  1. //-----------------------------------------------------------------------
  2. // <copyright file="AsyncServerClient.cs" company="Hibernating Rhinos LTD">
  3. // Copyright (c) Hibernating Rhinos LTD. All rights reserved.
  4. // </copyright>
  5. //-----------------------------------------------------------------------
  6. using Raven.Abstractions;
  7. using Raven.Abstractions.Cluster;
  8. using Raven.Abstractions.Commands;
  9. using Raven.Abstractions.Connection;
  10. using Raven.Abstractions.Data;
  11. using Raven.Abstractions.Exceptions;
  12. using Raven.Abstractions.Extensions;
  13. using Raven.Abstractions.Indexing;
  14. using Raven.Abstractions.Json;
  15. using Raven.Abstractions.Replication;
  16. using Raven.Abstractions.Smuggler;
  17. using Raven.Abstractions.Util;
  18. using Raven.Client.Changes;
  19. using Raven.Client.Connection.Implementation;
  20. using Raven.Client.Connection.Profiling;
  21. using Raven.Client.Connection.Request;
  22. using Raven.Client.Document;
  23. using Raven.Client.Exceptions;
  24. using Raven.Client.Extensions;
  25. using Raven.Client.Indexes;
  26. using Raven.Client.Listeners;
  27. using Raven.Client.Metrics;
  28. using Raven.Client.Util.Auth;
  29. using Raven.Database.Data;
  30. using Raven.Imports.Newtonsoft.Json;
  31. using Raven.Imports.Newtonsoft.Json.Linq;
  32. using Raven.Json.Linq;
  33. using System;
  34. using System.Collections.Generic;
  35. using System.Collections.Specialized;
  36. using System.Globalization;
  37. using System.IO;
  38. using System.Linq;
  39. using System.Net;
  40. using System.Net.Http;
  41. using System.Text;
  42. using System.Threading;
  43. using System.Threading.Tasks;
  44. namespace Raven.Client.Connection.Async
  45. {
  46. public class AsyncServerClient : IAsyncDatabaseCommands, IAsyncInfoDatabaseCommands
  47. {
  48. private readonly ProfilingInformation profilingInformation;
  49. private readonly IDocumentConflictListener[] conflictListeners;
  50. private readonly Guid? sessionId;
  51. private readonly Func<AsyncServerClient, string, bool, IRequestExecuter> requestExecuterGetter;
  52. private readonly Func<string, RequestTimeMetric> requestTimeMetricGetter;
  53. private readonly string databaseName;
  54. private readonly string primaryUrl;
  55. private readonly OperationCredentials credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication;
  56. private readonly RequestExecuterSelector requestExecuterSelector;
  57. internal readonly DocumentConvention convention;
  58. internal readonly HttpJsonRequestFactory jsonRequestFactory;
  59. private int requestCount;
  60. private NameValueCollection operationsHeaders = new NameValueCollection();
  61. public string Url
  62. {
  63. get { return primaryUrl; }
  64. }
  65. public IRequestExecuter RequestExecuter
  66. {
  67. get { return requestExecuterSelector.Select(); }
  68. }
  69. public OperationCredentials PrimaryCredentials
  70. {
  71. get
  72. {
  73. return credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication;
  74. }
  75. }
  76. public AsyncServerClient(
  77. string url,
  78. DocumentConvention convention,
  79. OperationCredentials credentials,
  80. HttpJsonRequestFactory jsonRequestFactory,
  81. Guid? sessionId,
  82. Func<AsyncServerClient, string, bool, IRequestExecuter> requestExecuterGetter,
  83. Func<string, RequestTimeMetric> requestTimeMetricGetter,
  84. string databaseName,
  85. IDocumentConflictListener[] conflictListeners,
  86. bool incrementReadStripe)
  87. {
  88. profilingInformation = ProfilingInformation.CreateProfilingInformation(sessionId);
  89. primaryUrl = url;
  90. if (primaryUrl.EndsWith("/"))
  91. primaryUrl = primaryUrl.Substring(0, primaryUrl.Length - 1);
  92. this.jsonRequestFactory = jsonRequestFactory;
  93. this.sessionId = sessionId;
  94. this.convention = convention;
  95. credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication = credentials;
  96. this.databaseName = databaseName;
  97. this.conflictListeners = conflictListeners;
  98. this.requestExecuterGetter = requestExecuterGetter;
  99. this.requestTimeMetricGetter = requestTimeMetricGetter;
  100. requestExecuterSelector = new RequestExecuterSelector(() =>
  101. requestExecuterGetter(this, databaseName, incrementReadStripe), convention);
  102. requestExecuterSelector.Select().UpdateReplicationInformationIfNeededAsync(this);
  103. }
  104. public void Dispose()
  105. {
  106. }
  107. public Task<string[]> GetIndexNamesAsync(int start, int pageSize, CancellationToken token = default(CancellationToken))
  108. {
  109. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  110. {
  111. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.IndexNames(start, pageSize), HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  112. {
  113. var json = (RavenJArray)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  114. return json.Select(x => x.Value<string>()).ToArray();
  115. }
  116. }, token);
  117. }
  118. public Task<IndexDefinition[]> GetIndexesAsync(int start, int pageSize, CancellationToken token = default(CancellationToken))
  119. {
  120. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  121. {
  122. var operationUrl = operationMetadata.Url + "/indexes/?start=" + start + "&pageSize=" + pageSize;
  123. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)))
  124. {
  125. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  126. var json = (RavenJArray)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  127. //NOTE: To review, I'm not confidence this is the correct way to deserialize the index definition
  128. return json.Select(x =>
  129. {
  130. var value = ((RavenJObject)x)["definition"].ToString();
  131. return JsonConvert.DeserializeObject<IndexDefinition>(value, new JsonToJsonConverter());
  132. }).ToArray();
  133. }
  134. }, token);
  135. }
  136. public Task<TransformerDefinition[]> GetTransformersAsync(int start, int pageSize, CancellationToken token = default(CancellationToken))
  137. {
  138. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  139. {
  140. var operationUrl = operationMetadata.Url + "/transformers?start=" + start + "&pageSize=" + pageSize;
  141. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)))
  142. {
  143. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  144. var json = (RavenJArray)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  145. //NOTE: To review, I'm not confidence this is the correct way to deserialize the transformer definition
  146. return json.Select(x => JsonConvert.DeserializeObject<TransformerDefinition>(((RavenJObject)x)["definition"].ToString(), new JsonToJsonConverter())).ToArray();
  147. }
  148. }, token);
  149. }
  150. public Task SetTransformerLockAsync(string name, TransformerLockMode lockMode, CancellationToken token = default(CancellationToken))
  151. {
  152. return ExecuteWithReplication(HttpMethod.Post, async (operationMetadata, requestTimeMetric) =>
  153. {
  154. var operationUrl = operationMetadata.Url + "/transformers/" + name + "?op=" + "lockModeChange" + "&mode=" + lockMode;
  155. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethod.Post, operationMetadata.Credentials, convention)))
  156. {
  157. request.AddOperationHeaders(OperationsHeaders);
  158. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  159. return await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  160. }
  161. }, token);
  162. }
  163. internal Task ReplicateIndexAsync(string name, CancellationToken token = default(CancellationToken))
  164. {
  165. var url = String.Format("/replication/replicate-indexes?indexName={0}", Uri.EscapeDataString(name));
  166. using (var request = CreateRequest(url, HttpMethods.Post))
  167. return request.ExecuteRawResponseAsync();
  168. }
  169. public Task ResetIndexAsync(string name, CancellationToken token = default(CancellationToken))
  170. {
  171. return ExecuteWithReplication(HttpMethods.Reset, async (operationMetadata, requestTimeMetric) =>
  172. {
  173. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/indexes/" + name, HttpMethods.Reset, operationMetadata.Credentials, convention, requestTimeMetric)))
  174. {
  175. request.AddOperationHeaders(OperationsHeaders);
  176. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  177. return await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  178. }
  179. }, token);
  180. }
  181. public Task SetIndexLockAsync(string name, IndexLockMode unLockMode, CancellationToken token = default(CancellationToken))
  182. {
  183. return ExecuteWithReplication(HttpMethod.Post, async (operationMetadata, requestTimeMetric) =>
  184. {
  185. var operationUrl = operationMetadata.Url + "/indexes/" + name + "?op=" + "lockModeChange" + "&mode=" + unLockMode;
  186. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric)))
  187. {
  188. request.AddOperationHeaders(OperationsHeaders);
  189. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  190. return await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  191. }
  192. }, token);
  193. }
  194. public Task SetIndexPriorityAsync(string name, IndexingPriority priority, CancellationToken token = default(CancellationToken))
  195. {
  196. return ExecuteWithReplication(HttpMethod.Post, async (operationMetadata, requestTimeMetric) =>
  197. {
  198. var operationUrl = operationMetadata.Url + "/indexes/set-priority/" + name + "?priority=" + priority;
  199. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationUrl, HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric)))
  200. {
  201. request.AddOperationHeaders(OperationsHeaders);
  202. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  203. return await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  204. }
  205. }, token);
  206. }
  207. public Task<string> PutIndexAsync<TDocument, TReduceResult>(string name, IndexDefinitionBuilder<TDocument, TReduceResult> indexDef, CancellationToken token = default(CancellationToken))
  208. {
  209. return PutIndexAsync(name, indexDef, false, token);
  210. }
  211. public Task<string> PutIndexAsync<TDocument, TReduceResult>(string name,
  212. IndexDefinitionBuilder<TDocument, TReduceResult> indexDef, bool overwrite = false, CancellationToken token = default(CancellationToken))
  213. {
  214. return PutIndexAsync(name, indexDef.ToIndexDefinition(convention), overwrite, token);
  215. }
  216. public Task<bool> IndexHasChangedAsync(string name, IndexDefinition indexDef, CancellationToken token = default(CancellationToken))
  217. {
  218. return ExecuteWithReplication(HttpMethod.Post, (operationMetadata, requestTimeMetric) => DirectIndexHasChangedAsync(name, indexDef, operationMetadata, requestTimeMetric, token), token);
  219. }
  220. private async Task<bool> DirectIndexHasChangedAsync(string name, IndexDefinition indexDef, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, CancellationToken token)
  221. {
  222. var requestUri = operationMetadata.Url.Indexes(name) + "?op=hasChanged";
  223. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  224. {
  225. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  226. var serializeObject = JsonConvert.SerializeObject(indexDef, Default.Converters);
  227. await request.WriteAsync(serializeObject).WithCancellation(token).ConfigureAwait(false);
  228. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  229. return result.Value<bool>("Changed");
  230. }
  231. }
  232. public Task<string> PutIndexAsync(string name, IndexDefinition indexDef, CancellationToken token = default(CancellationToken))
  233. {
  234. return PutIndexAsync(name, indexDef, false, token);
  235. }
  236. public Task<string> PutIndexAsync(string name, IndexDefinition indexDef, bool overwrite, CancellationToken token = default(CancellationToken))
  237. {
  238. return ExecuteWithReplication(HttpMethod.Put, (operationMetadata, requestTimeMetric) => DirectPutIndexAsync(name, indexDef, overwrite, operationMetadata, requestTimeMetric, token), token);
  239. }
  240. public Task<string[]> PutIndexesAsync(IndexToAdd[] indexesToAdd, CancellationToken token = default(CancellationToken))
  241. {
  242. return ExecuteWithReplication(HttpMethod.Put, (operationMetadata, requestTimeMetric) => DirectPutIndexesAsync(indexesToAdd, operationMetadata, token), token);
  243. }
  244. public Task<string[]> PutSideBySideIndexesAsync(IndexToAdd[] indexesToAdd, Etag minimumEtagBeforeReplace = null, DateTime? replaceTimeUtc = null, CancellationToken token = default(CancellationToken))
  245. {
  246. return ExecuteWithReplication(HttpMethod.Put, (operationMetadata, requestTimeMetric) => DirectPutSideBySideIndexesAsync(indexesToAdd, operationMetadata, minimumEtagBeforeReplace, replaceTimeUtc, token), token);
  247. }
  248. public Task<string> PutTransformerAsync(string name, TransformerDefinition transformerDefinition, CancellationToken token = default(CancellationToken))
  249. {
  250. return ExecuteWithReplication(HttpMethod.Put, (operationMetadata, requestTimeMetric) => DirectPutTransformerAsync(name, transformerDefinition, operationMetadata, requestTimeMetric, token), token);
  251. }
  252. public async Task<string> DirectPutIndexAsync(string name, IndexDefinition indexDef, bool overwrite, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, CancellationToken token = default(CancellationToken))
  253. {
  254. var requestUri = operationMetadata.Url + "/indexes/" + Uri.EscapeUriString(name) + "?definition=yes";
  255. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  256. {
  257. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  258. try
  259. {
  260. await request.ExecuteRequestAsync().WithCancellation(token).ConfigureAwait(false);
  261. if (overwrite == false)
  262. throw new InvalidOperationException("Cannot put index: " + name + ", index already exists");
  263. }
  264. catch (ErrorResponseException e)
  265. {
  266. if (e.StatusCode != HttpStatusCode.NotFound)
  267. throw;
  268. }
  269. }
  270. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Put, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  271. {
  272. var serializeObject = JsonConvert.SerializeObject(indexDef, Default.Converters);
  273. ErrorResponseException responseException;
  274. try
  275. {
  276. await request.WriteAsync(serializeObject).ConfigureAwait(false);
  277. var result = await request.ReadResponseJsonAsync().ConfigureAwait(false);
  278. return result.Value<string>("Index");
  279. }
  280. catch (ErrorResponseException e)
  281. {
  282. if (e.StatusCode != HttpStatusCode.BadRequest) throw;
  283. responseException = e;
  284. }
  285. var error = await responseException.TryReadErrorResponseObject(new { Error = "", Message = "", IndexDefinitionProperty = "", ProblematicText = "" }).ConfigureAwait(false);
  286. if (error == null) throw responseException;
  287. throw new IndexCompilationException(error.Message) { IndexDefinitionProperty = error.IndexDefinitionProperty, ProblematicText = error.ProblematicText };
  288. }
  289. }
  290. public async Task<string[]> DirectPutIndexesAsync(IndexToAdd[] indexesToAdd, OperationMetadata operationMetadata, CancellationToken token = default(CancellationToken))
  291. {
  292. var requestUri = operationMetadata.Url + "/indexes";
  293. return await PutIndexes(operationMetadata, token, requestUri, indexesToAdd).ConfigureAwait(false);
  294. }
  295. public async Task<string[]> DirectPutSideBySideIndexesAsync(IndexToAdd[] indexesToAdd, OperationMetadata operationMetadata, Etag minimumEtagBeforeReplace, DateTime? replaceTimeUtc, CancellationToken token = default(CancellationToken))
  296. {
  297. var sideBySideIndexes = new SideBySideIndexes
  298. {
  299. IndexesToAdd = indexesToAdd,
  300. MinimumEtagBeforeReplace = minimumEtagBeforeReplace,
  301. ReplaceTimeUtc = replaceTimeUtc
  302. };
  303. var requestUri = operationMetadata.Url + "/side-by-side-indexes";
  304. return await PutIndexes(operationMetadata, token, requestUri, sideBySideIndexes).ConfigureAwait(false);
  305. }
  306. private async Task<string[]> PutIndexes(OperationMetadata operationMetadata, CancellationToken token, string requestUri, object obj)
  307. {
  308. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Put, operationMetadata.Credentials, convention).AddOperationHeaders(OperationsHeaders)))
  309. {
  310. var serializeObject = JsonConvert.SerializeObject(obj, Default.Converters);
  311. ErrorResponseException responseException;
  312. try
  313. {
  314. await request.WriteAsync(serializeObject).WithCancellation(token).ConfigureAwait(false);
  315. var result = await request.ReadResponseJsonAsync().ConfigureAwait(false);
  316. return result
  317. .Value<RavenJArray>("Indexes")
  318. .Select(x => x.Value<string>())
  319. .ToArray();
  320. }
  321. catch (ErrorResponseException e)
  322. {
  323. if (e.StatusCode != HttpStatusCode.BadRequest)
  324. throw;
  325. responseException = e;
  326. }
  327. var error = await responseException.TryReadErrorResponseObject(new { Error = "", Message = "", IndexDefinitionProperty = "", ProblematicText = "" }).ConfigureAwait(false);
  328. if (error == null)
  329. throw responseException;
  330. throw new IndexCompilationException(error.Message) { IndexDefinitionProperty = error.IndexDefinitionProperty, ProblematicText = error.ProblematicText };
  331. }
  332. }
  333. public async Task<string> DirectPutTransformerAsync(string name, TransformerDefinition transformerDefinition, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, CancellationToken token = default(CancellationToken))
  334. {
  335. var requestUri = operationMetadata.Url + "/transformers/" + name;
  336. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Put, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  337. {
  338. var serializeObject = JsonConvert.SerializeObject(transformerDefinition, Default.Converters);
  339. ErrorResponseException responseException;
  340. try
  341. {
  342. await request.WriteAsync(serializeObject).ConfigureAwait(false);
  343. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  344. return result.Value<string>("Transformer");
  345. }
  346. catch (BadRequestException e)
  347. {
  348. throw new TransformCompilationException(e.Message);
  349. }
  350. catch (ErrorResponseException e)
  351. {
  352. if (e.StatusCode != HttpStatusCode.BadRequest)
  353. throw;
  354. responseException = e;
  355. }
  356. var error = await responseException.TryReadErrorResponseObject(new { Error = "", Message = "" }).ConfigureAwait(false);
  357. if (error == null)
  358. throw responseException;
  359. throw new TransformCompilationException(error.Message);
  360. }
  361. }
  362. public Task DeleteIndexAsync(string name, CancellationToken token = default(CancellationToken))
  363. {
  364. return ExecuteWithReplication(HttpMethod.Delete, async (operationMetadata, requestTimeMetric) =>
  365. {
  366. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.Indexes(name), HttpMethod.Delete, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(operationsHeaders)))
  367. {
  368. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  369. await request.ExecuteRequestAsync().WithCancellation(token).ConfigureAwait(false);
  370. }
  371. }, token);
  372. }
  373. public Task<Operation> DeleteByIndexAsync(string indexName, IndexQuery queryToDelete, BulkOperationOptions options = null, CancellationToken token = default(CancellationToken))
  374. {
  375. return ExecuteWithReplication(HttpMethod.Delete, async (operationMetadata, requestTimeMetric) =>
  376. {
  377. var notNullOptions = options ?? new BulkOperationOptions();
  378. string path = queryToDelete.GetIndexQueryUrl(operationMetadata.Url, indexName, "bulk_docs") + "&allowStale=" + notNullOptions.AllowStale
  379. + "&details=" + notNullOptions.RetrieveDetails;
  380. if (notNullOptions.MaxOpsPerSec != null)
  381. path += "&maxOpsPerSec=" + notNullOptions.MaxOpsPerSec;
  382. if (notNullOptions.StaleTimeout != null)
  383. path += "&staleTimeout=" + notNullOptions.StaleTimeout;
  384. token.ThrowCancellationIfNotDefault(); //maybe the operation is canceled and we can spare the request..
  385. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, path, HttpMethod.Delete, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  386. {
  387. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader);
  388. RavenJToken jsonResponse;
  389. try
  390. {
  391. jsonResponse = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  392. }
  393. catch (ErrorResponseException e)
  394. {
  395. if (e.StatusCode == HttpStatusCode.NotFound) throw new InvalidOperationException("There is no index named: " + indexName, e);
  396. throw;
  397. }
  398. // Be compatible with the response from v2.0 server
  399. var opId = ((RavenJObject)jsonResponse)["OperationId"];
  400. if (opId == null || opId.Type != JTokenType.Integer) return null;
  401. return new Operation(this, opId.Value<long>());
  402. }
  403. }, token);
  404. }
  405. public Task DeleteTransformerAsync(string name, CancellationToken token = default(CancellationToken))
  406. {
  407. return ExecuteWithReplication(HttpMethod.Delete, async (operationMetadata, requestTimeMetric) =>
  408. {
  409. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.Transformer(name), HttpMethod.Delete, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(operationsHeaders)))
  410. {
  411. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  412. await request.ExecuteRequestAsync().WithCancellation(token).ConfigureAwait(false);
  413. }
  414. }, token);
  415. }
  416. public Task<RavenJObject> PatchAsync(string key, PatchRequest[] patches, CancellationToken token = default(CancellationToken))
  417. {
  418. return PatchAsync(key, patches, null, token);
  419. }
  420. public async Task<RavenJObject> PatchAsync(string key, PatchRequest[] patches, bool ignoreMissing, CancellationToken token = default(CancellationToken))
  421. {
  422. var batchResults = await BatchAsync(new ICommandData[]
  423. {
  424. new PatchCommandData
  425. {
  426. Key = key,
  427. Patches = patches,
  428. }
  429. }, null, token).ConfigureAwait(false);
  430. if (!ignoreMissing && batchResults[0].PatchResult != null &&
  431. batchResults[0].PatchResult == PatchResult.DocumentDoesNotExists)
  432. throw new DocumentDoesNotExistsException("Document with key " + key + " does not exist.");
  433. return batchResults[0].AdditionalData;
  434. }
  435. public Task<RavenJObject> PatchAsync(string key, ScriptedPatchRequest patch, CancellationToken token = default(CancellationToken))
  436. {
  437. return PatchAsync(key, patch, null, token);
  438. }
  439. public async Task<RavenJObject> PatchAsync(string key, PatchRequest[] patches, Etag etag, CancellationToken token = default(CancellationToken))
  440. {
  441. var batchResults = await BatchAsync(new ICommandData[]
  442. {
  443. new PatchCommandData
  444. {
  445. Key = key,
  446. Patches = patches,
  447. Etag = etag,
  448. }
  449. }, null, token).ConfigureAwait(false);
  450. return batchResults[0].AdditionalData;
  451. }
  452. public async Task<RavenJObject> PatchAsync(string key, PatchRequest[] patchesToExisting,
  453. PatchRequest[] patchesToDefault, RavenJObject defaultMetadata, CancellationToken token = default(CancellationToken))
  454. {
  455. var batchResults = await BatchAsync(new ICommandData[]
  456. {
  457. new PatchCommandData
  458. {
  459. Key = key,
  460. Patches = patchesToExisting,
  461. PatchesIfMissing = patchesToDefault,
  462. Metadata = defaultMetadata
  463. }
  464. }, null, token).ConfigureAwait(false);
  465. return batchResults[0].AdditionalData;
  466. }
  467. public async Task<RavenJObject> PatchAsync(string key, ScriptedPatchRequest patch, bool ignoreMissing, CancellationToken token = default(CancellationToken))
  468. {
  469. var batchResults = await BatchAsync(new ICommandData[]
  470. {
  471. new ScriptedPatchCommandData
  472. {
  473. Key = key,
  474. Patch = patch,
  475. }
  476. }, null, token).ConfigureAwait(false);
  477. if (!ignoreMissing && batchResults[0].PatchResult != null &&
  478. batchResults[0].PatchResult == PatchResult.DocumentDoesNotExists)
  479. throw new DocumentDoesNotExistsException("Document with key " + key + " does not exist.");
  480. return batchResults[0].AdditionalData;
  481. }
  482. public async Task<RavenJObject> PatchAsync(string key, ScriptedPatchRequest patch, Etag etag, CancellationToken token = default(CancellationToken))
  483. {
  484. var batchResults = await BatchAsync(new ICommandData[]
  485. {
  486. new ScriptedPatchCommandData
  487. {
  488. Key = key,
  489. Patch = patch,
  490. Etag = etag
  491. }
  492. }, null, token).ConfigureAwait(false);
  493. return batchResults[0].AdditionalData;
  494. }
  495. public async Task<RavenJObject> PatchAsync(string key, ScriptedPatchRequest patchExisting,
  496. ScriptedPatchRequest patchDefault, RavenJObject defaultMetadata, CancellationToken token = default(CancellationToken))
  497. {
  498. var batchResults = await BatchAsync(new ICommandData[]
  499. {
  500. new ScriptedPatchCommandData
  501. {
  502. Key = key,
  503. Patch = patchExisting,
  504. PatchIfMissing = patchDefault,
  505. Metadata = defaultMetadata
  506. }
  507. }, null, token).ConfigureAwait(false);
  508. return batchResults[0].AdditionalData;
  509. }
  510. public Task<PutResult> PutAsync(string key, Etag etag, RavenJObject document, RavenJObject metadata, CancellationToken token = default(CancellationToken))
  511. {
  512. return ExecuteWithReplication(HttpMethod.Put, (operationMetadata, requestTimeMetric) => DirectPutAsync(operationMetadata, requestTimeMetric, key, etag, document, metadata, token), token);
  513. }
  514. private async Task<PutResult> DirectPutAsync(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string key, Etag etag, RavenJObject document, RavenJObject metadata, CancellationToken token = default(CancellationToken))
  515. {
  516. if (metadata == null)
  517. metadata = new RavenJObject();
  518. var method = String.IsNullOrEmpty(key) ? HttpMethod.Post : HttpMethod.Put;
  519. if (etag != null)
  520. metadata[Constants.MetadataEtagField] = new RavenJValue((string)etag);
  521. else
  522. metadata.Remove(Constants.MetadataEtagField);
  523. if (key != null)
  524. key = Uri.EscapeDataString(key);
  525. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/docs/" + key, method, metadata, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  526. {
  527. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  528. ErrorResponseException responseException;
  529. try
  530. {
  531. await request.WriteAsync(document).WithCancellation(token).ConfigureAwait(false);
  532. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  533. if (result == null)
  534. {
  535. throw new InvalidOperationException("Got null response from the server after doing a put on " + key + ", something is very wrong. Probably a garbled response.");
  536. }
  537. return convention.CreateSerializer().Deserialize<PutResult>(new RavenJTokenReader(result));
  538. }
  539. catch (ErrorResponseException e)
  540. {
  541. if (e.StatusCode != HttpStatusCode.Conflict) throw;
  542. responseException = e;
  543. }
  544. throw FetchConcurrencyException(responseException);
  545. }
  546. }
  547. public IAsyncDatabaseCommands ForDatabase(string database)
  548. {
  549. return ForDatabaseInternal(database);
  550. }
  551. public IAsyncDatabaseCommands ForSystemDatabase()
  552. {
  553. return ForSystemDatabaseInternal();
  554. }
  555. internal AsyncServerClient ForDatabaseInternal(string database)
  556. {
  557. if (database == Constants.SystemDatabase)
  558. return ForSystemDatabaseInternal();
  559. var databaseUrl = MultiDatabase.GetRootDatabaseUrl(Url).ForDatabase(database);
  560. if (databaseUrl == Url)
  561. return this;
  562. return new AsyncServerClient(databaseUrl, convention,
  563. credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication,
  564. jsonRequestFactory, sessionId,
  565. requestExecuterGetter, requestTimeMetricGetter, database, conflictListeners, false)
  566. {
  567. operationsHeaders = operationsHeaders
  568. };
  569. }
  570. internal AsyncServerClient ForSystemDatabaseInternal()
  571. {
  572. var databaseUrl = MultiDatabase.GetRootDatabaseUrl(Url);
  573. if (databaseUrl == Url)
  574. return this;
  575. return new AsyncServerClient(databaseUrl, convention, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, jsonRequestFactory, sessionId, requestExecuterGetter, requestTimeMetricGetter, null, conflictListeners, false) { operationsHeaders = operationsHeaders };
  576. }
  577. public NameValueCollection OperationsHeaders
  578. {
  579. get { return operationsHeaders; }
  580. set { operationsHeaders = value; }
  581. }
  582. public IAsyncGlobalAdminDatabaseCommands GlobalAdmin { get { return new AsyncAdminServerClient(this); } }
  583. public IAsyncAdminDatabaseCommands Admin { get { return new AsyncAdminServerClient(this); } }
  584. public Task<JsonDocument> GetAsync(string key, CancellationToken token = default(CancellationToken))
  585. {
  586. EnsureIsNotNullOrEmpty(key, "key");
  587. return ExecuteWithReplication(HttpMethod.Get, (operationMetadata, requestTimeMetric) => DirectGetAsync(operationMetadata, requestTimeMetric, key, token), token);
  588. }
  589. public Task<TransformerDefinition> GetTransformerAsync(string name, CancellationToken token = default(CancellationToken))
  590. {
  591. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  592. {
  593. try
  594. {
  595. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.Transformer(name), HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  596. {
  597. var transformerDefinitionJson = (RavenJObject)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  598. var value = transformerDefinitionJson.Value<RavenJObject>("Transformer");
  599. return convention.CreateSerializer().Deserialize<TransformerDefinition>(new RavenJTokenReader(value));
  600. }
  601. }
  602. catch (ErrorResponseException we)
  603. {
  604. if (we.StatusCode == HttpStatusCode.NotFound)
  605. return null;
  606. throw;
  607. }
  608. }, token);
  609. }
  610. public Task<IndexDefinition> GetIndexAsync(string name, CancellationToken token = default(CancellationToken))
  611. {
  612. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  613. {
  614. try
  615. {
  616. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.IndexDefinition(name), HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  617. {
  618. var indexDefinitionJson = (RavenJObject)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  619. var value = indexDefinitionJson.Value<RavenJObject>("Index");
  620. return convention.CreateSerializer().Deserialize<IndexDefinition>(new RavenJTokenReader(value));
  621. }
  622. }
  623. catch (ErrorResponseException we)
  624. {
  625. if (we.StatusCode == HttpStatusCode.NotFound)
  626. return null;
  627. throw;
  628. }
  629. }, token);
  630. }
  631. public Task<IndexingPerformanceStatistics[]> GetIndexingPerformanceStatisticsAsync()
  632. {
  633. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  634. {
  635. var url = operationMetadata.Url.IndexingPerformanceStatistics();
  636. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, url, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  637. {
  638. var indexingPerformanceStatisticsJson = (RavenJArray)await request.ReadResponseJsonAsync().ConfigureAwait(false);
  639. var results = new IndexingPerformanceStatistics[indexingPerformanceStatisticsJson.Length];
  640. for (var i = 0; i < indexingPerformanceStatisticsJson.Length; i++)
  641. {
  642. var stats = (RavenJObject)indexingPerformanceStatisticsJson[i];
  643. results[i] = stats.Deserialize<IndexingPerformanceStatistics>(convention);
  644. }
  645. return results;
  646. }
  647. });
  648. }
  649. private async Task<JsonDocument> DirectGetAsync(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string key, CancellationToken token)
  650. {
  651. if (key.Length > 127)
  652. {
  653. // avoid hitting UrlSegmentMaxLength limits in Http.sys
  654. var multiLoadResult = await DirectGetAsync(operationMetadata, requestTimeMetric, new[] { key }, new string[0], null, new Dictionary<string, RavenJToken>(), false, token).WithCancellation(token).ConfigureAwait(false);
  655. var result = multiLoadResult.Results.FirstOrDefault();
  656. if (result == null)
  657. return null;
  658. return SerializationHelper.RavenJObjectToJsonDocument(result);
  659. }
  660. var metadata = new RavenJObject();
  661. AddTransactionInformation(metadata);
  662. var createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this,
  663. (operationMetadata.Url + "/docs?id=" + Uri.EscapeDataString(key)),
  664. HttpMethod.Get,
  665. metadata,
  666. operationMetadata.Credentials,
  667. convention,
  668. requestTimeMetric);
  669. using (var request = jsonRequestFactory.CreateHttpJsonRequest(
  670. createHttpJsonRequestParams.AddOperationHeaders(OperationsHeaders))
  671. .AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  672. {
  673. Task<JsonDocument> resolveConflictTask;
  674. try
  675. {
  676. var requestJson = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  677. var docKey = request.ResponseHeaders.Get(Constants.DocumentIdFieldName) ?? key;
  678. docKey = Uri.UnescapeDataString(docKey);
  679. request.ResponseHeaders.Remove(Constants.DocumentIdFieldName);
  680. var deserializeJsonDocument = SerializationHelper.DeserializeJsonDocument(docKey, requestJson, request.ResponseHeaders, request.ResponseStatusCode);
  681. return deserializeJsonDocument;
  682. }
  683. catch (ErrorResponseException e)
  684. {
  685. switch (e.StatusCode)
  686. {
  687. case HttpStatusCode.NotFound:
  688. return null;
  689. case HttpStatusCode.Conflict:
  690. resolveConflictTask = ResolveConflict(e.ResponseString, e.Etag, operationMetadata, requestTimeMetric, key, token);
  691. break;
  692. default:
  693. throw;
  694. }
  695. }
  696. return await resolveConflictTask.WithCancellation(token).ConfigureAwait(false);
  697. }
  698. }
  699. private async Task<JsonDocument> ResolveConflict(string httpResponse, Etag etag, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string key, CancellationToken token)
  700. {
  701. var conflicts = new StringReader(httpResponse);
  702. var conflictsDoc = RavenJObject.Load(new RavenJsonTextReader(conflicts));
  703. var result =
  704. await TryResolveConflictOrCreateConcurrencyException(operationMetadata, requestTimeMetric, key, conflictsDoc, etag, token).ConfigureAwait(false);
  705. if (result != null)
  706. throw result;
  707. return await DirectGetAsync(operationMetadata, requestTimeMetric, key, token).ConfigureAwait(false);
  708. }
  709. public Task<MultiLoadResult> GetAsync(string[] keys, string[] includes, string transformer = null,
  710. Dictionary<string, RavenJToken> transformerParameters = null, bool metadataOnly = false, CancellationToken token = default(CancellationToken))
  711. {
  712. return ExecuteWithReplication(HttpMethod.Get, (operationMetadata, requestTimeMetric) => DirectGetAsync(operationMetadata, requestTimeMetric, keys, includes, transformer, transformerParameters, metadataOnly, token), token);
  713. }
  714. private async Task<MultiLoadResult> DirectGetAsync(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string[] keys, string[] includes, string transformer,
  715. Dictionary<string, RavenJToken> transformerParameters, bool metadataOnly, CancellationToken token = default(CancellationToken))
  716. {
  717. var path = operationMetadata.Url + "/queries/?";
  718. if (metadataOnly)
  719. path += "&metadata-only=true";
  720. if (includes != null && includes.Length > 0)
  721. {
  722. path += string.Join("&", includes.Select(x => "include=" + x).ToArray());
  723. }
  724. if (string.IsNullOrEmpty(transformer) == false)
  725. path += "&transformer=" + transformer;
  726. if (transformerParameters != null)
  727. {
  728. path = transformerParameters.Aggregate(path,
  729. (current, transformerParam) =>
  730. current + ("&" + string.Format("tp-{0}={1}", transformerParam.Key, transformerParam.Value)));
  731. }
  732. var metadata = new RavenJObject();
  733. AddTransactionInformation(metadata);
  734. var uniqueIds = new HashSet<string>(keys);
  735. // if it is too big, we drop to POST (note that means that we can't use the HTTP cache any longer)
  736. // we are fine with that, requests to load > 128 items are going to be rare
  737. var isGet = uniqueIds.Sum(x => x.Length) < 1024;
  738. var method = isGet ? HttpMethod.Get : HttpMethod.Post;
  739. if (isGet)
  740. {
  741. path += "&" + string.Join("&", uniqueIds.Select(x => "id=" + Uri.EscapeDataString(x)).ToArray());
  742. }
  743. var createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, path, method, metadata, operationMetadata.Credentials, convention)
  744. .AddOperationHeaders(OperationsHeaders);
  745. using (var request = jsonRequestFactory.CreateHttpJsonRequest(createHttpJsonRequestParams)
  746. .AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  747. {
  748. if (isGet == false)
  749. {
  750. await request.WriteAsync(new RavenJArray(uniqueIds)).WithCancellation(token).ConfigureAwait(false);
  751. }
  752. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  753. return await CompleteMultiGetAsync(operationMetadata, requestTimeMetric, keys, includes, transformer, transformerParameters, result, token).ConfigureAwait(false);
  754. }
  755. }
  756. private async Task<MultiLoadResult> CompleteMultiGetAsync(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string[] keys, string[] includes, string transformer,
  757. Dictionary<string, RavenJToken> transformerParameters, RavenJToken result, CancellationToken token = default(CancellationToken))
  758. {
  759. ErrorResponseException responseException;
  760. try
  761. {
  762. var uniqueKeys = new HashSet<string>(keys);
  763. var results = result
  764. .Value<RavenJArray>("Results")
  765. .Select(x => x as RavenJObject)
  766. .ToList();
  767. var documents = results
  768. .Where(x => x != null && x.ContainsKey("@metadata") && x["@metadata"].Value<string>("@id") != null)
  769. .ToDictionary(x => x["@metadata"].Value<string>("@id"), x => x, StringComparer.OrdinalIgnoreCase);
  770. if (results.Count >= uniqueKeys.Count)
  771. {
  772. for (var i = 0; i < uniqueKeys.Count; i++)
  773. {
  774. var key = keys[i];
  775. if (documents.ContainsKey(key))
  776. continue;
  777. documents.Add(key, results[i]);
  778. }
  779. }
  780. var multiLoadResult = new MultiLoadResult
  781. {
  782. Includes = result.Value<RavenJArray>("Includes").Cast<RavenJObject>().ToList(),
  783. Results = documents.Count == 0 ? results : keys.Select(key => documents.ContainsKey(key) ? documents[key] : null).ToList()
  784. };
  785. var docResults = multiLoadResult.Results.Concat(multiLoadResult.Includes);
  786. return
  787. await
  788. RetryOperationBecauseOfConflict(operationMetadata, requestTimeMetric, docResults, multiLoadResult,
  789. () => DirectGetAsync(operationMetadata, requestTimeMetric, keys, includes, transformer, transformerParameters, false, token), token: token).ConfigureAwait(false);
  790. }
  791. catch (ErrorResponseException e)
  792. {
  793. if (e.StatusCode != HttpStatusCode.Conflict)
  794. throw;
  795. responseException = e;
  796. }
  797. throw FetchConcurrencyException(responseException);
  798. }
  799. public Task<JsonDocument[]> GetDocumentsAsync(int start, int pageSize, bool metadataOnly = false, CancellationToken token = default(CancellationToken))
  800. {
  801. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  802. {
  803. var result = await GetDocumentsInternalAsync(start, null, pageSize, operationMetadata, requestTimeMetric, metadataOnly, token).ConfigureAwait(false);
  804. return result.Cast<RavenJObject>()
  805. .ToJsonDocuments()
  806. .ToArray();
  807. }, token);
  808. }
  809. public Task<JsonDocument[]> GetDocumentsAsync(Etag fromEtag, int pageSize, bool metadataOnly = false, CancellationToken token = default(CancellationToken))
  810. {
  811. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  812. {
  813. var result = await GetDocumentsInternalAsync(null, fromEtag, pageSize, operationMetadata, requestTimeMetric, metadataOnly, token).ConfigureAwait(false);
  814. return result.Cast<RavenJObject>()
  815. .ToJsonDocuments()
  816. .ToArray();
  817. }, token);
  818. }
  819. public async Task<RavenJArray> GetDocumentsInternalAsync(int? start, Etag fromEtag, int pageSize, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, bool metadataOnly = false, CancellationToken token = default(CancellationToken))
  820. {
  821. var requestUri = Url + "/docs/?";
  822. if (start.HasValue && start.Value > 0)
  823. {
  824. requestUri += "start=" + start;
  825. }
  826. else if (fromEtag != null)
  827. {
  828. requestUri += "etag=" + fromEtag;
  829. }
  830. requestUri += "&pageSize=" + pageSize;
  831. if (metadataOnly)
  832. requestUri += "&metadata-only=true";
  833. var @params = new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)
  834. .AddOperationHeaders(OperationsHeaders);
  835. using (var request = jsonRequestFactory.CreateHttpJsonRequest(@params))
  836. {
  837. return (RavenJArray)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  838. }
  839. }
  840. public Task<Operation> UpdateByIndexAsync(string indexName, IndexQuery queryToUpdate, ScriptedPatchRequest patch, BulkOperationOptions options = null, CancellationToken token = default(CancellationToken))
  841. {
  842. var notNullOptions = options ?? new BulkOperationOptions();
  843. var requestData = RavenJObject.FromObject(patch).ToString(Formatting.Indented);
  844. return UpdateByIndexImpl(indexName, queryToUpdate, notNullOptions, requestData, HttpMethods.Eval, token);
  845. }
  846. public Task<Operation> UpdateByIndexAsync(string indexName, IndexQuery queryToUpdate, PatchRequest[] patchRequests,
  847. BulkOperationOptions options = null, CancellationToken token = default(CancellationToken))
  848. {
  849. var notNullOptions = options ?? new BulkOperationOptions();
  850. var requestData = new RavenJArray(patchRequests.Select(x => x.ToJson())).ToString(Formatting.Indented);
  851. return UpdateByIndexImpl(indexName, queryToUpdate, notNullOptions, requestData, HttpMethods.Patch, token);
  852. }
  853. public async Task<MultiLoadResult> MoreLikeThisAsync(MoreLikeThisQuery query, CancellationToken token = default(CancellationToken))
  854. {
  855. var requestUrl = query.GetRequestUri();
  856. EnsureIsNotNullOrEmpty(requestUrl, "url");
  857. var result = await ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  858. {
  859. var metadata = new RavenJObject();
  860. AddTransactionInformation(metadata);
  861. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + requestUrl, HttpMethod.Get, metadata, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  862. {
  863. return await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  864. }
  865. }, token).ConfigureAwait(false);
  866. return ((RavenJObject)result).Deserialize<MultiLoadResult>(convention);
  867. }
  868. public Task<long> NextIdentityForAsync(string name, CancellationToken token = default(CancellationToken))
  869. {
  870. return ExecuteWithReplication(HttpMethod.Post, async (operationMetadata, requestTimeMetric) =>
  871. {
  872. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/identity/next?name=" + Uri.EscapeDataString(name), HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  873. {
  874. var readResponseJson = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  875. return readResponseJson.Value<long>("Value");
  876. }
  877. }, token);
  878. }
  879. public Task<long> SeedIdentityForAsync(string name, long value, CancellationToken token = default(CancellationToken))
  880. {
  881. return ExecuteWithReplication(HttpMethod.Post, async (operationMetadata, requestTimeMetric) =>
  882. {
  883. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/identity/seed?name=" + Uri.EscapeDataString(name) + "&value=" + Uri.EscapeDataString(value.ToString()), HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  884. {
  885. var readResponseJson = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  886. return readResponseJson.Value<long>("Value");
  887. }
  888. }, token);
  889. }
  890. public Task SeedIdentitiesAsync(List<KeyValuePair<string, long>> identities, CancellationToken token = default(CancellationToken))
  891. {
  892. return ExecuteWithReplication(HttpMethod.Post, async (operationMetadata,requestTimeMetric) =>
  893. {
  894. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/identity/seed/bulk", HttpMethod.Post, operationMetadata.Credentials, convention).AddOperationHeaders(OperationsHeaders)))
  895. {
  896. await request.WriteAsync(RavenJToken.FromObject(identities)).ConfigureAwait(false);
  897. await request.ExecuteRequestAsync().WithCancellation(token).ConfigureAwait(false);
  898. }
  899. }, token);
  900. }
  901. private Task<Operation> UpdateByIndexImpl(string indexName, IndexQuery queryToUpdate, BulkOperationOptions options, String requestData, HttpMethod method, CancellationToken token = default(CancellationToken))
  902. {
  903. return ExecuteWithReplication(method, async (operationMetadata, requestTimeMetric) =>
  904. {
  905. var notNullOptions = options ?? new BulkOperationOptions();
  906. string path = queryToUpdate.GetIndexQueryUrl(operationMetadata.Url, indexName, "bulk_docs") + "&allowStale=" + notNullOptions.AllowStale
  907. + "&maxOpsPerSec=" + notNullOptions.MaxOpsPerSec + "&details=" + notNullOptions.RetrieveDetails;
  908. if (notNullOptions.StaleTimeout != null)
  909. path += "&staleTimeout=" + notNullOptions.StaleTimeout;
  910. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, path, method, operationMetadata.Credentials, convention, requestTimeMetric)))
  911. {
  912. request.AddOperationHeaders(OperationsHeaders);
  913. await request.WriteAsync(requestData).ConfigureAwait(false);
  914. RavenJToken jsonResponse;
  915. try
  916. {
  917. jsonResponse = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  918. }
  919. catch (ErrorResponseException e)
  920. {
  921. if (e.StatusCode == HttpStatusCode.NotFound) throw new InvalidOperationException("There is no index named: " + indexName);
  922. throw;
  923. }
  924. return new Operation(this, jsonResponse.Value<long>("OperationId"));
  925. }
  926. }, token);
  927. }
  928. public Task<FacetResults> GetFacetsAsync(string index, IndexQuery query, string facetSetupDoc, int start = 0,
  929. int? pageSize = null, CancellationToken token = default(CancellationToken))
  930. {
  931. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  932. {
  933. var requestUri = operationMetadata.Url + string.Format("/facets/{0}?facetDoc={1}{2}&facetStart={3}&facetPageSize={4}",
  934. Uri.EscapeUriString(index),
  935. Uri.EscapeDataString(facetSetupDoc),
  936. query.GetMinimalQueryString(),
  937. start,
  938. pageSize);
  939. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  940. {
  941. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  942. var cachedRequestDetails = jsonRequestFactory.ConfigureCaching(requestUri, (key, val) => request.AddHeader(key, val));
  943. request.CachedRequestDetails = cachedRequestDetails.CachedRequest;
  944. request.SkipServerCheck = cachedRequestDetails.SkipServerCheck;
  945. var json = (RavenJObject)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  946. return json.JsonDeserialization<FacetResults>();
  947. }
  948. }, token);
  949. }
  950. public Task<FacetResults[]> GetMultiFacetsAsync(FacetQuery[] facetedQueries, CancellationToken token = default(CancellationToken))
  951. {
  952. var multiGetReuestItems = facetedQueries.Select(x =>
  953. {
  954. string addition;
  955. if (x.FacetSetupDoc != null)
  956. {
  957. addition = "facetDoc=" + x.FacetSetupDoc;
  958. return new GetRequest()
  959. {
  960. Url = "/facets/" + x.IndexName,
  961. Query = string.Format("{0}&facetStart={1}&facetPageSize={2}&{3}",
  962. x.Query.GetQueryString(),
  963. x.PageStart,
  964. x.PageSize,
  965. addition)
  966. };
  967. }
  968. var serializedFacets = SerializeFacetsToFacetsJsonString(x.Facets);
  969. if (serializedFacets.Length < (32 * 1024) - 1)
  970. {
  971. addition = "facets=" + Uri.EscapeDataString(serializedFacets);
  972. return new GetRequest()
  973. {
  974. Url = "/facets/" + x.IndexName,
  975. Query = string.Format("{0}&facetStart={1}&facetPageSize={2}&{3}",
  976. x.Query.GetQueryString(),
  977. x.PageStart,
  978. x.PageSize,
  979. addition)
  980. };
  981. }
  982. return new GetRequest()
  983. {
  984. Url = "/facets/" + x.IndexName,
  985. Query = string.Format("{0}&facetStart={1}&facetPageSize={2}",
  986. x.Query.GetQueryString(),
  987. x.PageStart,
  988. x.PageSize),
  989. Method = "POST",
  990. Content = serializedFacets
  991. };
  992. }).ToArray();
  993. var results = MultiGetAsync(multiGetReuestItems, token).ContinueWith(x =>
  994. {
  995. var facetResults = new FacetResults[x.Result.Length];
  996. var getResponses = x.Result;
  997. for (var facetResultCounter = 0; facetResultCounter < facetResults.Length; facetResultCounter++)
  998. {
  999. var getResponse = getResponses[facetResultCounter];
  1000. if (getResponse.RequestHasErrors())
  1001. {
  1002. throw new InvalidOperationException("Got an error from server, status code: " + getResponse.Status +
  1003. Environment.NewLine + getResponse.Result);
  1004. }
  1005. var curFacetDoc = getResponse.Result;
  1006. facetResults[facetResultCounter] = curFacetDoc.JsonDeserialization<FacetResults>();
  1007. }
  1008. return facetResults;
  1009. }, token);
  1010. return results;
  1011. }
  1012. public Task<FacetResults> GetFacetsAsync(string index, IndexQuery query, List<Facet> facets, int start = 0,
  1013. int? pageSize = null,
  1014. CancellationToken token = default(CancellationToken))
  1015. {
  1016. var facetsJson = SerializeFacetsToFacetsJsonString(facets);
  1017. var method = facetsJson.Length > 1024 ? HttpMethod.Post : HttpMethod.Get;
  1018. if (method == HttpMethod.Post)
  1019. {
  1020. return GetMultiFacetsAsync(new[]
  1021. {
  1022. new FacetQuery
  1023. {
  1024. Facets = facets,
  1025. IndexName = index,
  1026. Query = query,
  1027. PageSize = pageSize,
  1028. PageStart = start
  1029. }
  1030. }).ContinueWith(x => x.Result.FirstOrDefault());
  1031. }
  1032. return ExecuteWithReplication(method, async (operationMetadata, requestTimeMetric) =>
  1033. {
  1034. var requestUri = operationMetadata.Url + string.Format("/facets/{0}?{1}&facetStart={2}&facetPageSize={3}",
  1035. Uri.EscapeUriString(index),
  1036. query.GetQueryString(),
  1037. start,
  1038. pageSize);
  1039. if (method == HttpMethod.Get)
  1040. requestUri += "&facets=" + Uri.EscapeDataString(facetsJson);
  1041. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, method, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  1042. {
  1043. if (method != HttpMethod.Get)
  1044. {
  1045. await request.WriteAsync(facetsJson).ConfigureAwait(false);
  1046. }
  1047. var json = await request.ReadResponseJsonAsync().ConfigureAwait(false);
  1048. return json.JsonDeserialization<FacetResults>();
  1049. }
  1050. });
  1051. }
  1052. internal static string SerializeFacetsToFacetsJsonString(List<Facet> facets)
  1053. {
  1054. var ravenJArray = (RavenJArray)RavenJToken.FromObject(facets, new JsonSerializer
  1055. {
  1056. NullValueHandling = NullValueHandling.Ignore,
  1057. DefaultValueHandling = DefaultValueHandling.Ignore,
  1058. });
  1059. foreach (var facet in ravenJArray)
  1060. {
  1061. var obj = (RavenJObject)facet;
  1062. if (obj.Value<string>("Name") == obj.Value<string>("DisplayName"))
  1063. obj.Remove("DisplayName");
  1064. var jArray = obj.Value<RavenJArray>("Ranges");
  1065. if (jArray != null && jArray.Length == 0)
  1066. obj.Remove("Ranges");
  1067. }
  1068. string facetsJson = ravenJArray.ToString(Formatting.None);
  1069. return facetsJson;
  1070. }
  1071. public Task<LogItem[]> GetLogsAsync(bool errorsOnly, CancellationToken token = default(CancellationToken))
  1072. {
  1073. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  1074. {
  1075. var requestUri = Url + "/logs";
  1076. if (errorsOnly)
  1077. requestUri += "?type=error";
  1078. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)))
  1079. {
  1080. request.AddOperationHeaders(OperationsHeaders);
  1081. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1082. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1083. return convention.CreateSerializer().Deserialize<LogItem[]>(new RavenJTokenReader(result));
  1084. }
  1085. }, token);
  1086. }
  1087. public async Task<LicensingStatus> GetLicenseStatusAsync(CancellationToken token = default(CancellationToken))
  1088. {
  1089. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, (MultiDatabase.GetRootDatabaseUrl(Url) + "/license/status"), HttpMethod.Get, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention, requestTimeMetricGetter(Url))))
  1090. {
  1091. request.AddOperationHeaders(OperationsHeaders);
  1092. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1093. return convention.CreateSerializer().Deserialize<LicensingStatus>(new RavenJTokenReader(result));
  1094. }
  1095. }
  1096. public async Task<BuildNumber> GetBuildNumberAsync(CancellationToken token = default(CancellationToken))
  1097. {
  1098. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, (Url + "/build/version"), HttpMethod.Get, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention, requestTimeMetricGetter(Url))))
  1099. {
  1100. request.AddOperationHeaders(OperationsHeaders);
  1101. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1102. return convention.CreateSerializer().Deserialize<BuildNumber>(new RavenJTokenReader(result));
  1103. }
  1104. }
  1105. public async Task<IndexMergeResults> GetIndexMergeSuggestionsAsync(CancellationToken token = default(CancellationToken))
  1106. {
  1107. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, (Url + "/debug/suggest-index-merge"), HttpMethod.Get, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention, requestTimeMetricGetter(Url))))
  1108. {
  1109. request.AddOperationHeaders(OperationsHeaders);
  1110. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1111. return convention.CreateSerializer().Deserialize<IndexMergeResults>(new RavenJTokenReader(result));
  1112. }
  1113. }
  1114. public Task<JsonDocument[]> StartsWithAsync(string keyPrefix, string matches, int start, int pageSize,
  1115. RavenPagingInformation pagingInformation = null,
  1116. bool metadataOnly = false,
  1117. string exclude = null,
  1118. string transformer = null,
  1119. Dictionary<string, RavenJToken> transformerParameters = null,
  1120. string skipAfter = null, CancellationToken token = default(CancellationToken))
  1121. {
  1122. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  1123. {
  1124. var metadata = new RavenJObject();
  1125. AddTransactionInformation(metadata);
  1126. var actualStart = start;
  1127. var nextPage = pagingInformation != null && pagingInformation.IsForPreviousPage(start, pageSize);
  1128. if (nextPage)
  1129. actualStart = pagingInformation.NextPageStart;
  1130. var actualUrl = string.Format("{0}/docs?startsWith={1}&matches={5}&exclude={4}&start={2}&pageSize={3}", operationMetadata.Url,
  1131. Uri.EscapeDataString(keyPrefix), actualStart.ToInvariantString(), pageSize.ToInvariantString(), exclude, matches);
  1132. if (metadataOnly)
  1133. actualUrl += "&metadata-only=true";
  1134. if (string.IsNullOrEmpty(skipAfter) == false)
  1135. actualUrl += "&skipAfter=" + Uri.EscapeDataString(skipAfter);
  1136. if (string.IsNullOrEmpty(transformer) == false)
  1137. {
  1138. actualUrl += "&transformer=" + transformer;
  1139. if (transformerParameters != null)
  1140. {
  1141. actualUrl = transformerParameters.Aggregate(actualUrl,
  1142. (current, transformerParamater) =>
  1143. current + ("&" + string.Format("tp-{0}={1}", transformerParamater.Key, transformerParamater.Value)));
  1144. }
  1145. }
  1146. if (nextPage)
  1147. actualUrl += "&next-page=true";
  1148. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, actualUrl, HttpMethod.Get, metadata, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  1149. {
  1150. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1151. var result = (RavenJArray)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1152. int nextPageStart;
  1153. if (pagingInformation != null && int.TryParse(request.ResponseHeaders[Constants.NextPageStart], out nextPageStart)) pagingInformation.Fill(start, pageSize, nextPageStart);
  1154. var docResults = result.OfType<RavenJObject>().ToList();
  1155. var startsWithResults = SerializationHelper.RavenJObjectsToJsonDocuments(docResults.Select(x => (RavenJObject)x.CloneToken())).ToArray();
  1156. return await RetryOperationBecauseOfConflict(operationMetadata, requestTimeMetric, docResults, startsWithResults, () =>
  1157. StartsWithAsync(keyPrefix, matches, start, pageSize, pagingInformation, metadataOnly, exclude, transformer, transformerParameters, skipAfter, token), conflictedResultId =>
  1158. new ConflictException("Conflict detected on " + conflictedResultId.Substring(0, conflictedResultId.IndexOf("/conflicts/", StringComparison.OrdinalIgnoreCase)) +
  1159. ", conflict must be resolved before the document will be accessible")
  1160. { ConflictedVersionIds = new[] { conflictedResultId } }, token).ConfigureAwait(false);
  1161. }
  1162. }, token);
  1163. }
  1164. public Task<GetResponse[]> MultiGetAsync(GetRequest[] requests, CancellationToken token = default(CancellationToken))
  1165. {
  1166. return MultiGetAsyncInternal(requests, token, null, null);
  1167. }
  1168. private Task<GetResponse[]> MultiGetAsyncInternal(GetRequest[] requests, CancellationToken token, Reference<OperationMetadata> operationMetadataRef, Reference<IRequestTimeMetric> requestTimeMetricRef)
  1169. {
  1170. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) => // logical GET even though the actual request is a POST
  1171. {
  1172. if (operationMetadataRef != null)
  1173. operationMetadataRef.Value = operationMetadata;
  1174. if (requestTimeMetricRef != null)
  1175. requestTimeMetricRef.Value = requestTimeMetric;
  1176. var multiGetOperation = new MultiGetOperation(this, convention, operationMetadata.Url, requests);
  1177. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, multiGetOperation.RequestUri, HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  1178. {
  1179. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1180. var requestsForServer = multiGetOperation.PreparingForCachingRequest(jsonRequestFactory);
  1181. var postedData = JsonConvert.SerializeObject(requestsForServer);
  1182. if (multiGetOperation.CanFullyCache(jsonRequestFactory, request, postedData))
  1183. {
  1184. var cachedResponses = multiGetOperation.HandleCachingResponse(new GetResponse[requests.Length], jsonRequestFactory);
  1185. return cachedResponses;
  1186. }
  1187. await request.WriteAsync(postedData).ConfigureAwait(false);
  1188. var result = await request.ReadResponseJsonAsync().ConfigureAwait(false);
  1189. var responses = convention.CreateSerializer().Deserialize<GetResponse[]>(new RavenJTokenReader(result));
  1190. await multiGetOperation.TryResolveConflictOrCreateConcurrencyException(responses, (key, conflictDoc, etag) => TryResolveConflictOrCreateConcurrencyException(operationMetadata, requestTimeMetric, key, conflictDoc, etag, token)).ConfigureAwait(false);
  1191. return multiGetOperation.HandleCachingResponse(responses, jsonRequestFactory);
  1192. }
  1193. }, token);
  1194. }
  1195. public Task<QueryResult> QueryAsync(string index, IndexQuery query, string[] includes = null, bool metadataOnly = false, bool indexEntriesOnly = false, CancellationToken token = default(CancellationToken))
  1196. {
  1197. var method = (query.Query == null || query.Query.Length <= convention.MaxLengthOfQueryUsingGetUrl)
  1198. ? HttpMethod.Get : HttpMethod.Post;
  1199. if (method == HttpMethod.Post)
  1200. {
  1201. return QueryAsyncAsPost(index, query, includes, metadataOnly, indexEntriesOnly, token);
  1202. }
  1203. return QueryAsyncAsGet(index, query, includes, metadataOnly, indexEntriesOnly, method, token);
  1204. }
  1205. private Task<QueryResult> QueryAsyncAsGet(string index, IndexQuery query, string[] includes, bool metadataOnly, bool indexEntriesOnly, HttpMethod method, CancellationToken token = default(CancellationToken))
  1206. {
  1207. return ExecuteWithReplication(method, async (operationMetadata, requestTimeMetric) =>
  1208. {
  1209. EnsureIsNotNullOrEmpty(index, "index");
  1210. string path = query.GetIndexQueryUrl(operationMetadata.Url, index, "indexes", includeQuery: method == HttpMethod.Get);
  1211. if (metadataOnly)
  1212. path += "&metadata-only=true";
  1213. if (indexEntriesOnly)
  1214. path += "&debug=entries";
  1215. if (includes != null && includes.Length > 0)
  1216. {
  1217. path += "&" + string.Join("&", includes.Select(x => "include=" + x).ToArray());
  1218. }
  1219. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, path, method, operationMetadata.Credentials, convention, requestTimeMetric) { AvoidCachingRequest = query.DisableCaching }.AddOperationHeaders(OperationsHeaders)))
  1220. {
  1221. RavenJObject json = null;
  1222. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1223. json = (RavenJObject)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1224. ErrorResponseException responseException;
  1225. try
  1226. {
  1227. if (json == null) throw new InvalidOperationException("Got empty response from the server for the following request: " + request.Url);
  1228. var queryResult = SerializationHelper.ToQueryResult(json, request.ResponseHeaders.GetEtagHeader(), request.ResponseHeaders.Get("Temp-Request-Time"), request.Size);
  1229. if (request.ResponseStatusCode == HttpStatusCode.NotModified)
  1230. queryResult.DurationMilliseconds = -1;
  1231. var docResults = queryResult.Results.Concat(queryResult.Includes);
  1232. return await RetryOperationBecauseOfConflict(operationMetadata, requestTimeMetric, docResults, queryResult, () =>
  1233. QueryAsync(index, query, includes, metadataOnly, indexEntriesOnly, token), conflictedResultId =>
  1234. new ConflictException("Conflict detected on " + conflictedResultId.Substring(0, conflictedResultId.IndexOf("/conflicts/", StringComparison.OrdinalIgnoreCase)) +
  1235. ", conflict must be resolved before the document will be accessible")
  1236. { ConflictedVersionIds = new[] { conflictedResultId } }, token).ConfigureAwait(false);
  1237. }
  1238. catch (ErrorResponseException e)
  1239. {
  1240. if (e.StatusCode == HttpStatusCode.NotFound)
  1241. {
  1242. var text = e.ResponseString;
  1243. if (text.Contains("maxQueryString")) throw new ErrorResponseException(e, text);
  1244. throw new ErrorResponseException(e, "There is no index named: " + index);
  1245. }
  1246. responseException = e;
  1247. }
  1248. if (HandleException(responseException)) return null;
  1249. throw responseException;
  1250. }
  1251. }, token);
  1252. }
  1253. private async Task<QueryResult> QueryAsyncAsPost(string index, IndexQuery query, string[] includes, bool metadataOnly, bool indexEntriesOnly, CancellationToken token = default(CancellationToken))
  1254. {
  1255. var stringBuilder = new StringBuilder();
  1256. query.AppendQueryString(stringBuilder);
  1257. if (metadataOnly)
  1258. stringBuilder.Append("&metadata-only=true");
  1259. if (indexEntriesOnly)
  1260. stringBuilder.Append("&debug=entries");
  1261. if (includes != null && includes.Length > 0)
  1262. {
  1263. includes.ForEach(include => stringBuilder.Append("&include=").Append(include));
  1264. }
  1265. try
  1266. {
  1267. var operationMetadataRef = new Reference<OperationMetadata>();
  1268. var requestTimeMetricRef = new Reference<IRequestTimeMetric>();
  1269. var result = await MultiGetAsyncInternal(new[]
  1270. {
  1271. new GetRequest
  1272. {
  1273. Query = stringBuilder.ToString(),
  1274. Url = "/indexes/" + index
  1275. }
  1276. }, token, operationMetadataRef, requestTimeMetricRef).ConfigureAwait(false);
  1277. var json = (RavenJObject)result[0].Result;
  1278. var queryResult = SerializationHelper.ToQueryResult(json, result[0].GetEtagHeader(), result[0].Headers["Temp-Request-Time"], -1);
  1279. var docResults = queryResult.Results.Concat(queryResult.Includes);
  1280. return await RetryOperationBecauseOfConflict(operationMetadataRef.Value, requestTimeMetricRef.Value, docResults, queryResult,
  1281. () => QueryAsync(index, query, includes, metadataOnly, indexEntriesOnly, token),
  1282. conflictedResultId => new ConflictException("Conflict detected on " + conflictedResultId.Substring(0, conflictedResultId.IndexOf("/conflicts/", StringComparison.OrdinalIgnoreCase)) +
  1283. ", conflict must be resolved before the document will be accessible")
  1284. { ConflictedVersionIds = new[] { conflictedResultId } },
  1285. token).ConfigureAwait(false);
  1286. }
  1287. catch (OperationCanceledException oce)
  1288. {
  1289. throw new TaskCanceledException(string.Format("Canceled Index {0} Query", index), oce);
  1290. }
  1291. catch (Exception e)
  1292. {
  1293. var errorResponseException = e as ErrorResponseException;
  1294. if (errorResponseException != null)
  1295. {
  1296. if (errorResponseException.StatusCode == HttpStatusCode.NotFound)
  1297. {
  1298. var text = errorResponseException.ResponseString;
  1299. if (text.Contains("maxQueryString")) throw new ErrorResponseException(errorResponseException, text);
  1300. throw new ErrorResponseException(errorResponseException, "There is no index named: " + index);
  1301. }
  1302. if (HandleException(errorResponseException)) return null;
  1303. }
  1304. throw;
  1305. }
  1306. }
  1307. /// <summary>
  1308. /// Attempts to handle an exception raised when receiving a response from the server
  1309. /// </summary>
  1310. /// <param name="e">The exception to handle</param>
  1311. /// <returns>returns true if the exception is handled, false if it should be thrown</returns>
  1312. private bool HandleException(ErrorResponseException e)
  1313. {
  1314. if (e.StatusCode == HttpStatusCode.InternalServerError)
  1315. {
  1316. var content = e.ResponseString;
  1317. var json = RavenJObject.Load(new JsonTextReader(new StringReader(content)));
  1318. var error = json.Deserialize<ServerRequestError>(convention);
  1319. throw new ErrorResponseException(e, error.Error);
  1320. }
  1321. return false;
  1322. }
  1323. public Task<SuggestionQueryResult> SuggestAsync(string index, SuggestionQuery suggestionQuery, CancellationToken token = default(CancellationToken))
  1324. {
  1325. if (suggestionQuery == null)
  1326. throw new ArgumentNullException("suggestionQuery");
  1327. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  1328. {
  1329. var requestUri = operationMetadata.Url + string.Format("/suggest/{0}?term={1}&field={2}&max={3}&popularity={4}",
  1330. Uri.EscapeUriString(index),
  1331. Uri.EscapeDataString(suggestionQuery.Term),
  1332. Uri.EscapeDataString(suggestionQuery.Field),
  1333. Uri.EscapeDataString(suggestionQuery.MaxSuggestions.ToInvariantString()),
  1334. suggestionQuery.Popularity);
  1335. if (suggestionQuery.Accuracy.HasValue)
  1336. requestUri += "&accuracy=" + suggestionQuery.Accuracy.Value.ToInvariantString();
  1337. if (suggestionQuery.Distance.HasValue)
  1338. requestUri += "&distance=" + suggestionQuery.Distance;
  1339. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUri, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  1340. {
  1341. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1342. var json = (RavenJObject)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1343. return new SuggestionQueryResult { Suggestions = ((RavenJArray)json["Suggestions"]).Select(x => x.Value<string>()).ToArray(), };
  1344. }
  1345. }, token);
  1346. }
  1347. public Task<BatchResult[]> BatchAsync(IEnumerable<ICommandData> commandDatas, BatchOptions options = null, CancellationToken token = default(CancellationToken))
  1348. {
  1349. return ExecuteWithReplication(HttpMethod.Post, async (operationMetadata, requestTimeMetric) =>
  1350. {
  1351. var metadata = new RavenJObject();
  1352. AddTransactionInformation(metadata);
  1353. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/bulk_docs", HttpMethod.Post, metadata, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  1354. {
  1355. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1356. if (options?.WaitForReplicas == true)
  1357. request.AddHeader("Raven-Write-Assurance", options.NumberOfReplicasToWaitFor + ";" + options.WaitForReplicasTimout+";" +
  1358. options.ThrowOnTimeoutInWaitForReplicas);
  1359. if (options?.WaitForIndexes == true)
  1360. {
  1361. var headerVal = options.ThrowOnTimeoutInWaitForIndexes + ";" + options.WaitForIndexesTimeout +
  1362. ";" + string.Join(";", options.WaitForSpecificIndexes ?? new string[0]);
  1363. request.AddHeader("Raven-Wait-Indexes", headerVal);
  1364. }
  1365. var serializedData = commandDatas.Select(x => x.ToJson()).ToList();
  1366. var jArray = new RavenJArray(serializedData);
  1367. ErrorResponseException responseException;
  1368. try
  1369. {
  1370. await request.WriteAsync(jArray).WithCancellation(token).ConfigureAwait(false);
  1371. var response = (RavenJArray)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1372. if (response == null)
  1373. {
  1374. throw new InvalidOperationException("Got null response from the server after doing a batch, something is very wrong. Probably a garbled response. Posted: " + jArray);
  1375. }
  1376. return convention.CreateSerializer().Deserialize<BatchResult[]>(new RavenJTokenReader(response));
  1377. }
  1378. catch (ErrorResponseException e)
  1379. {
  1380. if (e.StatusCode != HttpStatusCode.Conflict) throw;
  1381. responseException = e;
  1382. }
  1383. throw FetchConcurrencyException(responseException);
  1384. }
  1385. }, token);
  1386. }
  1387. private static ConcurrencyException FetchConcurrencyException(ErrorResponseException e)
  1388. {
  1389. var text = e.ResponseString;
  1390. var errorResults = JsonConvert.DeserializeAnonymousType(text, new
  1391. {
  1392. url = (string)null,
  1393. actualETag = Etag.Empty,
  1394. expectedETag = Etag.Empty,
  1395. error = (string)null
  1396. });
  1397. return new ConcurrencyException(errorResults.error)
  1398. {
  1399. ActualETag = errorResults.actualETag,
  1400. ExpectedETag = errorResults.expectedETag
  1401. };
  1402. }
  1403. private void AddTransactionInformation(RavenJObject metadata)
  1404. {
  1405. if (convention.EnlistInDistributedTransactions == false)
  1406. return;
  1407. #if !DNXCORE50
  1408. var transactionInformation = RavenTransactionAccessor.GetTransactionInformation();
  1409. if (transactionInformation == null)
  1410. return;
  1411. string txInfo = string.Format("{0}, {1}", transactionInformation.Id, transactionInformation.Timeout);
  1412. metadata["Raven-Transaction-Information"] = new RavenJValue(txInfo);
  1413. #endif
  1414. }
  1415. private static void EnsureIsNotNullOrEmpty(string key, string argName)
  1416. {
  1417. if (string.IsNullOrEmpty(key))
  1418. throw new ArgumentException("Key cannot be null or empty", argName);
  1419. }
  1420. public async Task<DatabaseStatistics> GetStatisticsAsync(CancellationToken token = default(CancellationToken))
  1421. {
  1422. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, Url.Stats(), HttpMethod.Get, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention, requestTimeMetricGetter(Url))))
  1423. {
  1424. var json = (RavenJObject)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1425. return json.Deserialize<DatabaseStatistics>(convention);
  1426. }
  1427. }
  1428. public async Task<UserInfo> GetUserInfoAsync(CancellationToken token = default(CancellationToken))
  1429. {
  1430. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, Url.UserInfo(), HttpMethod.Get, PrimaryCredentials, convention)))
  1431. {
  1432. var json = (RavenJObject)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1433. return json.Deserialize<UserInfo>(convention);
  1434. }
  1435. }
  1436. public async Task<UserPermission> GetUserPermissionAsync(string database, bool readOnly, CancellationToken token = default(CancellationToken))
  1437. {
  1438. if (string.IsNullOrEmpty(database))
  1439. {
  1440. throw new ArgumentException("database name cannot be null or empty");
  1441. }
  1442. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, Url.UserPermission(database, readOnly), HttpMethod.Get, PrimaryCredentials, convention)))
  1443. {
  1444. var json = (RavenJObject)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1445. return json.Deserialize<UserPermission>(convention);
  1446. }
  1447. }
  1448. [Obsolete("Use RavenFS instead.")]
  1449. public Task<AttachmentInformation[]> GetAttachmentsAsync(int start, Etag startEtag, int pageSize, CancellationToken token = default(CancellationToken))
  1450. {
  1451. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  1452. {
  1453. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/static/?pageSize=" + pageSize + "&etag=" + startEtag + "&start=" + start, HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  1454. {
  1455. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1456. var json = (RavenJArray)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1457. return convention.CreateSerializer().Deserialize<AttachmentInformation[]>(new RavenJTokenReader(json));
  1458. }
  1459. }, token);
  1460. }
  1461. [Obsolete("Use RavenFS instead.")]
  1462. public Task PutAttachmentAsync(string key, Etag etag, Stream data, RavenJObject metadata, CancellationToken token = default(CancellationToken))
  1463. {
  1464. return ExecuteWithReplication(HttpMethod.Put, async (operationMetadata, requestTimeMetric) =>
  1465. {
  1466. if (metadata == null)
  1467. metadata = new RavenJObject();
  1468. if (etag != null)
  1469. metadata[Constants.MetadataEtagField] = new RavenJValue((string)etag);
  1470. else
  1471. metadata.Remove(Constants.MetadataEtagField);
  1472. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, Static(operationMetadata.Url, key), HttpMethod.Put, metadata, operationMetadata.Credentials, convention, requestTimeMetric)))
  1473. {
  1474. request.AddOperationHeaders(OperationsHeaders);
  1475. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1476. await request.WriteAsync(data).WithCancellation(token).ConfigureAwait(false);
  1477. }
  1478. }, token);
  1479. }
  1480. [Obsolete("Use RavenFS instead.")]
  1481. public Task<Attachment> GetAttachmentAsync(string key, CancellationToken token = default(CancellationToken))
  1482. {
  1483. EnsureIsNotNullOrEmpty(key, "key");
  1484. return ExecuteWithReplication(HttpMethod.Get, (operationMetadata, requestTimeMetric) => DirectGetAttachmentAsync(key, operationMetadata, requestTimeMetric, HttpMethod.Get, token), token);
  1485. }
  1486. [Obsolete("Use RavenFS instead.")]
  1487. public Task<Attachment> HeadAttachmentAsync(string key, CancellationToken token = default(CancellationToken))
  1488. {
  1489. EnsureIsNotNullOrEmpty(key, "key");
  1490. return ExecuteWithReplication(HttpMethod.Head, (operationMetadata, requestTimeMetric) => DirectGetAttachmentAsync(key, operationMetadata, requestTimeMetric, HttpMethod.Head, token), token);
  1491. }
  1492. [Obsolete("Use RavenFS instead.")]
  1493. private async Task<Attachment> DirectGetAttachmentAsync(string key, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, HttpMethod method, CancellationToken token = default(CancellationToken))
  1494. {
  1495. var metadata = new RavenJObject();
  1496. AddTransactionInformation(metadata);
  1497. var createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, (operationMetadata.Url + "/static/" + key), method, metadata, operationMetadata.Credentials, convention, requestTimeMetric);
  1498. using (var request = jsonRequestFactory.CreateHttpJsonRequest(createHttpJsonRequestParams.AddOperationHeaders(OperationsHeaders)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  1499. {
  1500. ErrorResponseException responseException;
  1501. try
  1502. {
  1503. var result = await request.ReadResponseBytesAsync().WithCancellation(token).ConfigureAwait(false);
  1504. request.HandleReplicationStatusChanges(request.ResponseHeaders, Url, operationMetadata.Url);
  1505. if (method == HttpMethod.Get)
  1506. {
  1507. var memoryStream = new MemoryStream(result);
  1508. return new Attachment
  1509. {
  1510. Key = key,
  1511. Data = () => memoryStream,
  1512. Size = result.Length,
  1513. Etag = request.ResponseHeaders.GetEtagHeader(),
  1514. Metadata = request.ResponseHeaders.FilterHeadersAttachment()
  1515. };
  1516. }
  1517. else
  1518. {
  1519. return new Attachment
  1520. {
  1521. Key = key,
  1522. Data = () =>
  1523. {
  1524. throw new InvalidOperationException("Cannot get attachment data because it was loaded using: " + method);
  1525. },
  1526. #if !DNXCORE50
  1527. Size = int.Parse(request.ResponseHeaders["Content-Length"]),
  1528. #else
  1529. Size = int.Parse(request.ResponseHeaders["Raven-Content-Length"]),
  1530. #endif
  1531. Etag = request.ResponseHeaders.GetEtagHeader(),
  1532. Metadata = request.ResponseHeaders.FilterHeadersAttachment()
  1533. };
  1534. }
  1535. }
  1536. catch (ErrorResponseException e)
  1537. {
  1538. if (e.StatusCode == HttpStatusCode.NotFound) return null;
  1539. if (e.StatusCode != HttpStatusCode.Conflict) throw;
  1540. responseException = e;
  1541. }
  1542. using (var stream = await responseException.Response.GetResponseStreamWithHttpDecompression().WithCancellation(token).ConfigureAwait(false))
  1543. {
  1544. string[] conflictIds;
  1545. if (method == HttpMethod.Get)
  1546. {
  1547. var conflictsDoc = stream.ToJObject();
  1548. conflictIds = conflictsDoc.Value<RavenJArray>("Conflicts").Select(x => x.Value<string>()).ToArray();
  1549. }
  1550. else
  1551. {
  1552. conflictIds = new[] { "Cannot get conflict ids in HEAD requesT" };
  1553. }
  1554. throw new ConflictException("Conflict detected on " + key + ", conflict must be resolved before the attachment will be accessible") { ConflictedVersionIds = conflictIds, Etag = responseException.Etag };
  1555. }
  1556. }
  1557. }
  1558. [Obsolete("Use RavenFS instead.")]
  1559. public Task DeleteAttachmentAsync(string key, Etag etag, CancellationToken token = default(CancellationToken))
  1560. {
  1561. return ExecuteWithReplication(HttpMethod.Delete, (operationMetadata, requestTimeMetric) =>
  1562. {
  1563. var metadata = new RavenJObject();
  1564. if (etag != null)
  1565. metadata[Constants.MetadataEtagField] = new RavenJValue((string)etag);
  1566. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, Static(operationMetadata.Url, key), HttpMethod.Delete, metadata, operationMetadata.Credentials, convention, requestTimeMetric)))
  1567. {
  1568. request.AddOperationHeaders(OperationsHeaders);
  1569. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1570. return request.ExecuteRequestAsync();
  1571. }
  1572. }, token);
  1573. }
  1574. public static string Static(string url, string key)
  1575. {
  1576. return url + "/static/" + Uri.EscapeUriString(key);
  1577. }
  1578. public IDisposable DisableAllCaching()
  1579. {
  1580. return jsonRequestFactory.DisableAllCaching();
  1581. }
  1582. public Task<string[]> GetTermsAsync(string index, string field, string fromValue, int pageSize, CancellationToken token = default(CancellationToken))
  1583. {
  1584. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  1585. {
  1586. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.Terms(index, field, fromValue, pageSize), HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  1587. {
  1588. var result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1589. var json = ((RavenJArray)result);
  1590. return json.Select(x => x.Value<string>()).ToArray();
  1591. }
  1592. }, token);
  1593. }
  1594. public ProfilingInformation ProfilingInformation
  1595. {
  1596. get { return profilingInformation; }
  1597. }
  1598. public event EventHandler<FailoverStatusChangedEventArgs> FailoverStatusChanged
  1599. {
  1600. add { RequestExecuter.FailoverStatusChanged += value; }
  1601. remove { RequestExecuter.FailoverStatusChanged -= value; }
  1602. }
  1603. public IDisposable ForceReadFromMaster()
  1604. {
  1605. return RequestExecuter.ForceReadFromMaster();
  1606. }
  1607. public Task<JsonDocumentMetadata> HeadAsync(string key, CancellationToken token = default(CancellationToken))
  1608. {
  1609. EnsureIsNotNullOrEmpty(key, "key");
  1610. return ExecuteWithReplication(HttpMethod.Head, (u, rtm) => DirectHeadAsync(u, rtm, key, token), token);
  1611. }
  1612. public async Task<IAsyncEnumerator<RavenJObject>> StreamExportAsync(ExportOptions options, CancellationToken cancellationToken = default(CancellationToken))
  1613. {
  1614. var path = "/smuggler/export";
  1615. var request = CreateRequest(path, HttpMethod.Post);
  1616. request.RemoveAuthorizationHeader();
  1617. var tokenRetriever = new SingleAuthTokenRetriever(this, jsonRequestFactory, convention, OperationsHeaders, new OperationMetadata(Url, PrimaryCredentials, null));
  1618. var token = await tokenRetriever.GetToken().WithCancellation(cancellationToken).ConfigureAwait(false);
  1619. try
  1620. {
  1621. token = await tokenRetriever.ValidateThatWeCanUseToken(token).WithCancellation(cancellationToken).ConfigureAwait(false);
  1622. }
  1623. catch (Exception e)
  1624. {
  1625. request.Dispose();
  1626. throw new InvalidOperationException(
  1627. "Could not authenticate token for export streaming, if you are using ravendb in IIS make sure you have Anonymous Authentication enabled in the IIS configuration",
  1628. e);
  1629. }
  1630. request.AddOperationHeader("Single-Use-Auth-Token", token);
  1631. HttpResponseMessage response;
  1632. try
  1633. {
  1634. response = await request.ExecuteRawResponseAsync(RavenJObject.FromObject(options))
  1635. .WithCancellation(cancellationToken)
  1636. .ConfigureAwait(false);
  1637. await response.AssertNotFailingResponse().WithCancellation(cancellationToken).ConfigureAwait(false);
  1638. }
  1639. catch (Exception)
  1640. {
  1641. request.Dispose();
  1642. throw;
  1643. }
  1644. return new YieldStreamResults(request, await response.GetResponseStreamWithHttpDecompression().ConfigureAwait(false));
  1645. }
  1646. public Task<IAsyncEnumerator<RavenJObject>> StreamQueryAsync(string index, IndexQuery query, Reference<QueryHeaderInformation> queryHeaderInfo, CancellationToken token = default(CancellationToken))
  1647. {
  1648. return ExecuteWithReplication(HttpMethod.Get, (operationMetadata, requestTimeMetric) => DirectStreamQueryAsync(index, query, queryHeaderInfo, operationMetadata, requestTimeMetric, token), token);
  1649. }
  1650. private async Task<IAsyncEnumerator<RavenJObject>> DirectStreamQueryAsync(string index, IndexQuery query, Reference<QueryHeaderInformation> queryHeaderInfo, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, CancellationToken cancellationToken = default(CancellationToken))
  1651. {
  1652. EnsureIsNotNullOrEmpty(index, "index");
  1653. string path;
  1654. HttpMethod method;
  1655. if (query.Query != null && query.Query.Length > convention.MaxLengthOfQueryUsingGetUrl)
  1656. {
  1657. path = query.GetIndexQueryUrl(operationMetadata.Url, index, "streams/query", includePageSizeEvenIfNotExplicitlySet: false, includeQuery: false);
  1658. method = HttpMethod.Post;
  1659. }
  1660. else
  1661. {
  1662. method = HttpMethod.Get;
  1663. path = query.GetIndexQueryUrl(operationMetadata.Url, index, "streams/query", includePageSizeEvenIfNotExplicitlySet: false);
  1664. }
  1665. var request = jsonRequestFactory
  1666. .CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, path, method, operationMetadata.Credentials, convention, requestTimeMetric)
  1667. .AddOperationHeaders(OperationsHeaders))
  1668. .AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1669. request.RemoveAuthorizationHeader();
  1670. var tokenRetriever = new SingleAuthTokenRetriever(this, jsonRequestFactory, convention, OperationsHeaders, operationMetadata);
  1671. var token = await tokenRetriever.GetToken().WithCancellation(cancellationToken).ConfigureAwait(false);
  1672. try
  1673. {
  1674. token = await tokenRetriever.ValidateThatWeCanUseToken(token).WithCancellation(cancellationToken).ConfigureAwait(false);
  1675. }
  1676. catch (Exception e)
  1677. {
  1678. request.Dispose();
  1679. throw new InvalidOperationException(
  1680. "Could not authenticate token for query streaming, if you are using ravendb in IIS make sure you have Anonymous Authentication enabled in the IIS configuration",
  1681. e);
  1682. }
  1683. request.AddOperationHeader("Single-Use-Auth-Token", token);
  1684. HttpResponseMessage response;
  1685. try
  1686. {
  1687. if (method == HttpMethod.Post)
  1688. {
  1689. response = await request.ExecuteRawResponseAsync(query.Query)
  1690. .WithCancellation(cancellationToken)
  1691. .ConfigureAwait(false);
  1692. }
  1693. else
  1694. {
  1695. response = await request.ExecuteRawResponseAsync()
  1696. .WithCancellation(cancellationToken)
  1697. .ConfigureAwait(false);
  1698. }
  1699. await response.AssertNotFailingResponse().WithCancellation(cancellationToken).ConfigureAwait(false);
  1700. }
  1701. catch (Exception e)
  1702. {
  1703. request.Dispose();
  1704. if (index.StartsWith("dynamic/", StringComparison.OrdinalIgnoreCase) && request.ResponseStatusCode == HttpStatusCode.NotFound)
  1705. {
  1706. throw new InvalidOperationException(
  1707. @"StreamQuery does not support querying dynamic indexes. It is designed to be used with large data-sets and is unlikely to return all data-set after 15 sec of indexing, like Query() does.",
  1708. e);
  1709. }
  1710. throw;
  1711. }
  1712. queryHeaderInfo.Value = new QueryHeaderInformation
  1713. {
  1714. Index = response.Headers.GetFirstValue("Raven-Index"),
  1715. IndexTimestamp = DateTime.ParseExact(response.Headers.GetFirstValue("Raven-Index-Timestamp"), Default.DateTimeFormatsToRead,
  1716. CultureInfo.InvariantCulture, DateTimeStyles.None),
  1717. IndexEtag = Etag.Parse(response.Headers.GetFirstValue("Raven-Index-Etag")),
  1718. ResultEtag = Etag.Parse(response.Headers.GetFirstValue("Raven-Result-Etag")),
  1719. IsStale = bool.Parse(response.Headers.GetFirstValue("Raven-Is-Stale")),
  1720. TotalResults = int.Parse(response.Headers.GetFirstValue("Raven-Total-Results"))
  1721. };
  1722. return new YieldStreamResults(request, await response.GetResponseStreamWithHttpDecompression().ConfigureAwait(false));
  1723. }
  1724. public class YieldStreamResults : IAsyncEnumerator<RavenJObject>
  1725. {
  1726. private readonly HttpJsonRequest request;
  1727. private readonly int start;
  1728. private readonly int pageSize;
  1729. private readonly RavenPagingInformation pagingInformation;
  1730. private readonly Stream stream;
  1731. private readonly StreamReader streamReader;
  1732. private readonly JsonTextReaderAsync reader;
  1733. private bool complete;
  1734. private bool wasInitialized;
  1735. private readonly Func<JsonTextReaderAsync, bool> customizedEndResult;
  1736. public YieldStreamResults(HttpJsonRequest request, Stream stream, int start = 0, int pageSize = 0, RavenPagingInformation pagingInformation = null, Func<JsonTextReaderAsync, bool> customizedEndResult = null)
  1737. {
  1738. this.request = request;
  1739. this.start = start;
  1740. this.pageSize = pageSize;
  1741. this.pagingInformation = pagingInformation;
  1742. this.stream = stream;
  1743. this.customizedEndResult = customizedEndResult;
  1744. streamReader = new StreamReader(stream);
  1745. reader = new JsonTextReaderAsync(streamReader);
  1746. }
  1747. private async Task InitAsync()
  1748. {
  1749. if (await reader.ReadAsync().ConfigureAwait(false) == false || reader.TokenType != JsonToken.StartObject)
  1750. throw new InvalidOperationException("Unexpected data at start of stream");
  1751. if (await reader.ReadAsync().ConfigureAwait(false) == false || reader.TokenType != JsonToken.PropertyName || Equals("Results", reader.Value) == false)
  1752. throw new InvalidOperationException("Unexpected data at stream 'Results' property name");
  1753. if (await reader.ReadAsync().ConfigureAwait(false) == false || reader.TokenType != JsonToken.StartArray)
  1754. throw new InvalidOperationException("Unexpected data at 'Results', could not find start results array");
  1755. }
  1756. public void Dispose()
  1757. {
  1758. try
  1759. {
  1760. reader.Close();
  1761. }
  1762. catch (Exception)
  1763. {
  1764. }
  1765. try
  1766. {
  1767. #if !DNXCORE50
  1768. streamReader.Close();
  1769. #else
  1770. streamReader.Dispose();
  1771. #endif
  1772. }
  1773. catch (Exception)
  1774. {
  1775. }
  1776. try
  1777. {
  1778. #if !DNXCORE50
  1779. stream.Close();
  1780. #else
  1781. stream.Dispose();
  1782. #endif
  1783. }
  1784. catch (Exception)
  1785. {
  1786. }
  1787. try
  1788. {
  1789. request.Dispose();
  1790. }
  1791. catch (Exception)
  1792. {
  1793. }
  1794. }
  1795. public async Task<bool> MoveNextAsync()
  1796. {
  1797. if (complete)
  1798. {
  1799. // to parallel IEnumerable<T>, subsequent calls to MoveNextAsync after it has returned false should
  1800. // also return false, rather than throwing
  1801. return false;
  1802. }
  1803. if (wasInitialized == false)
  1804. {
  1805. await InitAsync().ConfigureAwait(false);
  1806. wasInitialized = true;
  1807. }
  1808. if (await reader.ReadAsync().ConfigureAwait(false) == false)
  1809. throw new InvalidOperationException("Unexpected end of data");
  1810. if (reader.TokenType == JsonToken.EndArray)
  1811. {
  1812. complete = true;
  1813. await TryReadNextPageStart().ConfigureAwait(false);
  1814. await EnsureValidEndOfResponse().ConfigureAwait(false);
  1815. this.Dispose();
  1816. return false;
  1817. }
  1818. Current = (RavenJObject)await RavenJToken.ReadFromAsync(reader).ConfigureAwait(false);
  1819. return true;
  1820. }
  1821. private async Task TryReadNextPageStart()
  1822. {
  1823. if (!(await reader.ReadAsync().ConfigureAwait(false)) || reader.TokenType != JsonToken.PropertyName)
  1824. return;
  1825. switch ((string)reader.Value)
  1826. {
  1827. case "NextPageStart":
  1828. var nextPageStart = await reader.ReadAsInt32().ConfigureAwait(false);
  1829. if (pagingInformation == null)
  1830. return;
  1831. if (nextPageStart.HasValue == false)
  1832. throw new InvalidOperationException("Unexpected end of data");
  1833. pagingInformation.Fill(start, pageSize, nextPageStart.Value);
  1834. break;
  1835. case "Error":
  1836. var err = await reader.ReadAsString().ConfigureAwait(false);
  1837. throw new InvalidOperationException("Server error" + Environment.NewLine + err);
  1838. default:
  1839. if (customizedEndResult != null && customizedEndResult(reader))
  1840. break;
  1841. throw new InvalidOperationException("Unexpected property name: " + reader.Value);
  1842. }
  1843. }
  1844. private async Task EnsureValidEndOfResponse()
  1845. {
  1846. if (reader.TokenType != JsonToken.EndObject && await reader.ReadAsync().ConfigureAwait(false) == false)
  1847. throw new InvalidOperationException("Unexpected end of response - missing EndObject token");
  1848. if (reader.TokenType != JsonToken.EndObject)
  1849. throw new InvalidOperationException(string.Format("Unexpected token type at the end of the response: {0}. Error: {1}", reader.TokenType, streamReader.ReadToEnd()));
  1850. var remainingContent = await streamReader.ReadToEndAsync().ConfigureAwait(false);
  1851. if (string.IsNullOrEmpty(remainingContent) == false)
  1852. throw new InvalidOperationException("Server error: " + remainingContent);
  1853. }
  1854. public RavenJObject Current { get; private set; }
  1855. }
  1856. public async Task<IAsyncEnumerator<RavenJObject>> StreamDocsAsync(
  1857. Etag fromEtag = null, string startsWith = null,
  1858. string matches = null, int start = 0,
  1859. int pageSize = int.MaxValue,
  1860. string exclude = null,
  1861. RavenPagingInformation pagingInformation = null,
  1862. string skipAfter = null,
  1863. string transformer = null,
  1864. Dictionary<string, RavenJToken> transformerParameters = null,
  1865. CancellationToken token = default(CancellationToken))
  1866. {
  1867. if (fromEtag != null && startsWith != null)
  1868. throw new InvalidOperationException("Either fromEtag or startsWith must be null, you can't specify both");
  1869. if (fromEtag != null) // etags does not match between servers
  1870. return await DirectStreamDocsAsync(fromEtag, null, matches, start, pageSize, exclude, pagingInformation, new OperationMetadata(Url, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, null), requestTimeMetricGetter(Url), skipAfter, transformer, transformerParameters, token).ConfigureAwait(false);
  1871. return await ExecuteWithReplication(HttpMethod.Get, (operationMetadata, requestTimeMetric) => DirectStreamDocsAsync(null, startsWith, matches, start, pageSize, exclude, pagingInformation, operationMetadata, requestTimeMetric, skipAfter, transformer, transformerParameters, token), token).ConfigureAwait(false);
  1872. }
  1873. private async Task<IAsyncEnumerator<RavenJObject>> DirectStreamDocsAsync(Etag fromEtag, string startsWith, string matches, int start, int pageSize, string exclude, RavenPagingInformation pagingInformation, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string skipAfter, string transformer, Dictionary<string, RavenJToken> transformerParameters, CancellationToken cancellationToken = default(CancellationToken))
  1874. {
  1875. if (fromEtag != null && startsWith != null)
  1876. throw new InvalidOperationException("Either fromEtag or startsWith must be null, you can't specify both");
  1877. var sb = new StringBuilder(operationMetadata.Url).Append("/streams/docs?");
  1878. if (fromEtag != null)
  1879. {
  1880. sb.Append("etag=").Append(fromEtag).Append("&");
  1881. }
  1882. else
  1883. {
  1884. if (startsWith != null)
  1885. {
  1886. sb.Append("startsWith=").Append(Uri.EscapeDataString(startsWith)).Append("&");
  1887. }
  1888. if (matches != null)
  1889. {
  1890. sb.Append("matches=").Append(Uri.EscapeDataString(matches)).Append("&");
  1891. }
  1892. if (exclude != null)
  1893. {
  1894. sb.Append("exclude=").Append(Uri.EscapeDataString(exclude)).Append("&");
  1895. }
  1896. if (skipAfter != null)
  1897. {
  1898. sb.Append("skipAfter=").Append(Uri.EscapeDataString(skipAfter)).Append("&");
  1899. }
  1900. }
  1901. if (string.IsNullOrEmpty(transformer) == false)
  1902. sb.Append("transformer=").Append(Uri.EscapeDataString(transformer)).Append("&");
  1903. if (transformerParameters != null && transformerParameters.Count > 0)
  1904. {
  1905. foreach (var pair in transformerParameters)
  1906. {
  1907. var parameterName = pair.Key;
  1908. var parameterValue = pair.Value;
  1909. sb.AppendFormat("tp-{0}={1}", parameterName, parameterValue).Append("&");
  1910. }
  1911. }
  1912. var actualStart = start;
  1913. var nextPage = pagingInformation != null && pagingInformation.IsForPreviousPage(start, pageSize);
  1914. if (nextPage)
  1915. actualStart = pagingInformation.NextPageStart;
  1916. if (actualStart != 0)
  1917. sb.Append("start=").Append(actualStart).Append("&");
  1918. if (pageSize != int.MaxValue)
  1919. sb.Append("pageSize=").Append(pageSize).Append("&");
  1920. if (nextPage)
  1921. sb.Append("next-page=true").Append("&");
  1922. var request = jsonRequestFactory
  1923. .CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, sb.ToString(), HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric)
  1924. .AddOperationHeaders(OperationsHeaders))
  1925. .AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1926. request.RemoveAuthorizationHeader();
  1927. var tokenRetriever = new SingleAuthTokenRetriever(this, jsonRequestFactory, convention, OperationsHeaders, operationMetadata);
  1928. var token = await tokenRetriever.GetToken().WithCancellation(cancellationToken).ConfigureAwait(false);
  1929. try
  1930. {
  1931. token = await tokenRetriever.ValidateThatWeCanUseToken(token).WithCancellation(cancellationToken).ConfigureAwait(false);
  1932. }
  1933. catch (Exception e)
  1934. {
  1935. request.Dispose();
  1936. throw new InvalidOperationException(
  1937. "Could not authenticate token for query streaming, if you are using ravendb in IIS make sure you have Anonymous Authentication enabled in the IIS configuration",
  1938. e);
  1939. }
  1940. request.AddOperationHeader("Single-Use-Auth-Token", token);
  1941. HttpResponseMessage response;
  1942. try
  1943. {
  1944. response = await request.ExecuteRawResponseAsync()
  1945. .WithCancellation(cancellationToken)
  1946. .ConfigureAwait(false);
  1947. await response.AssertNotFailingResponse().WithCancellation(cancellationToken).ConfigureAwait(false);
  1948. }
  1949. catch (Exception)
  1950. {
  1951. request.Dispose();
  1952. throw;
  1953. }
  1954. return new YieldStreamResults(request, await response.GetResponseStreamWithHttpDecompression().WithCancellation(cancellationToken).ConfigureAwait(false), start, pageSize, pagingInformation);
  1955. }
  1956. public Task DeleteAsync(string key, Etag etag, CancellationToken token = default(CancellationToken))
  1957. {
  1958. EnsureIsNotNullOrEmpty(key, "key");
  1959. return ExecuteWithReplication(HttpMethod.Delete, async (operationMetadata, requestTimeMetric) =>
  1960. {
  1961. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url.Doc(key), HttpMethod.Delete, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(operationsHeaders)))
  1962. {
  1963. request.AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader );
  1964. if (etag != null)
  1965. request.AddHeader("If-None-Match", etag);
  1966. try
  1967. {
  1968. await request.ExecuteRequestAsync().WithCancellation(token).ConfigureAwait(false);
  1969. }
  1970. catch (ErrorResponseException e)
  1971. {
  1972. if (e.StatusCode != HttpStatusCode.Conflict)
  1973. throw;
  1974. throw FetchConcurrencyException(e);
  1975. }
  1976. }
  1977. }, token);
  1978. }
  1979. public string UrlFor(string documentKey)
  1980. {
  1981. return Url + "/docs/" + documentKey;
  1982. }
  1983. public ILowLevelBulkInsertOperation GetBulkInsertOperation(BulkInsertOptions options, IDatabaseChanges changes)
  1984. {
  1985. if (options.ChunkedBulkInsertOptions != null)
  1986. return new ChunkedRemoteBulkInsertOperation(options, this, changes);
  1987. return new RemoteBulkInsertOperation(options, this, changes);
  1988. }
  1989. private async Task<JsonDocumentMetadata> DirectHeadAsync(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string key, CancellationToken token = default(CancellationToken))
  1990. {
  1991. var metadata = new RavenJObject();
  1992. AddTransactionInformation(metadata);
  1993. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/docs/" + key, HttpMethod.Head, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  1994. {
  1995. try
  1996. {
  1997. await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  1998. return SerializationHelper.DeserializeJsonDocumentMetadata(key, request.ResponseHeaders, request.ResponseStatusCode);
  1999. }
  2000. catch (ErrorResponseException e)
  2001. {
  2002. if (e.StatusCode == HttpStatusCode.NotFound) return null;
  2003. if (e.StatusCode == HttpStatusCode.Conflict)
  2004. {
  2005. throw new ConflictException("Conflict detected on " + key + ", conflict must be resolved before the document will be accessible. Cannot get the conflicts ids because a HEAD request was performed. A GET request will provide more information, and if you have a document conflict listener, will automatically resolve the conflict") { Etag = e.Etag };
  2006. }
  2007. throw;
  2008. }
  2009. }
  2010. }
  2011. public Task<RavenJToken> ExecuteGetRequest(string requestUrl)
  2012. {
  2013. EnsureIsNotNullOrEmpty(requestUrl, "url");
  2014. return ExecuteWithReplication(HttpMethod.Get, async (operationMetadata, requestTimeMetric) =>
  2015. {
  2016. var metadata = new RavenJObject();
  2017. AddTransactionInformation(metadata);
  2018. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + requestUrl, HttpMethod.Get, metadata, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)))
  2019. {
  2020. return await request.ReadResponseJsonAsync().ConfigureAwait(false);
  2021. }
  2022. });
  2023. }
  2024. public HttpJsonRequest CreateRequest(string requestUrl, HttpMethod method, bool disableRequestCompression = false, bool disableAuthentication = false, TimeSpan? timeout = null)
  2025. {
  2026. var metadata = new RavenJObject();
  2027. AddTransactionInformation(metadata);
  2028. var createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, Url + requestUrl, method, metadata, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention, requestTimeMetricGetter(Url), timeout)
  2029. .AddOperationHeaders(OperationsHeaders);
  2030. createHttpJsonRequestParams.DisableRequestCompression = disableRequestCompression;
  2031. createHttpJsonRequestParams.DisableAuthentication = disableAuthentication;
  2032. return jsonRequestFactory.CreateHttpJsonRequest(createHttpJsonRequestParams);
  2033. }
  2034. public HttpJsonRequest CreateReplicationAwareRequest(string currentServerUrl, string requestUrl, HttpMethod method, bool disableRequestCompression = false, bool disableAuthentication = false, TimeSpan? timeout = null)
  2035. {
  2036. var metadata = new RavenJObject();
  2037. AddTransactionInformation(metadata);
  2038. var createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, currentServerUrl + requestUrl, method, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication,
  2039. convention, requestTimeMetricGetter(Url), timeout).AddOperationHeaders(OperationsHeaders);
  2040. createHttpJsonRequestParams.DisableRequestCompression = disableRequestCompression;
  2041. createHttpJsonRequestParams.DisableAuthentication = disableAuthentication;
  2042. return jsonRequestFactory.CreateHttpJsonRequest(createHttpJsonRequestParams)
  2043. .AddRequestExecuterAndReplicationHeaders(this, currentServerUrl);
  2044. }
  2045. [Obsolete("Use RavenFS instead.")]
  2046. public Task UpdateAttachmentMetadataAsync(string key, Etag etag, RavenJObject metadata, CancellationToken token = default(CancellationToken))
  2047. {
  2048. return ExecuteWithReplication(HttpMethod.Post, (operationMetadata, requestTimeMetric) => DirectUpdateAttachmentMetadata(key, metadata, etag, operationMetadata, requestTimeMetric, token), token);
  2049. }
  2050. [Obsolete("Use RavenFS instead.")]
  2051. private async Task DirectUpdateAttachmentMetadata(string key, RavenJObject metadata, Etag etag, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, CancellationToken token = default(CancellationToken))
  2052. {
  2053. if (etag != null)
  2054. {
  2055. metadata[Constants.MetadataEtagField] = etag.ToString();
  2056. }
  2057. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/static/" + key, HttpMethod.Post, metadata, operationMetadata.Credentials, convention, requestTimeMetric)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  2058. {
  2059. ErrorResponseException responseException;
  2060. try
  2061. {
  2062. await request.ExecuteRequestAsync().WithCancellation(token).ConfigureAwait(false);
  2063. return;
  2064. }
  2065. catch (ErrorResponseException e)
  2066. {
  2067. responseException = e;
  2068. }
  2069. if (!HandleException(responseException)) throw responseException;
  2070. }
  2071. }
  2072. [Obsolete("Use RavenFS instead.")]
  2073. public Task<IAsyncEnumerator<Attachment>> GetAttachmentHeadersStartingWithAsync(string idPrefix, int start, int pageSize, CancellationToken token = default(CancellationToken))
  2074. {
  2075. return ExecuteWithReplication(HttpMethod.Get, (operationMetadata, requestTimeMetric) => DirectGetAttachmentHeadersStartingWith(HttpMethod.Get, idPrefix, start, pageSize, operationMetadata, requestTimeMetric, token), token);
  2076. }
  2077. [Obsolete("Use RavenFS instead.")]
  2078. private async Task<IAsyncEnumerator<Attachment>> DirectGetAttachmentHeadersStartingWith(HttpMethod method, string idPrefix, int start, int pageSize, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, CancellationToken token = default(CancellationToken))
  2079. {
  2080. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/static/?startsWith=" + idPrefix + "&start=" + start + "&pageSize=" + pageSize, method, operationMetadata.Credentials, convention, requestTimeMetric)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  2081. {
  2082. RavenJToken result = await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  2083. List<Attachment> attachments = convention.CreateSerializer().Deserialize<Attachment[]>(new RavenJTokenReader(result)).Select(x => new Attachment
  2084. {
  2085. Etag = x.Etag,
  2086. Metadata = x.Metadata.WithCaseInsensitivePropertyNames(),
  2087. Size = x.Size,
  2088. Key = x.Key,
  2089. Data = () =>
  2090. { throw new InvalidOperationException("Cannot get attachment data from an attachment header"); }
  2091. }).ToList();
  2092. return new AsyncEnumeratorBridge<Attachment>(attachments.GetEnumerator());
  2093. }
  2094. }
  2095. #if !DNXCORE50
  2096. public Task CommitAsync(string txId, CancellationToken token = default(CancellationToken))
  2097. {
  2098. return ExecuteWithReplication(HttpMethod.Post, (operationMetadata, requestTimeMetric) => DirectCommit(txId, operationMetadata, requestTimeMetric, token), token);
  2099. }
  2100. private async Task DirectCommit(string txId, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, CancellationToken token)
  2101. {
  2102. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/transaction/commit?tx=" + txId, HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  2103. {
  2104. await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  2105. }
  2106. }
  2107. public Task RollbackAsync(string txId, CancellationToken token = default(CancellationToken))
  2108. {
  2109. return ExecuteWithReplication(HttpMethod.Post, (operationMetadata, requestTimeMetric) => DirectRollback(txId, operationMetadata, requestTimeMetric, token), token);
  2110. }
  2111. private async Task DirectRollback(string txId, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, CancellationToken token = default(CancellationToken))
  2112. {
  2113. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/transaction/rollback?tx=" + txId, HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric).AddOperationHeaders(OperationsHeaders)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  2114. {
  2115. await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  2116. }
  2117. }
  2118. public Task PrepareTransactionAsync(string txId, Guid? resourceManagerId = null, byte[] recoveryInformation = null, CancellationToken token = default(CancellationToken))
  2119. {
  2120. return ExecuteWithReplication(HttpMethod.Post, (operationMetadata, requestTimeMetric) => DirectPrepareTransaction(txId, operationMetadata, requestTimeMetric, resourceManagerId, recoveryInformation, token), token);
  2121. }
  2122. private async Task DirectPrepareTransaction(string txId, OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, Guid? resourceManagerId, byte[] recoveryInformation, CancellationToken token = default(CancellationToken))
  2123. {
  2124. var opUrl = operationMetadata.Url + "/transaction/prepare?tx=" + txId;
  2125. if (resourceManagerId != null)
  2126. opUrl += "&resourceManagerId=" + resourceManagerId;
  2127. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, opUrl, HttpMethod.Post, operationMetadata.Credentials, convention, requestTimeMetric)
  2128. .AddOperationHeaders(OperationsHeaders))
  2129. .AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  2130. {
  2131. if (recoveryInformation != null)
  2132. {
  2133. var ms = new MemoryStream(recoveryInformation);
  2134. await request.WriteAsync(ms).WithCancellation(token).ConfigureAwait(false);
  2135. }
  2136. await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  2137. }
  2138. }
  2139. #endif
  2140. internal Task ExecuteWithReplication(HttpMethod method, Func<OperationMetadata, IRequestTimeMetric, Task> operation, CancellationToken token = default(CancellationToken))
  2141. {
  2142. // Convert the Func<string, Task> to a Func<string, Task<object>>
  2143. return ExecuteWithReplication(method, (u, rtm) => operation(u, rtm).ContinueWith<object>(t =>
  2144. {
  2145. t.AssertNotFailed();
  2146. return null;
  2147. }, token), token);
  2148. }
  2149. private volatile bool currentlyExecuting;
  2150. private volatile bool retryBecauseOfConflict;
  2151. private bool resolvingConflict;
  2152. private bool resolvingConflictRetries;
  2153. internal async Task<T> ExecuteWithReplication<T>(HttpMethod method, Func<OperationMetadata, IRequestTimeMetric, Task<T>> operation, CancellationToken token = default(CancellationToken))
  2154. {
  2155. var currentRequest = Interlocked.Increment(ref requestCount);
  2156. if (currentlyExecuting && convention.AllowMultipuleAsyncOperations == false && retryBecauseOfConflict == false)
  2157. throw new InvalidOperationException("Only a single concurrent async request is allowed per async client instance.");
  2158. currentlyExecuting = true;
  2159. try
  2160. {
  2161. return await RequestExecuter.ExecuteOperationAsync(this, method, currentRequest, operation, token).ConfigureAwait(false);
  2162. }
  2163. finally
  2164. {
  2165. currentlyExecuting = false;
  2166. }
  2167. }
  2168. private async Task<bool> AssertNonConflictedDocumentAndCheckIfNeedToReload(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, RavenJObject docResult,
  2169. Func<string, ConflictException> onConflictedQueryResult = null, CancellationToken token = default(CancellationToken))
  2170. {
  2171. if (docResult == null)
  2172. return (false);
  2173. var metadata = docResult[Constants.Metadata];
  2174. if (metadata == null)
  2175. return (false);
  2176. if (metadata.Value<int>("@Http-Status-Code") == 409)
  2177. {
  2178. var etag = HttpExtensions.EtagHeaderToEtag(metadata.Value<string>("@etag"));
  2179. var e = await TryResolveConflictOrCreateConcurrencyException(operationMetadata, requestTimeMetric, metadata.Value<string>("@id"), docResult, etag, token).ConfigureAwait(false);
  2180. if (e != null)
  2181. throw e;
  2182. return true;
  2183. }
  2184. if (metadata.Value<bool>(Constants.RavenReplicationConflict) && onConflictedQueryResult != null)
  2185. throw onConflictedQueryResult(metadata.Value<string>("@id"));
  2186. return (false);
  2187. }
  2188. private async Task<ConflictException> TryResolveConflictOrCreateConcurrencyException(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, string key,
  2189. RavenJObject conflictsDoc,
  2190. Etag etag,
  2191. CancellationToken token)
  2192. {
  2193. var ravenJArray = conflictsDoc.Value<RavenJArray>("Conflicts");
  2194. if (ravenJArray == null)
  2195. throw new InvalidOperationException(
  2196. "Could not get conflict ids from conflicted document, are you trying to resolve a conflict when using metadata-only?");
  2197. var conflictIds = ravenJArray.Select(x => x.Value<string>()).ToArray();
  2198. var result = await TryResolveConflictByUsingRegisteredListenersAsync(key, etag, conflictIds, operationMetadata, requestTimeMetric, token).ConfigureAwait(false);
  2199. if (result)
  2200. return null;
  2201. return
  2202. new ConflictException(
  2203. "Conflict detected on " + key + ", conflict must be resolved before the document will be accessible")
  2204. {
  2205. ConflictedVersionIds = conflictIds,
  2206. Etag = etag
  2207. };
  2208. }
  2209. internal async Task<bool> TryResolveConflictByUsingRegisteredListenersAsync(string key, Etag etag, string[] conflictIds, OperationMetadata operationMetadata = null, IRequestTimeMetric requestTimeMetric = null, CancellationToken token = default(CancellationToken))
  2210. {
  2211. if (operationMetadata == null)
  2212. operationMetadata = new OperationMetadata(Url, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, null);
  2213. if (requestTimeMetric == null)
  2214. requestTimeMetric = requestTimeMetricGetter(Url);
  2215. if (conflictListeners.Length > 0 && resolvingConflict == false)
  2216. {
  2217. resolvingConflict = true;
  2218. try
  2219. {
  2220. var result = await DirectGetAsync(operationMetadata, requestTimeMetric, conflictIds, null, null, null, false, token).ConfigureAwait(false);
  2221. var results = result.Results.Select(SerializationHelper.ToJsonDocument).ToArray();
  2222. foreach (var conflictListener in conflictListeners)
  2223. {
  2224. JsonDocument resolvedDocument;
  2225. if (conflictListener.TryResolveConflict(key, results, out resolvedDocument))
  2226. {
  2227. resolvedDocument.Metadata.Remove(Constants.RavenReplicationConflictDocument);
  2228. await DirectPutAsync(operationMetadata, requestTimeMetric, key, etag, resolvedDocument.DataAsJson, resolvedDocument.Metadata, token).ConfigureAwait(false);
  2229. return true;
  2230. }
  2231. }
  2232. return false;
  2233. }
  2234. finally
  2235. {
  2236. resolvingConflict = false;
  2237. }
  2238. }
  2239. return false;
  2240. }
  2241. private async Task<T> RetryOperationBecauseOfConflict<T>(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, IEnumerable<RavenJObject> docResults,
  2242. T currentResult, Func<Task<T>> nextTry, Func<string, ConflictException> onConflictedQueryResult = null, CancellationToken token = default(CancellationToken))
  2243. {
  2244. bool requiresRetry = false;
  2245. foreach (var docResult in docResults)
  2246. {
  2247. token.ThrowIfCancellationRequested();
  2248. requiresRetry |=
  2249. await AssertNonConflictedDocumentAndCheckIfNeedToReload(operationMetadata, requestTimeMetric, docResult, onConflictedQueryResult, token).ConfigureAwait(false);
  2250. }
  2251. if (!requiresRetry)
  2252. return currentResult;
  2253. if (resolvingConflictRetries)
  2254. throw new InvalidOperationException(
  2255. "Encountered another conflict after already resolving a conflict. Conflict resolution cannot recurse.");
  2256. resolvingConflictRetries = true;
  2257. retryBecauseOfConflict = true;
  2258. try
  2259. {
  2260. return await nextTry().WithCancellation(token).ConfigureAwait(false);
  2261. }
  2262. finally
  2263. {
  2264. resolvingConflictRetries = false;
  2265. retryBecauseOfConflict = false;
  2266. }
  2267. }
  2268. public async Task<RavenJToken> GetOperationStatusAsync(long id)
  2269. {
  2270. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, Url + "/operation/status?id=" + id, HttpMethod.Get, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention, requestTimeMetricGetter(Url)).AddOperationHeaders(OperationsHeaders)))
  2271. {
  2272. try
  2273. {
  2274. return await request.ReadResponseJsonAsync().ConfigureAwait(false);
  2275. }
  2276. catch (ErrorResponseException e)
  2277. {
  2278. if (e.StatusCode == HttpStatusCode.NotFound)
  2279. return null;
  2280. throw;
  2281. }
  2282. }
  2283. }
  2284. public IAsyncInfoDatabaseCommands Info
  2285. {
  2286. get { return this; }
  2287. }
  2288. async Task<ReplicationStatistics> IAsyncInfoDatabaseCommands.GetReplicationInfoAsync(CancellationToken token)
  2289. {
  2290. using (var request = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, Url.ReplicationInfo(), HttpMethod.Get, credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication, convention, requestTimeMetricGetter(Url))))
  2291. {
  2292. var json = (RavenJObject)await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false);
  2293. return json.Deserialize<ReplicationStatistics>(convention);
  2294. }
  2295. }
  2296. public IAsyncDatabaseCommands With(ICredentials credentialsForSession)
  2297. {
  2298. return WithInternal(credentialsForSession);
  2299. }
  2300. internal AsyncServerClient WithInternal(ICredentials credentialsForSession)
  2301. {
  2302. return new AsyncServerClient(Url, convention, new OperationCredentials(credentialsThatShouldBeUsedOnlyInOperationsWithoutReplication.ApiKey, credentialsForSession), jsonRequestFactory, sessionId, requestExecuterGetter, requestTimeMetricGetter, databaseName, conflictListeners, false);
  2303. }
  2304. internal async Task WithWriteAssurance(OperationMetadata operationMetadata,
  2305. IRequestTimeMetric requestTimeMetric, Etag etag, TimeSpan? timeout = null, int replicas = 1)
  2306. {
  2307. var sb = new StringBuilder(operationMetadata.Url + "/replication/writeAssurance?");
  2308. sb.Append("etag=").Append(etag).Append("&");
  2309. sb.Append("replicas=").Append(replicas).Append("&");
  2310. sb.Append("timeout=").Append(timeout);
  2311. var createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, sb.ToString(), HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric);
  2312. using (var request = jsonRequestFactory.CreateHttpJsonRequest(createHttpJsonRequestParams.AddOperationHeaders(OperationsHeaders)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  2313. {
  2314. await request.ReadResponseJsonAsync().ConfigureAwait(false);
  2315. }
  2316. }
  2317. internal async Task<ReplicationDocumentWithClusterInformation> DirectGetReplicationDestinationsAsync(OperationMetadata operationMetadata, IRequestTimeMetric requestTimeMetric, TimeSpan? timeout = null)
  2318. {
  2319. var createHttpJsonRequestParams = new CreateHttpJsonRequestParams(this, operationMetadata.Url + "/replication/topology", HttpMethod.Get, operationMetadata.Credentials, convention, requestTimeMetric, timeout);
  2320. using (var request = jsonRequestFactory.CreateHttpJsonRequest(createHttpJsonRequestParams.AddOperationHeaders(OperationsHeaders)).AddRequestExecuterAndReplicationHeaders(this, operationMetadata.Url, operationMetadata.ClusterInformation.WithClusterFailoverHeader))
  2321. {
  2322. try
  2323. {
  2324. var requestJson = await request.ReadResponseJsonAsync().ConfigureAwait(false);
  2325. return requestJson.JsonDeserialization<ReplicationDocumentWithClusterInformation>();
  2326. }
  2327. catch (ErrorResponseException e)
  2328. {
  2329. switch (e.StatusCode)
  2330. {
  2331. case HttpStatusCode.NotFound:
  2332. case HttpStatusCode.BadRequest: //replication bundle if not enabled
  2333. return null;
  2334. default:
  2335. throw;
  2336. }
  2337. }
  2338. }
  2339. }
  2340. }
  2341. }