public async Task <IHttpActionResult> PostPublishAsync(RouteIdentifier identifier) { // Load route to get source code. var route = await _routeRepository.GetRouteByIdentifierAsync(identifier); var code = route.Code; // Compile code and get assembly as byte array. var compilationResult = _compilationService.Compile(code); // We'll save the assembly and mark the route as published if the compilation was successful. if (compilationResult.Success) { route.Assembly = compilationResult.Assembly; route.IsCurrent = true; route.IsOnline = true; route.PublishedOn = DateTimeOffset.UtcNow; route = await _routeRepository.UpdateRouteAsync(route); } var result = new PublishResponse { Compilation = compilationResult, Route = RouteResponse.Map(route) }; // Return an error response if compile was unsuccessful, otherwise the response was successful. return(Content(compilationResult.Success ? HttpStatusCode.OK : HttpStatusCode.InternalServerError, result)); }
public async Task <RouteResponse> PatchRouteByIdentiferAsync(RouteIdentifier identifier, IDictionary <string, object> values) { // We'll get the intersection of known valid property names, to the incoming property // names, as this will get us a list of only valid incoming property keys. var userId = User.GetUserId(); var route = await _routeRepository.GetRouteByIdentifierAsync(identifier, userId); var validProperties = new List <string> { "owner", "uri", "title", "type", "code", "isOnline" }; // Allow the IsDefault property to be set if the user is an admin. if (User.IsAdmin()) { validProperties.Add("isDefault"); } var selectedProperties = values.Keys.Intersect(validProperties); // Only set allowed properties using reflection and type coersion. We proper-case the // name because our serializer automatically camel-cases json property names. foreach (var property in selectedProperties) { route.SetProperty(property.ToProperCase(), values[property]); } // Update server properties. route.IsCurrent = false; route = await _routeRepository.UpdateRouteAsync(route, userId); return(RouteResponse.Map(route)); }
public async Task <RouteResponse> GetRouteByIdentiferAsync(RouteIdentifier identifier) { var userId = User.GetUserId(); var route = await _routeRepository.GetRouteByIdentifierAsync(identifier, userId); return(RouteResponse.Map(route)); }
public async Task <Route> GetRouteByIdentifierAsync(RouteIdentifier identifier, string userId = null) { using (var db = new SubrouteContext()) { var query = db.Routes .Include(r => r.RouteSettings) .Include(r => r.RoutePackages) .AsNoTracking() .AsQueryable(); if (userId != null) { query = query.Where(q => q.UserId == userId); } // We'll load the route from an untracked collection since we don't want outside changes causing unwanted updates. var route = identifier.Type == RouteIdentifierType.Id ? await query.AsNoTracking().FirstOrDefaultAsync(r => r.Id == identifier.Id) : await query.AsNoTracking().FirstOrDefaultAsync(r => r.Uri == identifier.Uri); if (route == null) { throw new NotFoundException($"No route with identifier '{identifier}' was found."); } return(route); } }
public async Task <dynamic> Push(RouteIdentifier routeId, string url, RestReq options) { Queue.Wait(); try { var temp = Manager.GlobalTimeout; if (!Limited()) { return(await MakeRequest(routeId, url, options)); } // Let library users know they have hit a ratelimit // this._manager.Rest.emit(RESTManagerEvents.Ratelimited, { // timeToReset: this.TimeToReset, // limit: this.limit, // method: options.method, // hash: this.hash, // route: routeID.route, // majorParameter: this.majorParameter // }); Console.WriteLine("R8 LIMIT!"); await Task.Delay(Convert.ToInt32(TimeToReset())); return(await MakeRequest(routeId, url, options)); } finally { Queue.Shift(); } }
public async Task <RouteSettingResponse[]> PutRouteSettingsAsync(RouteIdentifier identifier, RouteSettingRequest[] settings) { // Ensure that the current user is authorized to access this route. var route = await EnsureAuthorizedRouteAccessAsync(identifier); // Map all of the incoming route settings to the internal DB type. // Ignore any blank settings with no name or value. var mappedSettings = settings .Where(s => s.Name.HasValue() && s.Value.HasValue()) .Select(s => RouteSettingRequest.Map(s, route.Id)) .ToArray(); // All the route setting values will be specified, anything missing will be deleted, // anything new will be added, and any values that are different will be updated. // To compare, we'll need to load all the existing route settings. var existingSettings = await _routeSettingRepository.GetRouteSettingsAsync(identifier); // Find settings we need to add. var newSettings = mappedSettings.Where(s => existingSettings.All(es => !es.Name.CaseInsensitiveEqual(s.Name))).ToArray(); var deleteSettings = existingSettings.Where(es => mappedSettings.All(s => !s.Name.CaseInsensitiveEqual(es.Name))).ToArray(); var updateSettings = mappedSettings.Where(s => existingSettings.Any(es => es.Name.CaseInsensitiveEqual(s.Name) && !es.Value.CaseInsensitiveEqual(s.Value))).ToArray(); var unchangedSettings = existingSettings.Where(es => settings.Any(s => s.Name.CaseInsensitiveEqual(es.Name) && s.Value.CaseInsensitiveEqual(es.Value))).ToArray(); // Instantiate a new list to contain all the inserted and updated // settings, this way we'll return any server generated ids. var results = new List <RouteSetting>(); // Add all the unchanged settings to the results. results.AddRange(unchangedSettings); // We will execute all operations in a transaction so we don't end // up with the settings in a mis-aligned state with the UI. using (var ts = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { // Create new incoming settings. foreach (var setting in newSettings) { results.Add(await _routeSettingRepository.CreateRouteSettingAsync(setting)); } // Update settings where the value has changed. foreach (var setting in updateSettings) { results.Add(await _routeSettingRepository.UpdateRouteSettingAsync(setting)); } // Delete any missing settings. foreach (var setting in deleteSettings) { await _routeSettingRepository.DeleteRouteSettingAsync(setting); } // Complete the transaction scope to commit all operations. ts.Complete(); } // Map the RouteSetting type to the outgoing RouteSettingResponse type. return(results.Select(RouteSettingResponse.Map).OrderBy(s => s.Name).ToArray()); }
public void LogResponseContent(Stream content, RouteIdentifier routeIdentifier) { if (_configuration.IsLoggingEnabled) { using (var logStream = File.OpenWrite(GetFilePath(routeIdentifier, "response_"))) { content.CopyTo(logStream); } } }
public async Task <RouteSettingResponse[]> GetRouteSettingsAsync(RouteIdentifier identifier) { // Ensure that the current user is authorized to access this route. await EnsureAuthorizedRouteAccessAsync(identifier); // Load all route settings for the specified route identifier. var settings = await _routeSettingRepository.GetRouteSettingsAsync(identifier); // Map the RouteSetting types to RouteSettingResponse types. return(settings.Select(RouteSettingResponse.Map).ToArray()); }
/// <summary> /// Delete the route setting record with the specified identifier. /// </summary> /// <param name="identifier"><see cref="RouteIdentifier"/> for the requested route.</param> /// <param name="name">String that identifies the route setting record to delete.</param> /// <returns>Returns a Task, essentially void when using async syntax.</returns> public async Task DeleteRouteSettingAsync(RouteIdentifier identifier, string name) { using (var db = new SubrouteContext()) { var route = await GetRouteSettingByNameAsync(identifier, name); // Mark route for deletion so the context knowns to delete it. db.Entry(route).State = EntityState.Deleted; await db.SaveChangesAsync(); } }
protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // Note: No guarantee headers will end up being sent in the same order as originally // Note: Http protocol version may not be retained var requestContent = request.Method == HttpMethod.Get ? null : await request.Content.ReadAsStringAsync(); //Log request coming in against a new identifier var routeIdentifier = new RouteIdentifier(); _proxyLogging.LogRequest(request, requestContent, routeIdentifier); var routing = _proxyRouting.GetRouting(request.RequestUri, request.Method, GetClientIPAddress(request), requestContent, routeIdentifier); var responses = new List <Task <HttpResponseMessage> >(); if (request.Method == HttpMethod.Get) { foreach (var route in routing.Routes) { if (route.IsPrimary) { responses.Insert(0, GetAsyncRequest(request, route, routing)); } else if (_configuration.AreNonPrimaryRequestsEnabled) { responses.Add(GetAsyncRequest(request, route, routing)); } } } else if (request.Method == HttpMethod.Post) { foreach (var route in routing.Routes) { if (route.IsPrimary) { var requestHttpContent = GetRequestHttpContent(request, requestContent); responses.Insert(0, PostAsyncRequest(request, requestHttpContent, route, routing)); } else if (_configuration.AreNonPrimaryRequestsEnabled) { var requestHttpContent = GetRequestHttpContent(request, requestContent); responses.Add(PostAsyncRequest(request, requestHttpContent, route, routing)); } } } else { throw new NotSupportedException(); } //Primary task is always inserted as first object return(await responses.First()); }
public void LogRequest(HttpRequestMessage request, string requestContent, RouteIdentifier routeIdentifier) { if (_configuration.IsLoggingEnabled) { using (var logStream = File.CreateText(GetFilePath(routeIdentifier, "request"))) { logStream.WriteLine(request.RequestUri); var headers = request.GetHeadersLoggingString(); logStream.WriteLine(headers); logStream.WriteLine(requestContent); } } }
private async Task <Route> EnsureAuthorizedRouteAccessAsync(RouteIdentifier identifier) { // Load the route by identifier, this method will throw an exception if no route exists. var route = await _routeRepository.GetRouteByIdentifierAsync(identifier); // We've found a route, we need to ensure the user owns the route or they are an admin. if (!route.UserId.CaseInsensitiveEqual(User.GetUserId()) && !User.IsAdmin()) { throw new AuthorizationException("The current user is not authorized to access specified route."); } return(route); }
private CloudBlockBlob GetCloudBlockBlob(RouteIdentifier routeIdentifier, string additionalIdentifier) { var dateTime = routeIdentifier.DateTime; var datePath = dateTime.ToString("yyyy-MM-dd"); var timePath = dateTime.ToString("hh-mm-ss"); var blobPath = $"{datePath}\\{additionalIdentifier}{routeIdentifier.Name}\\{timePath}_{routeIdentifier.Id}.log"; var blockBlob = _container.GetBlockBlobReference(blobPath); blockBlob.Properties.ContentType = "application/json"; return(blockBlob); }
public async Task <long> GetRequestExecutionCountByRouteIdentifierAsync(RouteIdentifier identifier) { using (var db = new SubrouteContext()) { // Build base query to return all requests. var query = db.Requests.AsNoTracking().AsQueryable(); // Ensure we are only returning requests for the specified route. query = identifier.Type == RouteIdentifierType.Id ? query.Where(r => r.RouteId == identifier.Id) : query.Where(r => r.Route.Uri == identifier.Uri); // Execute query and get total count. return(await query.LongCountAsync()); } }
public void LogResponseContent(Stream content, RouteIdentifier routeIdentifier) { if (_configuration.IsLoggingEnabled) { try { using (var logStream = GetCloudBlockBlob(routeIdentifier, "response_").OpenWrite()) { content.CopyTo(logStream); } } catch (Exception) { //Swallow exeption to ensure logging errors don't cause the request to fail } } }
private string GetFilePath(RouteIdentifier routeIdentifier, string additionalIdentifier) { var dateTime = routeIdentifier.DateTime; var datePath = dateTime.ToString("yyyy-MM-dd"); var timePath = dateTime.ToString("hh-mm-ss"); var path = $"{_configuration.FileProxyLoggingRootPath}\\{datePath}\\{additionalIdentifier}{routeIdentifier.Name}"; if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } var filePath = $"{path}\\{timePath}_{routeIdentifier.Id}.log"; return(filePath); }
public async Task <RequestResponse> GetRequestByRequestId(RouteIdentifier identifier, int id) { var request = await _requestRepository.GetRequestByIdAsync(id); // Ensure the request belongs to the specified route. if (identifier.Type == RouteIdentifierType.Id && request.RouteId != identifier.Id) { throw new NotFoundException("Request does not belong to the specified route."); } if (identifier.Type == RouteIdentifierType.Uri && request.Route.Uri != identifier.Uri) { throw new NotFoundException("Request does not belong to the specified route."); } return(RequestResponse.Map(request)); }
public async Task DeleteRouteByIdentifierAsync(RouteIdentifier identifier, string userId = null) { using (var db = new SubrouteContext()) { var route = await GetRouteByIdentifierAsync(identifier); // Ensure route belongs to specified user. if (userId != null && route.UserId != userId) { throw new NotFoundException("Route does not belong to specified user, and cannot be deleted."); } // Mark route for deletion so the context knowns to delete it. db.Entry(route).State = EntityState.Deleted; await db.SaveChangesAsync(); } }
/// <summary> /// Load a single route setting by its name. /// </summary> /// <param name="identifier"><see cref="RouteIdentifier"/> for the requested route.</param> /// <param name="name">String representing the specified route setting name.</param> /// <returns>Returns a single <see cref="RouteSetting"/> with the matching route setting identifier.</returns> public async Task <RouteSetting> GetRouteSettingByNameAsync(RouteIdentifier identifier, string name) { using (var db = new SubrouteContext()) { var query = db.RouteSettings .AsNoTracking() .Where(rs => rs.Name == name); // Ensure we are only returning settings for the specified route // Use the correct identifier type to query the database. query = identifier.Type == RouteIdentifierType.Id ? query.Where(r => r.RouteId == identifier.Id) : query.Where(r => r.Route.Uri == identifier.Uri); // Return the first match. return(await query.FirstOrDefaultAsync()); } }
/// <summary> /// Load all the route settings for the specified <see cref="RouteIdentifier"/>. /// </summary> /// <param name="identifier"><see cref="RouteIdentifier"/> for the requested route.</param> /// <returns>Returns an array of <see cref="RouteSetting"/> objects for the specified route.</returns> public async Task <RouteSetting[]> GetRouteSettingsAsync(RouteIdentifier identifier) { using (var db = new SubrouteContext()) { // Get an untracked base queryable. var query = db.RouteSettings .AsNoTracking() .AsQueryable(); // Ensure we are only returning settings for the specified route // Use the correct identifier type to query the database. query = identifier.Type == RouteIdentifierType.Id ? query.Where(r => r.RouteId == identifier.Id) : query.Where(r => r.Route.Uri == identifier.Uri); // Materialize the results into memory. return(await query.ToArrayAsync()); } }
public void LogRequest(HttpRequestMessage request, string requestContent, RouteIdentifier routeIdentifier) { if (_configuration.IsLoggingEnabled) { try { using (var logStream = new StreamWriter(GetCloudBlockBlob(routeIdentifier, "request").OpenWrite())) { logStream.WriteLine(request.RequestUri); var headers = request.GetHeadersLoggingString(); logStream.WriteLine(headers); logStream.WriteLine(requestContent); } } catch (Exception) { //Swallow exeption to ensure logging errors don't cause the request to fail } } }
/// <summary> /// Load all the route packages for the specified <see cref="RouteIdentifier"/>. /// </summary> /// <param name="identifier"><see cref="RouteIdentifier"/> for the requested route.</param> /// <param name="specified">Indicates if the results should only include user specified packages.</param> /// <returns>Returns an array of <see cref="RoutePackage"/> objects for the specified route.</returns> public async Task <RoutePackage[]> GetRoutePackagesAsync(RouteIdentifier identifier, bool specified = false) { using (var db = new SubrouteContext()) { // Get an untracked base queryable. var query = db.RoutePackages .AsNoTracking() .AsQueryable(); // Ensure we are only returning packages for the specified route // Use the correct identifier type to query the database. query = identifier.Type == RouteIdentifierType.Id ? query.Where(r => r.RouteId == identifier.Id) : query.Where(r => r.Route.Uri == identifier.Uri); // Determine if we are including all results or just user specified. query = specified ? query.Where(q => q.UserSpecified) : query; // Materialize the results into memory. return(await query.ToArrayAsync()); } }
private async Task <dynamic> MakeRequest(RouteIdentifier routeId, string url, RestReq options, int retries = 0) { var controller = new CancellationTokenSource(); var abortTimer = new Timer(Client.ClientOptions.RestOptions.Timeout.TotalMilliseconds) { AutoReset = false }; abortTimer.Start(); abortTimer.Elapsed += (sender, args) => { controller.Cancel(); abortTimer.Dispose(); }; IRestResponse res; try { var restRequest = new RestRequest(url, DataFormat.Json) { Method = options.Method }; if (options.Headers != null) { foreach (var header in options.Headers) { restRequest.AddHeader(header.Key, header.Value); } } if (options.Files != null) { foreach (var file in options.Files) { restRequest.AddFile(file.Name, file.Writer, file.FileName, file.ContentLength, file.ContentType); } } if (options.Data != null) { restRequest.AddJsonBody(options.Data); } switch (options.Method) { case Method.GET: res = Manager.RestClient.Get(restRequest); break; case Method.POST: res = Manager.RestClient.Post(restRequest); break; case Method.PUT: res = Manager.RestClient.Put(restRequest); break; case Method.PATCH: res = Manager.RestClient.Patch(restRequest); break; case Method.DELETE: res = Manager.RestClient.Delete(restRequest); break; default: res = Manager.RestClient.Get(restRequest); break; } } catch (Exception error) { Console.WriteLine(JsonConvert.SerializeObject(error)); // if (error.Source == 'AbortError' && retries !== this.manager.options.retries) return this.makeRequest(routeID, url, options, ++retries); throw; } finally { abortTimer.Stop(); abortTimer.Dispose(); } var retryAfter = 0; var limit = res.Headers.ToArray().ToList().Find(x => x.Name == "x-ratelimit-limit"); var remaining = res.Headers.ToArray().ToList().Find(x => x.Name == "x-ratelimit-remaining"); var reset = res.Headers.ToArray().ToList().Find(x => x.Name == "x-ratelimit-reset-after"); var hash = res.Headers.ToArray().ToList().Find(x => x.Name == "x-ratelimit-bucket"); var retry = res.Headers.ToArray().ToList().Find(x => x.Name == "Retry-After"); var cloudflare = res.Headers.ToArray().ToList().Find(x => x.Name == "Via") != null; Limit = limit != null?Convert.ToInt32(limit.Value) : int.MaxValue; Remaining = remaining != null?Convert.ToInt32(remaining.Value) : int.MaxValue; Reset = reset != null ? Convert.ToInt32(reset.Value) * 1000 + new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() + Client.ClientOptions.RestOptions.Offset : new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds(); if (retry != null) { retryAfter = Convert.ToInt32(retry.Value) * (cloudflare ? 1000 : 1) + Client.ClientOptions.RestOptions.Offset; } if (hash != null && Convert.ToString(hash.Value) != Hash) { // this.manager.rest.emit(RESTManagerEvents.Debug, `Bucket hash update: ${this.hash} => ${hash} for ${options.method}-${routeID.route}`); if (!Manager.Hashes.ContainsKey($"{options.Method}-${routeId.Route}")) { Manager.Hashes.Add($"{options.Method}-${routeId.Route}", Convert.ToString(hash.Value)); } } if (res.Headers.ToArray().ToList().Find(x => x.Name == "x-ratelimit-global") != null) { Manager.GlobalTimeout = new Task <bool>(() => true); Thread.Sleep(retryAfter); Manager.GlobalTimeout = null; } if (res.IsSuccessful) { return(ParseResponse(res)); } switch (res.StatusCode) { case HttpStatusCode.TooManyRequests: // this.manager.rest.emit(RESTManagerEvents.Debug, `429 hit on route: ${routeID.route}\nRetrying after: ${retryAfter}ms`); Console.WriteLine("R8 LIMIT!"); await Task.Delay(retryAfter); return(await MakeRequest(routeId, url, options, retries)); case HttpStatusCode.InternalServerError when retries != Client.ClientOptions.RestOptions.Retries: return(await MakeRequest(routeId, url, options, ++retries)); case HttpStatusCode.InternalServerError: Console.WriteLine("ERROR BITCH!"); break; default: { if (res.StatusCode != HttpStatusCode.BadRequest) { return(null); } var data = await ParseResponse(res); Console.WriteLine("MALFORMED DATA!"); // throw new DiscordAPIError(data.message, data.code, res.status, options.method as string, url); return(null); } } return(null); }
public async Task DeleteRouteByIdentiferAsync(RouteIdentifier identifier) { var userId = User.GetUserId(); await _routeRepository.DeleteRouteByIdentifierAsync(identifier, userId); }
public void LogResponseError(string message, HttpRequestHeaders httpRequestHeaders, HttpContentHeaders contentHeaders, AggregateException aggregateException, RouteIdentifier routeIdentifier) { if (_configuration.IsLoggingEnabled) { try { using (var logStream = new StreamWriter(GetCloudBlockBlob(routeIdentifier, "request").OpenWrite())) { logStream.WriteLine(message); var headers = httpRequestHeaders.GetHeadersLoggingString(contentHeaders); logStream.WriteLine(headers); var exceptionString = aggregateException.GetLoggingString(); logStream.WriteLine(exceptionString); } } catch (Exception) { //Swallow exeption to ensure logging errors don't cause the request to fail } } }
public Routing GetRouting(Uri requestUri, HttpMethod method, string ipAddress, string requestContent, RouteIdentifier routeIdentifier) { var isCompatabilityWebServicePrimary = IsCompatabilityWebServicePrimary(requestUri, requestContent); return(new Routing { Routes = new List <Route> { new Route(new Uri(_configuration.NasAvWebServiceRootUri.OriginalString + requestUri.PathAndQuery), new RouteIdentifier(routeIdentifier, "nasavwebservice"), !isCompatabilityWebServicePrimary), new Route(GetCompatabilityWebServiceUrl(requestUri), new RouteIdentifier(routeIdentifier, "compatabilitywebservice"), isCompatabilityWebServicePrimary) }, // The service is https-only and our configuration set accordingly, but the service thinks it is running under http, thus rewriting WSDL needs to be from http RewriteFrom = Regex.Replace(_configuration.NasAvWebServiceRootUri.OriginalString, "^https:", "http:"), RewriteTo = requestUri.Scheme + Uri.SchemeDelimiter + requestUri.Authority }); }
public Routing GetRouting(Uri requestUri, HttpMethod method, string ipAddress, string requestContent, RouteIdentifier routeIdentifier) { var routing = new Routing(); routing.Routes = new List <Route>(); if ((requestUri.PathAndQuery + "/").StartsWith("/news/")) { routing.Routes.Add(new Route("http://bbc.co.uk" + requestUri.PathAndQuery, new RouteIdentifier("bbc"), true)); } else { routing.Routes.Add(new Route("https://webapp.services.coventry.ac.uk/" + requestUri.PathAndQuery.Substring(1), new RouteIdentifier("coventryuniversitywebappservices"), true)); routing.RewriteFrom = "https://webapp.services.coventry.ac.uk/"; routing.RewriteTo = "://" + requestUri.Host; } return(routing); }
public Routing GetRouting(Uri requestUri, HttpMethod method, string ipAddress, string requestContent, RouteIdentifier routeIdentifier) { return(new Routing { Routes = new List <Route> { new Route("http://news.bbc.co.uk", new RouteIdentifier("bbcnews"), true) } }); }
public void LogResponseContent(Stream content, RouteIdentifier routeIdentifier) { }
public void LogRequest(HttpRequestMessage request, string requestContent, RouteIdentifier routeIdentifier) { }