PageRenderTime 48ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/ToMigrate/Raven.Database/Server/Controllers/RavenBaseApiController.cs

http://github.com/ayende/ravendb
C# | 871 lines | 717 code | 142 blank | 12 comment | 128 complexity | e557f8a96c9b37f1f7182ae8e3cf7ef3 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. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Specialized;
  4. using System.Diagnostics;
  5. using System.Globalization;
  6. using System.IO;
  7. using System.IO.Compression;
  8. using System.Linq;
  9. using System.Net;
  10. using System.Net.Http;
  11. using System.Net.Http.Headers;
  12. using System.Security.Principal;
  13. using System.Text;
  14. using System.Text.RegularExpressions;
  15. using System.Threading;
  16. using System.Threading.Tasks;
  17. using System.Web;
  18. using System.Web.Http;
  19. using System.Web.Http.Controllers;
  20. using Raven.Abstractions.Connection;
  21. using Raven.Abstractions.Data;
  22. using Raven.Abstractions.Exceptions;
  23. using Raven.Abstractions.Extensions;
  24. using Raven.Abstractions.Json;
  25. using Raven.Abstractions.Logging;
  26. using Raven.Abstractions.Util;
  27. using Raven.Client.Connection;
  28. using Raven.Database.Config;
  29. using Raven.Database.Raft;
  30. using Raven.Database.Server.Abstractions;
  31. using Raven.Database.Server.Tenancy;
  32. using Raven.Database.Server.WebApi;
  33. using Raven.Imports.Newtonsoft.Json;
  34. using Raven.Imports.Newtonsoft.Json.Bson;
  35. using Raven.Imports.Newtonsoft.Json.Linq;
  36. using Raven.Json.Linq;
  37. namespace Raven.Database.Server.Controllers
  38. {
  39. public abstract class RavenBaseApiController : ApiController
  40. {
  41. protected static readonly ILog Log = LogManager.GetCurrentClassLogger();
  42. private HttpRequestMessage request;
  43. internal bool SkipAuthorizationSinceThisIsMultiGetRequestAlreadyAuthorized{ get; set; }
  44. public HttpRequestMessage InnerRequest
  45. {
  46. get
  47. {
  48. return Request ?? request;
  49. }
  50. }
  51. public bool IsInternalRequest
  52. {
  53. get
  54. {
  55. var internalHeader = GetHeader("Raven-internal-request");
  56. return internalHeader != null && internalHeader == "true";
  57. }
  58. }
  59. public HttpHeaders InnerHeaders
  60. {
  61. get
  62. {
  63. var message = InnerRequest;
  64. return CloneRequestHttpHeaders(message.Headers, message.Content == null ? null : message.Content.Headers);
  65. }
  66. }
  67. public static HttpHeaders CloneRequestHttpHeaders( HttpRequestHeaders httpRequestHeaders, HttpContentHeaders httpContentHeaders)
  68. {
  69. var headers = new Headers();
  70. foreach (var header in httpRequestHeaders)
  71. {
  72. headers.Add(header.Key, header.Value);
  73. }
  74. if (httpContentHeaders == null)
  75. return headers;
  76. foreach (var header in httpContentHeaders)
  77. {
  78. headers.Add(header.Key, header.Value);
  79. }
  80. return headers;
  81. }
  82. public IEnumerable<KeyValuePair<string,IEnumerable<string>>> ReadInnerHeaders
  83. {
  84. get
  85. {
  86. foreach (var header in InnerRequest.Headers)
  87. {
  88. yield return new KeyValuePair<string, IEnumerable<string>>(header.Key, header.Value);
  89. }
  90. if (InnerRequest.Content == null)
  91. yield break;
  92. foreach (var header in InnerRequest.Content.Headers)
  93. {
  94. yield return new KeyValuePair<string, IEnumerable<string>>(header.Key, header.Value);
  95. }
  96. }
  97. }
  98. public new IPrincipal User { get; set; }
  99. public bool WasAlreadyAuthorizedUsingSingleAuthToken { get; set; }
  100. protected virtual void InnerInitialization(HttpControllerContext controllerContext)
  101. {
  102. request = controllerContext.Request;
  103. User = controllerContext.RequestContext.Principal;
  104. landlord = (DatabasesLandlord)controllerContext.Configuration.Properties[typeof(DatabasesLandlord)];
  105. fileSystemsLandlord = (FileSystemsLandlord)controllerContext.Configuration.Properties[typeof(FileSystemsLandlord)];
  106. countersLandlord = (CountersLandlord)controllerContext.Configuration.Properties[typeof(CountersLandlord)];
  107. timeSeriesLandlord = (TimeSeriesLandlord)controllerContext.Configuration.Properties[typeof(TimeSeriesLandlord)];
  108. requestManager = (RequestManager)controllerContext.Configuration.Properties[typeof(RequestManager)];
  109. clusterManager = ((Reference<ClusterManager>)controllerContext.Configuration.Properties[typeof(ClusterManager)]).Value;
  110. }
  111. public async Task<T> ReadJsonObjectAsync<T>()
  112. {
  113. using (var stream = await InnerRequest.Content.ReadAsStreamAsync().ConfigureAwait(false))
  114. using (var buffered = new BufferedStream(stream))
  115. using (var gzipStream = new GZipStream(buffered, CompressionMode.Decompress))
  116. using (var streamReader = new StreamReader(stream, GetRequestEncoding()))
  117. {
  118. using (var jsonReader = new JsonTextReader(streamReader))
  119. {
  120. var result = JsonExtensions.CreateDefaultJsonSerializer();
  121. return (T)result.Deserialize(jsonReader, typeof(T));
  122. }
  123. }
  124. }
  125. protected Guid ExtractOperationId()
  126. {
  127. Guid result;
  128. Guid.TryParse(GetQueryStringValue("operationId"), out result);
  129. return result;
  130. }
  131. protected async Task<RavenJObject> ReadJsonAsync()
  132. {
  133. using (var stream = await InnerRequest.Content.ReadAsStreamAsync().ConfigureAwait(false))
  134. using (var buffered = new BufferedStream(stream))
  135. using (var streamReader = new StreamReader(buffered, GetRequestEncoding()))
  136. using (var jsonReader = new RavenJsonTextReader(streamReader))
  137. return RavenJObject.Load(jsonReader);
  138. }
  139. protected async Task<RavenJArray> ReadJsonArrayAsync()
  140. {
  141. using (var stream = await InnerRequest.Content.ReadAsStreamAsync().ConfigureAwait(false))
  142. using (var buffered = new BufferedStream(stream))
  143. using (var streamReader = new StreamReader(buffered, GetRequestEncoding()))
  144. using (var jsonReader = new RavenJsonTextReader(streamReader))
  145. {
  146. return RavenJArray.Load(jsonReader);
  147. }
  148. }
  149. protected async Task<string> ReadStringAsync()
  150. {
  151. using (var stream = await InnerRequest.Content.ReadAsStreamAsync().ConfigureAwait(false))
  152. using (var buffered = new BufferedStream(stream))
  153. using (var streamReader = new StreamReader(buffered, GetRequestEncoding()))
  154. return streamReader.ReadToEnd();
  155. }
  156. protected async Task<RavenJArray> ReadBsonArrayAsync()
  157. {
  158. using (var stream = await InnerRequest.Content.ReadAsStreamAsync().ConfigureAwait(false))
  159. using (var buffered = new BufferedStream(stream))
  160. using (var jsonReader = new BsonReader(buffered))
  161. {
  162. var jObject = RavenJObject.Load(jsonReader);
  163. return new RavenJArray(jObject.Values<RavenJToken>());
  164. }
  165. }
  166. private Encoding GetRequestEncoding()
  167. {
  168. if (InnerRequest.Content.Headers.ContentType == null || string.IsNullOrWhiteSpace(InnerRequest.Content.Headers.ContentType.CharSet))
  169. return Encoding.GetEncoding(Constants.DefaultRequestEncoding);
  170. return Encoding.GetEncoding(InnerRequest.Content.Headers.ContentType.CharSet);
  171. }
  172. protected int GetStart()
  173. {
  174. int start;
  175. int.TryParse(GetQueryStringValue("start"), out start);
  176. return Math.Max(0, start);
  177. }
  178. protected int GetNextPageStart()
  179. {
  180. bool isNextPage;
  181. if (bool.TryParse(GetQueryStringValue("next-page"), out isNextPage) && isNextPage)
  182. return GetStart();
  183. return 0;
  184. }
  185. protected int GetPageSize(int maxPageSize)
  186. {
  187. int pageSize;
  188. if (int.TryParse(GetQueryStringValue("pageSize"), out pageSize) == false)
  189. pageSize = 25;
  190. if (pageSize < 0)
  191. return 0;
  192. if (pageSize > maxPageSize)
  193. pageSize = maxPageSize;
  194. return pageSize;
  195. }
  196. protected bool MatchEtag(Etag etag)
  197. {
  198. return EtagHeaderToEtag() == etag;
  199. }
  200. private Etag EtagHeaderToEtag()
  201. {
  202. try
  203. {
  204. var responseHeader = GetHeader("If-None-Match");
  205. if (string.IsNullOrEmpty(responseHeader))
  206. return Etag.InvalidEtag;
  207. if (responseHeader[0] == '\"')
  208. return Etag.Parse(responseHeader.Substring(1, responseHeader.Length - 2));
  209. return Etag.Parse(responseHeader);
  210. }
  211. catch (Exception e)
  212. {
  213. Console.WriteLine(e.Message);
  214. return Etag.InvalidEtag;
  215. }
  216. }
  217. public string GetQueryStringValue(string key)
  218. {
  219. return GetQueryStringValue(InnerRequest, key);
  220. }
  221. // public static string GetQueryStringValue(HttpRequestMessage req, string key)
  222. // {
  223. // var value = req.GetQueryNameValuePairs().Where(pair => pair.Key == key).Select(pair => pair.Value).FirstOrDefault();
  224. // if (value != null)
  225. // value = Uri.UnescapeDataString(value);
  226. // return value;
  227. // }
  228. protected static string GetQueryStringValue(HttpRequestMessage req, string key)
  229. {
  230. NameValueCollection nvc;
  231. object value;
  232. if (req.Properties.TryGetValue("Raven.QueryString", out value))
  233. {
  234. nvc = (NameValueCollection) value;
  235. return nvc[key];
  236. }
  237. nvc = HttpUtility.ParseQueryString(req.RequestUri.Query);
  238. if (!ClientIsV3OrHigher(req))
  239. {
  240. foreach (var queryKey in nvc.AllKeys)
  241. nvc[queryKey] = UnescapeStringIfNeeded(nvc[queryKey]);
  242. }
  243. req.Properties["Raven.QueryString"] = nvc;
  244. return nvc[key];
  245. }
  246. protected static bool ClientIsV3OrHigher(HttpRequestMessage req)
  247. {
  248. IEnumerable<string> values;
  249. if (req.Headers.TryGetValues("Raven-Client-Version", out values) == false)
  250. return false; // probably 1.0 client?
  251. foreach (var value in values)
  252. {
  253. if (string.IsNullOrEmpty(value) ) return false;
  254. if (value[0] == '1' || value[0] == '2') return false;
  255. }
  256. return true;
  257. }
  258. protected string[] GetQueryStringValues(string key)
  259. {
  260. var items = InnerRequest.GetQueryNameValuePairs().Where(pair => pair.Key == key);
  261. return items.Select(pair => (pair.Value != null) ? Uri.UnescapeDataString(pair.Value) : null ).ToArray();
  262. }
  263. protected Etag GetEtagFromQueryString()
  264. {
  265. var etagAsString = GetQueryStringValue("etag");
  266. return etagAsString != null ? Etag.Parse(etagAsString) : null;
  267. }
  268. protected void WriteETag(Etag etag, HttpResponseMessage msg)
  269. {
  270. if (etag == null)
  271. return;
  272. WriteETag(etag.ToString(), msg);
  273. }
  274. protected static void WriteETag(string etag, HttpResponseMessage msg)
  275. {
  276. if (string.IsNullOrWhiteSpace(etag))
  277. return;
  278. msg.Headers.ETag = new EntityTagHeaderValue("\"" + etag + "\"");
  279. }
  280. protected void WriteHeaders(RavenJObject headers, Etag etag, HttpResponseMessage msg)
  281. {
  282. foreach (var header in headers)
  283. {
  284. if (header.Key.StartsWith("@"))
  285. continue;
  286. switch (header.Key)
  287. {
  288. case "Content-Type":
  289. var headerValue = header.Value.Value<string>();
  290. string charset = null;
  291. if (headerValue.Contains("charset="))
  292. {
  293. var splits = headerValue.Split(';');
  294. headerValue = splits[0];
  295. charset = splits[1].Split('=')[1];
  296. }
  297. msg.Content.Headers.ContentType = new MediaTypeHeaderValue(headerValue) { CharSet = charset };
  298. break;
  299. default:
  300. if (header.Value.Type == JTokenType.Date)
  301. {
  302. if (header.Key.StartsWith("Raven-"))
  303. {
  304. var iso8601 = GetDateString(header.Value, "o");
  305. msg.Content.Headers.Add(header.Key, iso8601);
  306. }
  307. else
  308. {
  309. var rfc1123 = GetDateString(header.Value, "r");
  310. msg.Content.Headers.Add(header.Key, rfc1123);
  311. if (!headers.ContainsKey("Raven-" + header.Key))
  312. {
  313. var iso8601 = GetDateString(header.Value, "o");
  314. msg.Content.Headers.Add("Raven-" + header.Key, iso8601);
  315. }
  316. }
  317. }
  318. else if (header.Value.Type == JTokenType.Boolean)
  319. {
  320. msg.Content.Headers.Add(header.Key, header.Value.ToString());
  321. }
  322. else
  323. {
  324. //headers do not need url decoding because they might contain special symbols (like + symbol in clr type)
  325. var value = UnescapeStringIfNeeded(header.Value.ToString(Formatting.None), shouldDecodeUrl: false);
  326. msg.Content.Headers.Add(header.Key, value);
  327. }
  328. break;
  329. }
  330. }
  331. if (headers["@Http-Status-Code"] != null)
  332. {
  333. msg.StatusCode = (HttpStatusCode)headers.Value<int>("@Http-Status-Code");
  334. msg.Content.Headers.Add("Temp-Status-Description", headers.Value<string>("@Http-Status-Description"));
  335. }
  336. WriteETag(etag, msg);
  337. }
  338. public void AddHeader(string key, string value, HttpResponseMessage msg)
  339. {
  340. if (msg.Content == null)
  341. msg.Content = JsonContent();
  342. // Ensure we haven't already appended these values.
  343. IEnumerable<string> existingValues;
  344. var hasExistingHeaderAppended = msg.Content.Headers.TryGetValues(key, out existingValues) && existingValues.Any(v => v == value);
  345. if (!hasExistingHeaderAppended)
  346. {
  347. msg.Content.Headers.Add(key, value);
  348. }
  349. }
  350. private string GetDateString(RavenJToken token, string format)
  351. {
  352. var value = token as RavenJValue;
  353. if (value == null)
  354. return token.ToString();
  355. var obj = value.Value;
  356. if (obj is DateTime)
  357. return ((DateTime)obj).ToString(format);
  358. if (obj is DateTimeOffset)
  359. return ((DateTimeOffset)obj).ToString(format);
  360. return obj.ToString();
  361. }
  362. private static string UnescapeStringIfNeeded(string str, bool shouldDecodeUrl = true)
  363. {
  364. if (str.StartsWith("\"") && str.EndsWith("\""))
  365. str = Regex.Unescape(str.Substring(1, str.Length - 2));
  366. if (str.Any(ch => ch > 127))
  367. {
  368. // contains non ASCII chars, needs encoding
  369. return Uri.EscapeDataString(str);
  370. }
  371. return shouldDecodeUrl ? HttpUtility.UrlDecode(str) : str;
  372. }
  373. public virtual HttpResponseMessage GetMessageWithObject(object item, HttpStatusCode code = HttpStatusCode.OK, Etag etag = null)
  374. {
  375. var token = item as RavenJToken;
  376. if (token == null && item != null)
  377. {
  378. token = RavenJToken.FromObject(item);
  379. }
  380. bool metadataOnly;
  381. if (bool.TryParse(GetQueryStringValue("metadata-only"), out metadataOnly) && metadataOnly)
  382. token = Extensions.HttpExtensions.MinimizeToken(token);
  383. var msg = new HttpResponseMessage(code)
  384. {
  385. Content = JsonContent(token),
  386. };
  387. WriteETag(etag, msg);
  388. return msg;
  389. }
  390. public virtual HttpResponseMessage GetMessageWithString(string msg, HttpStatusCode code = HttpStatusCode.OK, Etag etag = null)
  391. {
  392. var resMsg = new HttpResponseMessage(code)
  393. {
  394. Content = new MultiGetSafeStringContent(msg),
  395. };
  396. WriteETag(etag, resMsg);
  397. return resMsg;
  398. }
  399. public virtual HttpResponseMessage GetEmptyMessage(HttpStatusCode code = HttpStatusCode.OK, Etag etag = null)
  400. {
  401. var resMsg = new HttpResponseMessage(code)
  402. {
  403. Content = JsonContent()
  404. };
  405. WriteETag(etag, resMsg);
  406. return resMsg;
  407. }
  408. public virtual Task<HttpResponseMessage> GetMessageWithObjectAsTask(object item, HttpStatusCode code = HttpStatusCode.OK, Etag etag = null)
  409. {
  410. return new CompletedTask<HttpResponseMessage>(GetMessageWithObject(item, code, etag));
  411. }
  412. public Task<HttpResponseMessage> GetMessageWithStringAsTask(string msg, HttpStatusCode code = HttpStatusCode.OK, Etag etag = null)
  413. {
  414. return new CompletedTask<HttpResponseMessage>(GetMessageWithString(msg, code, etag));
  415. }
  416. public Task<HttpResponseMessage> GetEmptyMessageAsTask(HttpStatusCode code = HttpStatusCode.OK, Etag etag = null)
  417. {
  418. return new CompletedTask<HttpResponseMessage>(GetEmptyMessage(code, etag));
  419. }
  420. public HttpResponseMessage WriteData(RavenJObject data, RavenJObject headers, Etag etag, HttpStatusCode status = HttpStatusCode.OK, HttpResponseMessage msg = null)
  421. {
  422. if (msg == null)
  423. msg = GetEmptyMessage(status);
  424. var jsonContent = ((JsonContent)msg.Content);
  425. WriteHeaders(headers, etag, msg);
  426. var jsonp = GetQueryStringValue("jsonp");
  427. if (string.IsNullOrEmpty(jsonp) == false)
  428. jsonContent.Jsonp = jsonp;
  429. jsonContent.Data = data;
  430. return msg;
  431. }
  432. public Etag GetEtag()
  433. {
  434. var etagAsString = GetHeader("If-None-Match") ?? GetHeader("If-Match");
  435. if (etagAsString != null)
  436. {
  437. // etags are usually quoted
  438. if (etagAsString.StartsWith("\"") && etagAsString.EndsWith("\""))
  439. etagAsString = etagAsString.Substring(1, etagAsString.Length - 2);
  440. Etag result;
  441. if (Etag.TryParse(etagAsString, out result))
  442. return result;
  443. throw new BadRequestException("Could not parse If-None-Match or If-Match header as Guid");
  444. }
  445. return null;
  446. }
  447. public string GetHeader(string key)
  448. {
  449. IEnumerable<string> values;
  450. if (InnerRequest.Headers.TryGetValues(key, out values) ||
  451. (InnerRequest.Content != null && InnerRequest.Content.Headers.TryGetValues(key, out values)))
  452. return values.FirstOrDefault();
  453. return null;
  454. }
  455. public List<string> GetHeaders(string key)
  456. {
  457. IEnumerable<string> values;
  458. if (InnerRequest.Headers.TryGetValues(key, out values) ||
  459. InnerRequest.Content.Headers.TryGetValues(key, out values))
  460. return values.ToList();
  461. return null;
  462. }
  463. public bool HasCookie(string key)
  464. {
  465. return InnerRequest.Headers.GetCookies(key).Count != 0;
  466. }
  467. public string GetCookie(string key)
  468. {
  469. var cookieHeaderValue = InnerRequest.Headers.GetCookies(key).FirstOrDefault();
  470. if (cookieHeaderValue != null)
  471. {
  472. var cookie = cookieHeaderValue.Cookies.FirstOrDefault();
  473. if (cookie != null)
  474. return cookie.Value;
  475. }
  476. return null;
  477. }
  478. public HttpResponseMessage WriteEmbeddedFile(string ravenPath, string embeddedPath, string zipPath, string docPath)
  479. {
  480. var filePath = Path.Combine(ravenPath, docPath);
  481. if (File.Exists(filePath))
  482. return WriteFile(filePath);
  483. filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../Raven.Studio.Html5/", docPath);
  484. if (File.Exists(filePath))
  485. return WriteFile(filePath);
  486. filePath = Path.Combine(this.SystemConfiguration.Core.EmbeddedFilesDirectory, docPath);
  487. if (File.Exists(filePath))
  488. return WriteFile(filePath);
  489. filePath = Path.Combine("~/../../../../Raven.Studio.Html5", docPath);
  490. if (File.Exists(filePath))
  491. return WriteFile(filePath);
  492. if (string.IsNullOrEmpty(zipPath) == false)
  493. {
  494. var fullZipPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, zipPath + ".zip");
  495. if (File.Exists(fullZipPath) == false)
  496. fullZipPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", zipPath + ".zip");
  497. if (File.Exists(fullZipPath) == false)
  498. fullZipPath = Path.Combine(this.SystemConfiguration.Core.EmbeddedFilesDirectory, zipPath + ".zip");
  499. if (File.Exists(fullZipPath))
  500. {
  501. return WriteFileFromZip(fullZipPath, docPath);
  502. }
  503. }
  504. return WriteEmbeddedFileOfType(embeddedPath, docPath);
  505. }
  506. private HttpResponseMessage WriteFileFromZip(string zipPath, string docPath)
  507. {
  508. var etagValue = GetHeader("If-None-Match") ?? GetHeader("If-Match");
  509. var currentFileEtag = EmbeddedLastChangedDate + docPath;
  510. if (etagValue == "\"" + currentFileEtag + "\"")
  511. return GetEmptyMessage(HttpStatusCode.NotModified);
  512. var fileStream = new FileStream(zipPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
  513. var zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read, false);
  514. var zipEntry = zipArchive.Entries.FirstOrDefault(a => a.FullName.Equals(docPath, StringComparison.OrdinalIgnoreCase));
  515. if (zipEntry == null)
  516. return EmbeddedFileNotFound(docPath);
  517. var entry = zipEntry.Open();
  518. var msg = new HttpResponseMessage
  519. {
  520. Content = new CompressedStreamContent(entry, false)
  521. {
  522. Disposables = { zipArchive }
  523. },
  524. };
  525. WriteETag(currentFileEtag, msg);
  526. var type = GetContentType(docPath);
  527. msg.Content.Headers.ContentType = new MediaTypeHeaderValue(type);
  528. return msg;
  529. }
  530. public abstract void MarkRequestDuration(long duration);
  531. public abstract Task<RequestWebApiEventArgs> TrySetupRequestToProperResource();
  532. public abstract RavenConfiguration ResourceConfiguration { get; }
  533. public HttpResponseMessage WriteFile(string filePath)
  534. {
  535. var etagValue = GetHeader("If-None-Match") ?? GetHeader("If-Match");
  536. if (etagValue != null)
  537. {
  538. // Bug fix: the etag header starts and ends with quotes, resulting in cache-busting; the Studio always receives new files, even if should be cached.
  539. etagValue = etagValue.Trim(new[] { '\"' });
  540. }
  541. var fileEtag = File.GetLastWriteTimeUtc(filePath).ToString("G");
  542. if (etagValue == fileEtag)
  543. return GetEmptyMessage(HttpStatusCode.NotModified);
  544. var msg = new HttpResponseMessage
  545. {
  546. Content = new CompressedStreamContent(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), false)
  547. };
  548. WriteETag(fileEtag, msg);
  549. var type = GetContentType(filePath);
  550. msg.Content.Headers.ContentType = new MediaTypeHeaderValue(type);
  551. return msg;
  552. }
  553. private HttpResponseMessage WriteEmbeddedFileOfType(string embeddedPath, string docPath)
  554. {
  555. var etagValue = GetHeader("If-None-Match") ?? GetHeader("If-Match");
  556. var currentFileEtag = EmbeddedLastChangedDate + docPath;
  557. if (etagValue == "\"" + currentFileEtag + "\"")
  558. return GetEmptyMessage(HttpStatusCode.NotModified);
  559. byte[] bytes;
  560. var resourceName = embeddedPath + "." + docPath.Replace("/", ".");
  561. var resourceAssembly = typeof(RavenBaseApiController).Assembly;
  562. var resourceNames = resourceAssembly.GetManifestResourceNames();
  563. var lowercasedResourceName = resourceNames.FirstOrDefault(s => string.Equals(s, resourceName, StringComparison.OrdinalIgnoreCase));
  564. if (lowercasedResourceName == null)
  565. {
  566. return EmbeddedFileNotFound(docPath);
  567. }
  568. using (var resource = resourceAssembly.GetManifestResourceStream(lowercasedResourceName))
  569. {
  570. if (resource == null)
  571. return EmbeddedFileNotFound(docPath);
  572. bytes = resource.ReadData();
  573. }
  574. var msg = new HttpResponseMessage
  575. {
  576. Content = new ByteArrayContent(bytes),
  577. };
  578. WriteETag(currentFileEtag, msg);
  579. var type = GetContentType(docPath);
  580. msg.Content.Headers.ContentType = new MediaTypeHeaderValue(type);
  581. return msg;
  582. }
  583. private HttpResponseMessage EmbeddedFileNotFound(string docPath)
  584. {
  585. var message = "The following embedded file was not available: " + docPath +
  586. ". Please make sure that the Raven.Studio.Html5.zip file exist in the main directory (near to the Raven.Database.dll).";
  587. return GetMessageWithObject(new {Message = message}, HttpStatusCode.NotFound);
  588. }
  589. private static readonly string EmbeddedLastChangedDate =
  590. File.GetLastWriteTime(AssemblyHelper.GetAssemblyLocationFor(typeof(HttpExtensions))).Ticks.ToString("G");
  591. private static string GetContentType(string docPath)
  592. {
  593. switch (Path.GetExtension(docPath))
  594. {
  595. case ".html":
  596. case ".htm":
  597. return "text/html";
  598. case ".css":
  599. return "text/css";
  600. case ".js":
  601. return "text/javascript";
  602. case ".ico":
  603. return "image/vnd.microsoft.icon";
  604. case ".jpg":
  605. return "image/jpeg";
  606. case ".gif":
  607. return "image/gif";
  608. case ".png":
  609. return "image/png";
  610. case ".xap":
  611. return "application/x-silverlight-2";
  612. case ".json":
  613. return "application/json";
  614. case ".eot":
  615. return "application/vnd.ms-fontobject";
  616. case ".svg":
  617. return "image/svg+xml";
  618. case ".ttf":
  619. return "application/octet-stream";
  620. case ".woff":
  621. return "application/font-woff";
  622. case ".woff2":
  623. return "application/font-woff2";
  624. default:
  625. return "text/plain";
  626. }
  627. }
  628. protected class Headers : HttpHeaders {}
  629. public JsonContent JsonContent(RavenJToken data = null)
  630. {
  631. return new JsonContent(data)
  632. .WithRequest(InnerRequest);
  633. }
  634. public string GetRequestUrl()
  635. {
  636. var rawUrl = InnerRequest.RequestUri.PathAndQuery;
  637. return UrlExtension.GetRequestUrlFromRawUrl(rawUrl, SystemConfiguration);
  638. }
  639. public abstract RavenConfiguration SystemConfiguration { get; }
  640. protected void AddRavenHeader(HttpResponseMessage msg, Stopwatch sp)
  641. {
  642. AddHeader(Constants.RavenServerBuild, DocumentDatabase.BuildVersion.ToInvariantString(), msg);
  643. AddHeader("Temp-Request-Time", sp.ElapsedMilliseconds.ToString("#,#;;0", CultureInfo.InvariantCulture), msg);
  644. }
  645. public abstract string ResourcePrefix { get; }
  646. public abstract string ResourceName { get; protected set; }
  647. private int innerRequestsCount;
  648. public int InnerRequestsCount { get { return innerRequestsCount; } }
  649. public List<Action<StringBuilder>> CustomRequestTraceInfo { get; private set; }
  650. protected void AddRequestTraceInfo(Action<StringBuilder> info)
  651. {
  652. if (info == null)
  653. return;
  654. if (CustomRequestTraceInfo == null)
  655. CustomRequestTraceInfo = new List<Action<StringBuilder>>();
  656. CustomRequestTraceInfo.Add(info);
  657. }
  658. protected void IncrementInnerRequestsCount()
  659. {
  660. Interlocked.Increment(ref innerRequestsCount);
  661. }
  662. protected static bool Match(string x, string y)
  663. {
  664. return string.Equals(x, y, StringComparison.OrdinalIgnoreCase);
  665. }
  666. #region Landlords
  667. private DatabasesLandlord landlord;
  668. public DatabasesLandlord DatabasesLandlord
  669. {
  670. get
  671. {
  672. if (Configuration == null || landlord != null)
  673. return landlord;
  674. return landlord = (DatabasesLandlord)Configuration.Properties[typeof(DatabasesLandlord)];
  675. }
  676. }
  677. private CountersLandlord countersLandlord;
  678. public CountersLandlord CountersLandlord
  679. {
  680. get
  681. {
  682. if (Configuration == null)
  683. return countersLandlord;
  684. return (CountersLandlord)Configuration.Properties[typeof(CountersLandlord)];
  685. }
  686. }
  687. private TimeSeriesLandlord timeSeriesLandlord;
  688. public TimeSeriesLandlord TimeSeriesLandlord
  689. {
  690. get
  691. {
  692. if (Configuration == null)
  693. return timeSeriesLandlord;
  694. return (TimeSeriesLandlord)Configuration.Properties[typeof(TimeSeriesLandlord)];
  695. }
  696. }
  697. private FileSystemsLandlord fileSystemsLandlord;
  698. public FileSystemsLandlord FileSystemsLandlord
  699. {
  700. get
  701. {
  702. if (Configuration == null)
  703. return fileSystemsLandlord;
  704. return (FileSystemsLandlord)Configuration.Properties[typeof(FileSystemsLandlord)];
  705. }
  706. }
  707. private RequestManager requestManager;
  708. public RequestManager RequestManager
  709. {
  710. get
  711. {
  712. if (Configuration == null)
  713. return requestManager;
  714. return (RequestManager)Configuration.Properties[typeof(RequestManager)];
  715. }
  716. }
  717. private ClusterManager clusterManager;
  718. public ClusterManager ClusterManager
  719. {
  720. get
  721. {
  722. if (Configuration == null)
  723. return clusterManager;
  724. return ((Reference<ClusterManager>)Configuration.Properties[typeof(ClusterManager)]).Value;
  725. }
  726. }
  727. #endregion
  728. }
  729. }