webapi batching and delegating handlers - asp.net-web-api

based on my last post I was able to get batching working... until a certain point. In addition to registering the route specific handler I also have 2 delegating handlers
Authenticate the user
logging
the batch handler goes through the delegating handlers authenticating the user and logging the request. when the messagehandlerinvoker starts to send the child/nested requests the following exception is thrown.
System.ArgumentException was unhandled by user code
HResult=-2147024809
Message=The 'DelegatingHandler' list is invalid because the property 'InnerHandler' of 'AuthenticationMessageHandler' is not null.
Parameter name: handlers
Source=System.Net.Http.Formatting
ParamName=handlers
StackTrace:
at System.Net.Http.HttpClientFactory.CreatePipeline(HttpMessageHandler innerHandler, IEnumerable`1 handlers)
at System.Web.Http.HttpServer.Initialize()
at System.Web.Http.HttpServer.<EnsureInitialized>b__3()
at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory)
at System.Threading.LazyInitializer.EnsureInitialized[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory)
at System.Web.Http.HttpServer.EnsureInitialized()
at System.Web.Http.HttpServer.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at RoutingRequest.Service.Startup.BatchMessageHandler.<>c__DisplayClassd.<PrcoessRequest>b__b(Task`1 m) in C:\CEI\Clients\Footlocker.com\FL - Vendor Routing Portal\source\RoutingRequest.Service\Startup\BatchMessageHandler.cs:line 45
at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
InnerException:
is there a config option I am missing, or do I need to bypass the delegating handlers?
edit
here is my authentication handler.
public class AuthenticationMessageHandler
: DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
SetCurrentUser(request);
return base.SendAsync(request, cancellationToken);
}
private void SetCurrentUser(HttpRequestMessage request)
{
var values = new List<string>().AsEnumerable();
if (request.Headers.TryGetValues("routingrequest-username", out values) == false) return;
var username = values.First();
var user = Membership.GetUser(username, true);
if (user == null)
{
var message = string.Format("membership information for '{0}' could not be found.", username);
throw new HttpRequestException(message);
}
var roles = Roles.GetRolesForUser(username);
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(user.UserName), roles);
}
}
based on Kiran's answer a subclassed httpserver fixes one issue and introduces another. My roles provider is getting a null reference exception. looking into that now.

That blog post correctly identifies the problem, but there is a simpler solution if you are configuring OWIN using a Startup or OwinStartup class:
Change the OWIN configuration call from
UseWebApi(this IAppBuilder builder, HttpConfiguration configuration);
to
UseWebApi(this IAppBuilder builder, HttpServer httpServer);
so that your batch handler and the OWIN pipeline are using the same HttpServer instance.
The root cause of this is that many of the batching articles/examples (eg http://bradwilson.typepad.com/blog/2012/06/batching-handler-for-web-api.html ) create a new HttpServer for batching in addition to the main HttpServer that is handling HTTP requests; and both HttpServers are using the same HttpConfiguration.
When each HttpServer is initialized the first time it receives requests, it creates a pipeline of handlers (in HttpClientFactory.CreatePipeline) by reversing all the configured delegating handlers (eg tracing handlers, or other proxy-type handlers), and terminating the pipeline with the Web API dispatcher.
If you don't have any delegating handlers configured, then this problem won't bite you - you can have 2 HttpServer objects that use the same HttpConfiguration.
But if you have any delegating handlers explicitly or implicitly configured (eg by enabling Web API Tracing), then Web API can't build the 2nd pipeline - the delegating handlers are already linked in the first pipeline - and this exception is thrown on the first request to the 2nd HttpServer.
This exception should absolutely be more clear about what is going on. Better yet, this problem shouldn't even be possible - configuration should be configuration, not individual handlers. The configuration could be a factory for delegating handlers. But I digress...
While the issue is kinda hard to figure out, there's a pretty easy fix:
If you're using OWIN, pass the same HttpServer as you use in the batch handler to the OWIN pipeline via UseWebApi(this IAppBuilder builder, HttpServer httpServer);
If you're using IIS + Web API (no OWIN Startup class), pass GlobalConfiguration.DefaultServer to your batch handler, to avoid creating a new HttpServer
Here's an example OWIN startup class that creates a single HttpServer and passes it to both the batch handler, and Web API. This example uses to OData batch handler:
[assembly: OwinStartup(typeof(My.Web.OwinStartup))]
namespace My.Web
{
/// <summary>
/// OWIN webapp configuration.
/// </summary>
public sealed class OwinStartup
{
/// <summary>
/// Configure all the OWIN modules that participate in each request.
/// </summary>
/// <param name="app">The OWIN appBuilder</param>
public void Configuration(IAppBuilder app)
{
HttpConfiguration webApiConfig = new HttpConfiguration();
webApiConfig.MapHttpAttributeRoutes();
HttpServer webApiServer = new HttpServer(webApiConfig);
// Configure batch handler
var batchHandler = new DefaultODataBatchHandler(webApiServer);
webApiConfig.Routes.MapODataServiceRoute("ODataRoute",
"odata",
BuildEdmModel(),
new DefaultODataPathHandler(),
ODataRoutingConventions.CreateDefault(),
batchHandler);
app.UseWebApi(webApiServer);
}
private EdmModel BuildEdmModel()
{
// ...
}
}
}

I've had this error without batching. I made an HttpClientFactory of my own and it takes in a HandlerFactory, also my own.
It calls the HandlerFactory.Create() method in the constructor and stores the resulting handlers that it made.
These are passed to the System.Net.Http.HttpClientFactory.Create(...) method whenever the factory needs to make a new HttpClient.
But it's then only good for a single call because the handlers themselves are mutated by the .NET code leaving them in a state that means they cannot be reused.
I altered my constructor so that it doesn't create the handlers up front, but each time. It now works.

Related

issue with Spring and asynchronous controller + HandlerInterceptor + IE/Edge

I am working on a Spring application that serves up REST endpoints. One of the endpoints essentially acts as a proxy between the HTML client and a third party cloud storage provider. This endpoint retrieves files from the storage provider and proxies them back to the client. Something like the following (note there is a synchronous and asynchronous version of the same endpoint):
#Controller
public class CloudStorageController {
...
#RequestMapping(value = "/fetch-image/{id}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<byte[]> fetchImageSynchronous(#PathVariable final Long id) {
final byte[] imageFileContents = this.fetchImage(id);
return ResponseEntity.ok().body(imageFileContents);
}
#RequestMapping(value = "/fetch-image-async/{id}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public Callable<ResponseEntity<byte[]>> fetchImageAsynchronous(#PathVariable final Long id) {
return () -> {
final byte[] imageFileContents = this.fetchImage(id);
return ResponseEntity.ok().body(imageFileContents);
};
}
private byte[] fetchImage(final long id) {
// fetch the file from cloud storage and return as byte array
...
}
...
}
Due to the nature of the client app (HTML5 + ajax) and how this endpoint is used, user authentication is supplied to this endpoint differently that the other endpoints. To handle this, a HandlerInterceptor was developed to deal with authentication for this endpoint:
#Component("cloudStorageAuthenticationInterceptor")
public class CloudStorageAuthenticationInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
// examine the request for the authentication information and verify it
final Authentication authenticated = ...
if (authenticated == null) {
try {
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
} catch (IOException e) {
throw new RuntimeException(e);
}
return false;
}
else {
try {
request.login(authenticated.getName(), (String) authenticated.getCredentials());
} catch (final ServletException e) {
throw new BadCredentialsException("Bad credentials");
}
}
return true;
}
}
The interceptor is registered like this:
#Configuration
#EnableWebMvc
public class ApiConfig extends WebMvcConfigurerAdapter {
#Autowired
#Qualifier("cloudStorageAuthenticationInterceptor")
private HandlerInterceptor cloudStorageAuthenticationInterceptor;
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(this.cloudStorageAuthenticationInterceptor)
.addPathPatterns(
"/fetch-image/**",
"/fetch-image-async/**"
);
}
#Override
public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(this.asyncThreadPoolCoreSize);
executor.setMaxPoolSize(this.asyncThreadPoolMaxSize);
executor.setQueueCapacity(this.asyncThreadPoolQueueCapacity);
executor.setThreadNamePrefix(this.asyncThreadPoolPrefix);
executor.initialize();
configurer.setTaskExecutor(executor);
super.configureAsyncSupport(configurer);
}
}
Ideally, the image fetching would be done asynchronously (using the /fetch-image-asyc/{id} endpoint) because it has to call a third party web service which could have some latency.
The synchronous endpoint (/fetch-image/{id}) works correctly for all browsers. However, if using the asynchronous endpoint (/fetch-image-async/{id}), Chrome and Firefox work as expect.
However, if the client is Microsoft IE or Microsoft Edge, we seem some strange behavior. The endpoint is called correctly and the response sent successfully (at least from the server's viewpoint). However, it seems that the browser is waiting for something additional. In the IE/Edge DevTools window, the network request for the image shows as pending for 30 seconds, then seems to timeout, updates to successful and the image is successfully display. It also seems the connection to the server is still open, as the server side resources like database connections are not released. In the other browsers, the async response is received and processed in a second or less.
If I remove the HandlerInterceptor and just hard-wire some credentials for debugging, the behavior goes away. So this seems to have something to with the interaction between the HandlerInterceptor and the asynchronous controller method, and is only exhibited for some clients.
Anyone have a suggestion on why the semantics of IE/Edge are causing this behavior?
Based on your description, there are some different behaviors when using IE or Edge
it seems that the browser is waiting for something additional
the connection seems still open
it works fine if remove HandlerInterceptor and use hard code in auth logic
For the first behavior, I would suggest you use fiddler to trace all http requests. It is better if you could compare two different actions via fiddler (1) run on chrome, 2) run on edge ). Check all http headers in requests and responses carefully to see whether there is some different part. For the other behaviors, I would suggest you write logs to find which part spend the most time. It will provide you useful information to troubleshot.
After much tracing on the server and reading through the JavaDocs comments for AsyncHandlerInterceptor, I was able to resolve the issue. For requests to asynchronous controller methods, the preHandle method of any interceptor is called twice. It is called before the request is handed off to the servlet handling the request and again after the servlet has handled the request. In my case, the interceptor was attempting to authenticate the request for both scenarios (pre and post request handling). The application's authentication provider checks credentials in a database. For some reason if the client is IE or Edge, the authentication provider was unable to get a database connection when called from preHandle in the interceptor after the servlet handled the request. The following exception would be thrown:
ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataAccessResourceFailureException: Could not open connection; nested exception is org.hibernate.exception.JDBCConnectionException: Could not open connection] with root cause
java.sql.SQLTransientConnectionException: HikariPool-0 - Connection is not available, request timed out after 30001ms.
So the servlet would successfully handle the request and send a response, but the filter would get hung up for 30 seconds waiting for the database connection to timeout on the post processing called to preHandle.
So for me, the simple solution was to add a check in preHandle if it is being called after the servlet has already handled the request. I updated the preHandle method as follows:
#Override
public boolean preHandle(final HttpServletRequest pRequest, final HttpServletResponse pResponse, final Object pHandler) {
if (pRequest.getDispatcherType().equals(DispatcherType.REQUEST)) {
... perform authentication ...
}
return true;
}
That solved the issue for me. It doesn't explain everything (i.e., why only IE/Edge would cause the issue), but it seems that preHandle should only do work before the servlet handles the request anyways.

Trying to use IPipelineBehavior with ASP.NET Core - get System.InvalidCastException

I'm using Mediatr 3's new features for Pipeline Behaviors. Problem is, when I wire them into Mediatr, exceptions (System.InvalidCastException) get thrown for every .Send() call. Handlers get called fine, but when its time for the pipeline behaviors to get called, it fails.
Here's my setup:
services.AddScoped<SingleInstanceFactory>(p => t => p.GetRequiredService(t));
services.AddScoped<MultiInstanceFactory>(p => t => GetRequiredServices(p,t));
services.AddScoped(typeof( IPipelineBehavior<, >), typeof(AddSessionBehavior<, >));
services.AddMediatR();
And my Behavior:
public class AddSessionBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
IHttpContextAccessor Accessor;
public AddSessionBehavior(IHttpContextAccessor anAccessor)
{
this.Accessor = anAccessor;
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
{
var response = await next();
return response;
}
}
When I send my first message through mediator, I get:
System.InvalidCastException occurred
HResult=0x80004002
Message=Object cannot be stored in an array of this type.
Source=<Cannot evaluate the exception source>
StackTrace:
at System.Array.InternalSetValue(Void* target, Object value)
at System.Array.SetValue(Object value, Int32[] indices)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitClosedIEnumerable(ClosedIEnumerableCallSite closedIEnumerableCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitTransient(TransientCallSite transientCallSite, ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.<>c__DisplayClass16_0.<RealizeService>b__0(ServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at MediatR.ServiceCollectionExtensions.GetRequiredServices(IServiceProvider provider, Type serviceType)
at MediatR.Internal.RequestHandlerImpl`2.GetPipeline(TRequest request, RequestHandlerDelegate`1 invokeHandler, MultiInstanceFactory factory)
at MediatR.Internal.RequestHandlerImpl`2.Handle(IRequest`1 request, CancellationToken cancellationToken, SingleInstanceFactory singleFactory, MultiInstanceFactory multiFactory)
at MediatR.Mediator.Send[TResponse](IRequest`1 request, CancellationToken cancellationToken)
at Merlin.Web.Controllers.CourseController.Add(CourseAddVM model)
If I comment out the wiring for my behavior, my handlers get called fine. And the rest of the application seems to work.
What have I done wrong?
I figured it out.
Turns out that somewhere along the way an internal class with the same name as my behavior was created in a different namespace in my application. Probably the result of a "Generate Class definition".
Removing the generated class cleared up the issue.

How to handle session expired exception in Spring MVC-Spring Security app for GWT RPC calls

I have Spring MVC application where security is handled by Spring Security.
UI is built using GWT which gets the data from server using RPC approach.
I need to handle on UI the situation when session is expired:
For example RPC AsyncCallback can get SessionExpiredException type of exception and popup the window with message like "You session is expired, please click the refresh link" or something.
Did someone deal with such problem?
Thanks.
I suppose that for processing of incoming GWT call you use some Spring MVC controller or some servlet. It can have following logic
try{
// decode payload from GWT call
com.google.gwt.user.server.rpc.RPC.decodeRequest(...)
// get spring bean responsible for actual business logic
Object bean = applicationContext.getBean(beanName);
// execute business logic and encode response
return RPC.invokeAndEncodeResponse(bean, ….)
} catch (com.google.gwt.user.server.rpc.UnexpectedException ex) {
// send unexpected exception to client
return RPC.encodeResponseForFailure(..., new MyCustomUnexpectedException(), …) ;
}
Solution for this case
HttpServletRequest request = getRequest() ;
if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
return RPC.encodeResponseForFailure(..., new MyCustomSessionExpiredException(), …) ;
} else {
// first code snippet goes here
}
Then catch custom session expired exception in a client side code. If you do not use RPC directly then provide more details about your bridge implementation between GWT and Spring.
You will need also force GWT compiler to include MyCustomSessionExpiredException type to a serialization white list (to prevent case when GWT security policy stops propogation of the exception to client side). Solution: include MyCustomSessionExpiredException type to each method signature of each synchronous interface:
#RemoteServiceRelativePath("productRpcService.rpc")
public interface ProductRpcService extends RemoteService {
List<Product> getAllProducts() throws ApplicationException;
void removeProduct(Product product) throws ApplicationException;
}
MyCustomSessionExpiredException extends ApplicationException
Then show pop-up in client side code:
public class ApplicationUncaughtExceptionHandler implements GWT.UncaughtExceptionHandler {
#Override
public void onUncaughtException(Throwable caught) {
if (caught instanceof MyCustomSessionExpiredException) {
Window.alert("Session expired");
}
}
}
// Inside of EntryPoint.onModuleLoad method
GWT.setUncaughtExceptionHandler(new ApplicationUncaughtExceptionHandler());
I researched a bit and uploaded the solution here http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired%253Fstate%253Dclosed.
Use mvn jetty:run-war to see the demo after checking it out and go to rpc-security-sample/index.htm
There are two ways to solve it.
The first is around to pass the delegate proxy for GWT RemoteServlet which throws SessionExpiredException during method invocation. This requires to declare Exception in every RPC service method. Example: http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired%253Fstate%253Dclosed
Steps:
Develop new filter which intercepts first
Declare SessionExpiredException in each RPC method service which could inherit RuntimeException for simplicity (no need to follow this in implementers)
Develop parent generic AsyncCallback handler
Use http://code.google.com/p/gspring/ solution to handle all incoming RCP requests.
The second which is much more simplest: return the 401 HTTP error and handle in UI side (GWT native general exception contains the HTTP status number). Example: http://code.google.com/p/gspring/source/browse/#svn%2Ftrunk%2Fsample%2Fsession-expired-401
The second approach is simplest and does not require declaring Exception in service methods contract. However following the first approach can give you some flexibility: it could contain some additional info like last login time (for SessionExpiredException) etc. Also the second approach can introduce new exceptions which are inherited from SecurityException like blacklisted user (for example if user was blacklisted during his session) or for example if user does the same actions very often like a robot (it could be asked for passing the captcha) etc.

WebApi Per-Request Storage for Self Hosting Mode

When hosting WebApi is IIS, you have access to HttpContext and can use the items collection to store objects for a single HTTP request.
When self hosting, you no longer have a HttpContext, so what can I use to store an object for a single request ?
Obviously, there is no direct equivalent of System.Web's HttpContext in self-host.
However, if you wish to start info for the single request, then each HttpRequestMessage exposes a dictionary of <string,object>, called Properties - http://msdn.microsoft.com/en-us/library/system.net.http.httprequestmessage.properties.aspx which you can use to i.e. transport data between handlers, filters, binders and so on.
For selfhost (no IIS involved) you could construct an attribute class deriving from System.Web.Http.Filters.ActionFilterAttribute type (in the assembly system.web.http .net 4.0+). Then override OnActionExecuted method as below:
public class NoResponseCachingAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Response.Headers.CacheControl == null)
actionExecutedContext.Response.Headers.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue();
actionExecutedContext.Response.Headers.CacheControl.NoCache = true;
actionExecutedContext.Response.Headers.CacheControl.NoStore = true;
actionExecutedContext.Response.Headers.CacheControl.MustRevalidate = true;
base.OnActionExecuted(actionExecutedContext);
}
}
This approach worked for my application.

JSF 2 Partial Requests doesn't use FacesContext from the Factory

It seems like the partial requests don't use the faces context instances that are created by FacesContextFactory implementations.
Here's the code in UIViewRoot#processDecodes that indicates the same
if (context.getPartialViewContext().isPartialRequest() &&
!context.getPartialViewContext().isExecuteAll()) {
context.getPartialViewContext().processPartial(PhaseId.APPLY_REQUEST_VALUES);
} else {
super.processDecodes(context);
}
It seems like the PartialViewContext stores the default FacesContextImpl implementation within it and uses it to call lifecycle methods. (Notice that the processPartial method doesn't take a context object, because it uses it own internally stored one)
Is this a bug or this code in there for a specific reason?
Thanks
FacesContext instances are unique per thread, and The FacesServlet creates a ThreadLocal<FacesContext> on the beginning of the request while acquiring the FacesContext (which is the contract of FacesContextFactory#getFacesContext) and removes it on the end of the response associated with the HTTP servlet request (by calling the FacesContext#release).
Whenever you do a FacesContext#getCurrentInstance() in your JSF code, you'll always get the same instance throughout the entire HTTP servlet request/response processing.
About the method UIViewRoot#processDecodes,I really don't see any line which probably can indicate that method uses it's own created instance rather than the passed one. Which line made you think that?
It can be seen in the FacesServlet#service method that it creates the FacesContext from The FacesContextFactory, here is a excerpt from the FacesServlet#service method which shows this -
// Acquire the FacesContext instance for this request
FacesContext context = facesContextFactory.getFacesContext
(servletConfig.getServletContext(), request, response, lifecycle);
// Execute the request processing lifecycle for this request
try {
...
} catch (FacesException e) {
...
}
finally {
// Release the FacesContext instance for this request
context.release();
}
Considering this, I don't feel UIViewRoot#processDecodes can have the FacesContext instance which is not from FacesContextFactory.
Since you're saying - you have set some additional parameters to the FacesContext which get returned from FacesContextFactory, that means you have your own custom implementation of FacesContextFactory, if this is the case then are you sure that your instance is injected in the FacesServlet and not mojarra's com.sun.faces.context.FacesContextFactoryImpl (if you're using mojarra)?
Here's how i got it to work. Below is the code in my custom faces context factory
public FacesContext getFacesContext(Object context, Object request, Object response, Lifecycle lifecycle) throws FacesException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
ExternalContextFactory externalContextFactory = (ExternalContextFactory) getFactory(FactoryFinder.EXTERNAL_CONTEXT_FACTORY);
ExternalContext externalContext = externalContextFactory.getExternalContext(context, request, response);
// CustomFacesContext extends from FacesContextImpl
CustomFacesContext facesContext = new CustomFacesContext(externalContext, lifecycle);
ExceptionHandlerFactory exceptionHandlerFactory = (ExceptionHandlerFactory) getFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY);
ExceptionHandler exceptionHandler = exceptionHandlerFactory.getExceptionHandler();
facesContext.setExceptionHandler(exceptionHandler);
}

Resources