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