Quantcast
Channel: ASPNETWebStack Issue Tracker Rss Feed
Viewing all 7215 articles
Browse latest View live

Created Unassigned: Improve experience of using UriPathExtensionMapping with AttributeRouting [1086]

$
0
0
__Scenario__:
Its popular with users to use Uri path extensions to get content in a particular format even though one could use Accept headers. My understanding is that this is for users from browsers who cannot modify headers of the request that a browser sends.

I have the following ValuesController which uses AttributeRouting.

```
[RoutePrefix("api/values")]
public class ValuesController : ApiController
{
[HttpGet("")]
public IEnumerable<string> GetAll()
{
return new string[] { "value1", "value2" };
}

[HttpGet("{id}")]
public string GetSingle(int id)
{
return "value";
}

[HttpPost("")]
public void Post([FromBody]string value)
{
}

[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}

[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
```

Now I would like to use UriPathExtensionMapping for the GetAll and GetSingle actions. I would like users to do "/api/values.xml" and "/api/values/10.xml" etc.

__Issue__:
I cannot do something like below for GetAll action, because we append the RoutePrefix with the action's RouteTemplate (ex: api/values/.{ext}). As you can notice this is not the desired behavior.

```
[HttpGet("")]
[HttpGet(".{ext}")]
public IEnumerable<string> GetAll()
```

__Workaround__:

__NOTE__: Here I would be placing the controller name on each of the individual action's route template, which ideally I would like to avoid.

```
[RoutePrefix("api")]
public class ValuesController : ApiController
{
[HttpGet("values")]
[HttpGet("values.{ext}")]
public IEnumerable<string> GetAll()
{
return new string[] { "value1", "value2" };
}

[HttpGet("values/{id}")]
[HttpGet("values/{id}.{ext}")]
public string GetSingle(int id)
{
return "value";
}

[HttpPost("values")]
public void Post([FromBody]string value)
{
}

[HttpPut("values/{id}")]
public void Put(int id, [FromBody]string value)
{
}

[HttpDelete("values/{id}")]
public void Delete(int id)
{
}
}
```

__Proposal__:
Could we introduce ability for individual actions' route attribute to ignore the RoutePrefix.

Example(__Notice__ the GetAll action's attribute):
```
[RoutePrefix("api/values")]
public class ValuesController : ApiController
{
[HttpGet("")]
[HttpGet("~/api/values.{ext}")] //NOTE
public IEnumerable<string> GetAll()
{
return new string[] { "value1", "value2" };
}

[HttpGet("{id}")]
[HttpGet("{id}.{ext}")]
public string GetSingle(int id)
{
return "value";
}

[HttpPost("")]
public void Post([FromBody]string value)
{
}

[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}

[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
```

Created Unassigned: WebApi model binding fails when Property inherits from Dictionary [1087]

$
0
0
When I submit the webpage data to the ApiController the binding will not bind the MyDictionary<string, string> ExtraData property. If I change the property to Dictionary<string, string> ExtraData property then Api controller will bind correctly. However this is not an option because the MyDictionary class has additional functionality that I need. I have four buttons on the webpage for submitting data to the apicontroller. The red button fails to load the MyDictionary ExtraData property. The 3 green buttons load MyDictionary and Dictionary property ok.

Edited Unassigned: Improve experience of using UriPathExtensionMapping with AttributeRouting [1086]

$
0
0
__Scenario__:
Its popular with users to use Uri path extensions to get content in a particular format even though one could use Accept headers. My understanding is that this is for users from browsers who cannot modify headers of the request that a browser sends.

I have the following ValuesController which uses AttributeRouting.

```
[RoutePrefix("api/values")]
public class ValuesController : ApiController
{
[HttpGet("")]
public IEnumerable<string> GetAll()
{
return new string[] { "value1", "value2" };
}

[HttpGet("{id}")]
public string GetSingle(int id)
{
return "value";
}

[HttpPost("")]
public void Post([FromBody]string value)
{
}

[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}

[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
```

Now I would like to use UriPathExtensionMapping for the GetAll and GetSingle actions. I would like users to do "/api/values.xml" and "/api/values/10.xml" etc.

__Issue__:
I cannot do something like below for GetAll action, because we append the RoutePrefix with the action's RouteTemplate (ex: api/values/.{ext}). As you can notice this is not the desired behavior.

```
[HttpGet("")]
[HttpGet(".{ext}")]
public IEnumerable<string> GetAll()
```

__Workaround__:

__NOTE__: Here I would be placing the controller name on each of the individual action's route template, which ideally I would like to avoid.

```
[RoutePrefix("api")]
public class ValuesController : ApiController
{
[HttpGet("values")]
[HttpGet("values.{ext}")]
public IEnumerable<string> GetAll()
{
return new string[] { "value1", "value2" };
}

[HttpGet("values/{id}")]
[HttpGet("values/{id}.{ext}")]
public string GetSingle(int id)
{
return "value";
}

[HttpPost("values")]
public void Post([FromBody]string value)
{
}

[HttpPut("values/{id}")]
public void Put(int id, [FromBody]string value)
{
}

[HttpDelete("values/{id}")]
public void Delete(int id)
{
}
}
```

__Impact__:
1. User experience would be bad as users could be having many controllers and if they like to support Uri path extension on all of them, then they have to do the above workaround.
2. Alternatively, I can think of having non-attribute routes in WebApiConfig.cs which could probably fix this, what do you think?
Example:
```
config.Routes.MapHttpRoute(
name: "DefaultApiWithExtension2",
routeTemplate: "api/{controller}/{id}.{ext}"
);

config.Routes.MapHttpRoute(
name: "DefaultApiWithExtension1",
routeTemplate: "api/{controller}.{ext}");

config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
```

__Proposal__:
Could we introduce ability for individual actions' route attribute to ignore the RoutePrefix.

Example(__Notice__ the GetAll action's attribute):
```
[RoutePrefix("api/values")]
public class ValuesController : ApiController
{
[HttpGet("")]
[HttpGet("~/api/values.{ext}")] //NOTE
public IEnumerable<string> GetAll()
{
return new string[] { "value1", "value2" };
}

[HttpGet("{id}")]
[HttpGet("{id}.{ext}")]
public string GetSingle(int id)
{
return "value";
}

[HttpPost("")]
public void Post([FromBody]string value)
{
}

[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}

[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
```

Edited Issue: Support external creation of IEdmModel [802]

$
0
0
Currently for an IEdmModel to be used with the ODataMediaTypeFormatters, it MUST be built using an ODataModelBuilder. This is because the formatters require an annotation on IEdmEntitySet using a class called EntitySetLinkBuilderAnnotation, and this class is internal.

However there are clearly scenarios where people want to create there own IEdmModel. For example from:
- DbContext
- IDataServiceMetadataProvider

I see a layered solution.

First make EntitySetLinkBuilderAnnotation public, and give it a constructor that doesn't require an EntitySetConfiguration (you don't necessarily have one of those).
Second make the Formatter annotate the model if an EntitySetLinkBuilderAnnotation is not found, this way if a service follows conventions (most do) then you don't need to annotate the IEdmModel you build at all.

The first part has already been completed with this [commit](https://aspnetwebstack.codeplex.com/SourceControl/changeset/bd007387c45bf3f7f1b3b9ced7b476e601190b58)


Closed Feature: Expose ExpectedType and ActualType from IDelta [1078]

$
0
0
When looking at a delta internally we know the actual type and expected type, but this code is internal.
It is very useful, and is needed to create a bridge between WCF Data Services and Web API and is locked away.

I talked to Eilon about this and he was okay with making this change for RTM.
Comments: This has been addressed by this [commit](https://aspnetwebstack.codeplex.com/SourceControl/changeset/bd007387c45bf3f7f1b3b9ced7b476e601190b58) NOTE: the actual solution was to make Delta<> inherit from a new abstract base class called Delta, and put the properties on their (EntityType for actual Type and ExpectedType).

Edited Unassigned: [WebApiOnOwin]Indicate Soft 404 Not Found response when CORS is enabled and route doesn't match [986]

$
0
0
In SelfHost/OwinHost, route matching normally happens at HttpRoutingDispatcher, but when CORS is enabled, we add a message handler where we do route matching for pre-flight requests. Since this message handler runs before the HttpRoutingDispatcher, we need to make sure that we set the property on the response to indicate _Soft_ 404 Not Found response when a route doesn't match.

This bug is related to issue # 985. Once its fixed, we need to add the property "MS_NoRouteMatched" in the 404 Not Found response to indicate that its a soft response.

AttributeBasedPolicyProviderFactory.cs:
```
private static HttpActionDescriptor SelectAction(HttpRequestMessage request)
{
HttpConfiguration config = request.GetConfiguration();
if (config == null)
{
throw new InvalidOperationException(SRResources.NoConfiguration);
}

IHttpRouteData routeData = request.GetRouteData();
if (routeData == null)
{
routeData = config.Routes.GetRouteData(request);
if (routeData == null)
{
throw new InvalidOperationException(SRResources.NoRouteData);
}
request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
}
```

Commented Unassigned: [WebApiOnOwin]Indicate Soft 404 Not Found response when CORS is enabled and route doesn't match [986]

$
0
0
In SelfHost/OwinHost, route matching normally happens at HttpRoutingDispatcher, but when CORS is enabled, we add a message handler where we do route matching for pre-flight requests. Since this message handler runs before the HttpRoutingDispatcher, we need to make sure that we set the property on the response to indicate _Soft_ 404 Not Found response when a route doesn't match.

This bug is related to issue # 985. Once its fixed, we need to add the property "MS_NoRouteMatched" in the 404 Not Found response to indicate that its a soft response.

AttributeBasedPolicyProviderFactory.cs:
```
private static HttpActionDescriptor SelectAction(HttpRequestMessage request)
{
HttpConfiguration config = request.GetConfiguration();
if (config == null)
{
throw new InvalidOperationException(SRResources.NoConfiguration);
}

IHttpRouteData routeData = request.GetRouteData();
if (routeData == null)
{
routeData = config.Routes.GetRouteData(request);
if (routeData == null)
{
throw new InvalidOperationException(SRResources.NoRouteData);
}
request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
}
```
Comments: Yao, can you take a look at this? It's not clear exactly what work needs to be done here or in issue #985 (also assigned to you). If either of these bugs don't seem like there needs to be any change, please email triage with your thoughts.

Edited Feature: Expose async on filters in 4.5 [922]

$
0
0

As we are moving to 4.5 we should also move the attribute filter implementations (see for example AuthorizationFilterAttribute) to expose async in addition to just sync. In v1 we hid async part so well that you can’t actually get at it unless you implement the raw interface directly which makes it more of a pain than necessary.

Closed Issue: Make UrlHelper.GenerateUrl "collection-aware" [636]

$
0
0
It's a fairly common requirement to want to generate a url which handles collections in a way that is compatible with the default model binder, e.g. see:

http://stackoverflow.com/questions/717690/asp-net-mvc-pass-array-object-as-a-route-value-within-html-actionlink
http://stackoverflow.com/questions/8391055/passing-an-array-to-routevalues-and-have-it-render-model-binder-friendly-url

Basically this would involve special-casing IEnumerable values in a RouteValueDictionary, and generating repeated keys. For example:

routeValues.Add("productId", new[] {1, 3, 5});

would result in a Url being generated something like:

http://server/Controller/Action?productId=1&productId=3&productId=5

As far as I can see with Reflector, the generation of the Url is currently delegated to System.Web.Routing.ParsedRoute.Bind(), which is a Framework class and isn't going to change any time soon.

But this could be achieved in the MVC framework by modifying Url.GenerateUrl as follows:

- Clone the "routeValues" dictionary, and remove all elements whose value is IEnumerable. Pass the cloned dictionary to "routeCollection.GetVirtualPathForArea(requestContext, routeName, values)", which will generate a Url that excludes the IEnumerable routeValues.
- Append the IEnumerable routeValues as queryString parameters.


By making this change in UrlHelper.GenerateUrl, it would automatically become available for all helpers that use UrlHelper.GenerateUrl (UrlHelper.Action, LinkExtensions.ActionLink, etc).
Comments: Hello - We have reviewed this issue and while we agree it would be a cool feature, it is not a sufficiently high priority for us to design, develop, and test it. We recommend using a custom helper method using logic similar to what is described in this issue.

Closed Issue: Dynamic suffixes for Display Modes and Views location caching [615]

$
0
0
By implementing IDisplayMode you can transform a virtual path pretty much in any way you want. The problem is in Views location caching, which uses the DisplayModeId to cache a location (AppendDisplayModeToCacheKey). This means the transformed path must always be the same and cannot vary depending on the HttpContext.

One use case would be a Display Mode for localization. Based on the user's browser settings, and by setting uiCulture="auto" on Web.config, you could have a display mode that transforms a path like ~/Views/Home/Index.cshtml to ~/Views/Home/Index.es.cshtml. CanHandleContext would always return true.

What I would do is add a new string property to DisplayInfo and use that in the cache key, and fallback to using DisplayModeId if null.
Comments: Hi - We have reviewed this suggestion and we feel that this feature is not well-aligned with Display Modes, which are designed to be static and finite in nature.

Created Unassigned: Exception inside PushStreamContent won't be caught by ExceptionFilterAttribute? [1089]

$
0
0
Hi,

I'm using PushStreamContent in asp.net web api to stream data to the client, I have derived from ExceptionFilterAttribute to capture all the exceptions and return standard HttpError to client. However,
I found that exception thrown inside PushStreamContent won't be caught by ExceptionFilterAttribute, it will return a "text/html" error message to client.
Below is the sample code:

response.Content = new PushStreamContent(
async (outputStream, httpContent, transportContext) =>
{
try
{
// do something and throw exception here

}
catch (Exception ex)
{
throw;
}
finally
{
outputStream.Close();
}
});

Commented Unassigned: Exception inside PushStreamContent won't be caught by ExceptionFilterAttribute? [1089]

$
0
0
Hi,

I'm using PushStreamContent in asp.net web api to stream data to the client, I have derived from ExceptionFilterAttribute to capture all the exceptions and return standard HttpError to client. However,
I found that exception thrown inside PushStreamContent won't be caught by ExceptionFilterAttribute, it will return a "text/html" error message to client.
Below is the sample code:

response.Content = new PushStreamContent(
async (outputStream, httpContent, transportContext) =>
{
try
{
// do something and throw exception here

}
catch (Exception ex)
{
throw;
}
finally
{
outputStream.Close();
}
});
Comments: This is an expected behavior I think. The PushStreamContent's delegate is invoked way below in the stack (at hosting layers), so any exception thrown at this layer wouldn't be caught in exception filters.

Edited Issue: Tracing should trace warning not error for HttpResponseException [949]

$
0
0
The default trace writers do not currently special-case HttpResponseException, and therefore trace at TraceLevel.Error. This is confusing, because it means simple user error in the URI will generate an error trace. This makes it hard to use the tracing layer to report errors. The System.Diagnostic, ETW, and Memory trace writers all try to correct for this to convert it back into a warning.

Recommend the default tracing logic should trace at TraceLevel.Warn whenever the exception is HttpResponseException.

Edited Issue: Asynchronous child actions not supported [601]

$
0
0
Attempting to use a child action that's marked as "async" in its method declaration results in the exception "HttpServerUtility.Execute blocked while waiting for an asynchronous operation to complete.".

Currently MVC 4 does not support asynchronous child actions, nor does it provide any graceful way of handling the request synchronously other than having to write a synchronous version of each action.

So far I've had to resort to hacks (http://stackoverflow.com/questions/13166180/force-synchronous-execution-of-asynchronous-action-in-asp-net-mvc-4/13210277#13210277) in order to force a blocking call.

A better solution might be to create a custom MvcHandler implementation that does this although MVC does not exactly make this easy with so many of the classes used in the current child action pipeline being internal/sealed.

Of course the ideal solution would be to have async child actions supported, but it would be nice to have a clean workaround until this is done.

There is also a request open on Uservoice for this, please vote. http://aspnet.uservoice.com/forums/41201-asp-net-mvc/suggestions/3233329-support-asynchronous-child-actions.

Code to reproduce:

public class HomeController : Controller
{
//
// GET: /Home/

public ActionResult Index()
{
return View();
}

[ChildActionOnly]
public async Task<ActionResult> Widget()
{
await Task.Delay(1000);
return Content("widget");
}

}

View:

@{
ViewBag.Title = "Index";
}

<h2>Index</h2>

@Html.Action("widget")

Edited Feature: Global error handler for Web API [1001]

$
0
0
If my Web API application is going to return an error to a client, I want to know about it.

I want to set up something like ELMAH to notify me when the problem occurs, and I want full details of the error, including exception stack trace, logged on the server.

In MVC, the __Application_Error__ event is how you achive this, but in Web Api this event often isn't triggered because exceptions are converted into **HttpResponseMessage**s higher in the call stack, and because some error conditions create a **HttpResponseMessage** directly without using an exception. One source of this is __HttpControllerDispatcher.SendAsync__, which catches all exceptions and turns them into error responses, discarding the exception information.

Here are some examples of exceptions that I am interested in handling:

* Exceptions thrown from action methods and action filters
* Exceptions thrown when creating a controller instance
* Exceptions due to routing errors, bad requests, authentication problems, and invalid OData queries
* Exceptions thrown when an IQueryable returned by an action method fails to execute
* Exceptions thrown by custom message handlers and formatters

When I started using Web API, I was surprised when my __Application_Error__ handler did not pick up an action method exception. I added a global __ExceptionFilterAttribute__ to take care of that, but then found several other categories of errors that could be returned to the client and still leave silence in the server logs.

In a production environment, relying on clients to report errors and having no error information available on the server will make diagnosing problems reactive and difficult.

Please provide a global error handling event for Web API, so that error details can be logged and notifications sent. The event should provide access to unhandled exceptions and non-success **HttpResponseException**s and **HttpResponseMessage**s. In the case of **HttpError** objects created from an exception, the exception details should be retained for the error handling event.

Edited Issue: CORS message handler should return 404 in case route doesn't match [985]

$
0
0
This scenario effects WebApi-on-Owin's _Soft_ 404 Not Found responses.

In SelfHost/OwinHost, route matching normally happens at HttpRoutingDispatcher, but when CORS is enabled, we add a message handler where we do route matching for pre-flight requests. This message handler runs before the HttpRoutingDispatcher.

Currently CORS handler(or specifically AttributeBasedPolicyProviderFactory.cs) returns a 500 Internal Server Error. We need to modify it to return a 404 Not Found response.

AttributeBasedPolicyProviderFactory.cs:
```
private static HttpActionDescriptor SelectAction(HttpRequestMessage request)
{
HttpConfiguration config = request.GetConfiguration();
if (config == null)
{
throw new InvalidOperationException(SRResources.NoConfiguration);
}

IHttpRouteData routeData = request.GetRouteData();
if (routeData == null)
{
routeData = config.Routes.GetRouteData(request);
if (routeData == null)
{
throw new InvalidOperationException(SRResources.NoRouteData);
}
request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
}
```

Edited Feature: Add overloads taking a CancellationToken parameter [983]

$
0
0
Currently some of TAP-based methods in the aspnetwebstack libraries does not have overloads with _CancellationToken_ parameter. For instance following classes:

_System.Net.Http_: HttpContent and successors.
```
HttpContent: ReadAsStringAsync, ReadAsByteArrayAsync, ReadAsStreamAsync, etc.
```
_System.Net.Http.Formatting_: HttpContent extensions.
```
HttpContentMessageExtensions: ReadAsHttpRequestMessageAsync
HttpContentMultipartExtensions: ReadAsMultipartAsync
HttpContentFormDataExtensions: ReadAsFormDataAsync
HttpContentMessageExtensions: ReadAsHttpRequestMessageAsync
MultipartStreamProvider: ExecutePostProcessingAsync
```
_System.Net.Http.Formatting_: MediaTypeFormatter and successors.
```
MediaTypeFormatter: ReadFromStreamAsync, WriteToStreamAsync
BufferedMediaTypeFormatter
FormUrlEncodedMediaTypeFormatter
JsonMediaTypeFormatter
XmlMediaTypeFormatter
```
_System.Web.Http_: Model binder which breaks cancellable pipeline.
```
FormatterParameterBinding: ReadContentAsync
```
Methods of above classes potentially may be executed for a long time period. In addition if we're trying to create custom _MediaTypeFormatter_ we unable to cancel _ReadFromStreamAsync_ and _WriteToStreamAsync_ methods.

I suppose that it will be great if such methods will contain an overloads with a _CancellationToken_ parameter, which should be properly integrated into Web API message processing pipeline.

Code sample how it can be refactored: https://aspnetwebstack.codeplex.com/SourceControl/network/forks/dtretyakov/CancellationToken/contribution/4472

P.S. Discussions looks dead - no answers for over 2 weeks: http://aspnetwebstack.codeplex.com/discussions/438601

Edited Issue: Action selection requires a default value on a parameter even when the parameter is marked as optional [966]

$
0
0
This is especially an issue for attribute routing:

[HttpGet("{id?}")]
public string Get(string id) { }

doesn't work unless you add a default value

[HttpGet("{id?}")]
public string Get(string id = null) { }

Edited Issue: [CORS] Trace level of preflight request rejected should be Warning instead of Information. [965]

$
0
0
__Issue__
Today if a preflight is rejected. 400 is returned and following is the tracing output
```
iisexpress.exe Information: 0 : [2013-04-02T23:39:57.9893186Z] Level=Info, Kind=Begin, Category='System.Web.Http.Cors', Id=00000000-0000-0000-0000-000000000000, Operation=CorsEngine.EvaluatePolicy
iisexpress.exe Information: 0 : [2013-04-02T23:39:57.9993268Z] Level=Info, Kind=End, Category='System.Web.Http.Cors', Id=00000000-0000-0000-0000-000000000000, Message='CorsResult returned: 'IsValid: False, AllowCredentials: False, PreflightMaxAge: null, AllowOrigin: http://localhost:28793, AllowExposedHeaders: {}, AllowHeaders: {}, AllowMethods: {GET}, ErrorMessages: {The collection of headers 'dataserviceversion,accept,origin' is not allowed.}'', Operation=CorsEngine.EvaluatePolicy
```
Note that the level is Information. Though it is a customer failure, it is still worth to bump the level to warning so user can filter the trace records but still keep failed CORS request records.

Contrast with how we dealing with 404 in tracing. We set the level to warning when controller selection is failed.
```
iisexpress.exe Warning: 0 : Message='UserMessage='No HTTP resource was found that matches the request URI 'http://localhost:27032/api/values'.', MessageDetail='No type was found that matches the controller named 'values'.'', Operation=DefaultHttpControllerSelector.SelectController, Status=404 (NotFound), Exception=System.Web.Http.HttpResponseException: Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details.
at System.Web.Http.Dispatcher.DefaultHttpControllerSelector.SelectController(HttpRequestMessage request)
at System.Web.Http.Tracing.Tracers.HttpControllerSelectorTracer.<>c__DisplayClass3.<System.Web.Http.Dispatcher.IHttpControllerSelector.SelectController>b__1()
at System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEnd(ITraceWriter traceWriter, HttpRequestMessage request, String category, TraceLevel level, String operatorName, String operationName, Action`1 beginTrace, Action execute, Action`1 endTrace, Action`1 errorTrace)
```

In a simplified tracing model:
When the http status are 1xx to 3xx: the level should be information
When the http status are 4xx, the issues are caused by customer, level should be warning
When the http status are 5xx, the issues are server, level should be error

Edited Issue: Expose ability convert FilterClause to Linq Expression [950]

$
0
0
We can parse a filter to a FilterClause but there is no easy way to convert that FilterClause to an Expression;

ex: var filterClause = Microsoft.Data.OData.Query.ODataUriParser.ParseFilter(filter, model, entityType);

it would be nice if we can then call filterClause.ToLinqExpression();
Viewing all 7215 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>