Diagnostics and Middleware Signature [WAS Re: [VOTE] - Add Func<AppFunc, AppFunc> as the Middleware delegate signature in the OWIN specification]

63 views
Skip to first unread message

Ryan Riley

unread,
May 7, 2014, 10:18:22 AM5/7/14
to .NET HTTP Abstractions
Thanks for this, Nik. See more inline below.

On Wed, May 7, 2014 at 8:46 AM, Nik Molnar <nik...@gmail.com> wrote:
Okay, looks like I'm going to be "that guy".

-1

I don't have an issue with adding a signature to the OWIN spec (I think that's good), but I'm not a huge fan of this signature, by itself, for a very specific reason.

Let me explain:

Obviously as one of the founders of Glimpse I think and care a lot about diagnostics. For example, we've been able to build this middleware visualization on top of Katana:

Inline image 1

That is beautiful! I'm really excited about what you are doing to support Katana!
 
Technically we are able to pull this off because IAppBuilder's Use(object middleware, params object[] args) method gives us the ability to figure out the type associated with the middleware. Some string manipulation gets us friendly names like "Custom Trace", and boom, Bob's your uncle.

My understanding from Chris is that Func<AppFunc, AppFunc> is a valid parameter for object middleware above, so you would still have the same problem today if the majority of middleware had not been written using Katana abstractions (or possibly worse since we had no standard).
 
My fear is that standardizing on Func<AppFunc, AppFunc> will result in builder implementations simply leveraging that signature for Use() methods as well (e.g. IXBuilder.Use(Func<AppFunc, AppFunc> func)) . Func<AppFunc, AppFunc> does not reliably expose any information about "the piece of middleware" that has been registered, severely hampering middleware chain diagnostics like what I've shown above.

Now, of course builder implementations don't have to do that. They could build the Func<AppFunc, AppFunc> themselves, along with allowing for .WithX() style builder decorators, like Glimpse, and the problem would be solved. But if this signature is standardized, they have no real motivation to make that separation - sans diagnostics "vendors" chasing them down.

Unfortunately, I think there will always be some level of chasing down vendors. For example, I have stubbed out in a local branch the use of an F# computation expression (i.e. LINQ-like syntax) that would allow wrapping middleware with tracing calls (following the points of our previous conversation):

glimpse {
    do! middleware1
    do! middleware2
    do! middleware3
}

Translated:

GlimpseMiddleware(
    middleware1(
        GlimpseMiddleware(
            middleware2(
                GlimpseMiddleware(
                    middleware3(
                        GlimpseMiddleware
                    )
                )
            )
        )
    )
)

which results in a Func<AppFunc, AppFunc> ready to accept the application AppFunc.
 
Here's my proposal:

Let's not add this to the standard until we can also standardize, or at least make strong recommendations/define conventions, with regards to builder implementations. I don't want us to get painted into a corner and lose valuable run time tooling, Glimpse or otherwise.

In short, I don't think we are going to agree on a standard builder implementation. We might get a solid majority, but if middleware are built to work directly with that builder signature and not as a standard, pluggable component, then that middleware weakens the ecosystem.

I want to use Glimpse, and I realize that I have to resort to some of my own gymnastics for it to work for me. I also don't think you'll have to reach out to that many vendors. Most will leverage one of the existing OWIN stacks, especially Katana, which will have the IAppBuilder.

Finally, I think you have to deal with the Func<AppFunc, AppFunc> signature anyway, as it is currently a valid middleware option in the existing builder signature.

Chris, please correct me if I'm incorrect about the middleware signature. I'm referencing your comment here.

Darrel Miller

unread,
May 7, 2014, 10:27:42 AM5/7/14
to net-http-a...@googlegroups.com
Nik,

I agree that being able to extract diagnostic information from middleware is very important.  However I think limiting ourselves to just the type name would be missing out on an opportunity.  I think we need a more sophisticated mechanism that allows us to declare a bunch of other information about middleware.  I would like to know if a piece of middleware could modify a request header, or a response header.  Could it short circuit the request, or does it just monitor requests passing through?  Does it transform request or response bodies?  Having this kind of summary metadata available would make debugging problems much easier.

How you go about exposing that information is a whole other story :-)

Darrel

Nik Molnar

unread,
May 7, 2014, 11:40:05 AM5/7/14
to net-http-a...@googlegroups.com
Thanks Ryan and Darrel,

Ryan, you are right that a func being passed in for that Use(object, ...) potentially being an issue. We didn't run into that in our (probably too limited) testing, but that doesn't mean it won't happen.

Darrel - I'm 100% with you that limiting to types would be far from ideal. I hope it didn't sound like I was suggesting that. I do think that "a more sophisticated mechanism" would be better. I guess what my -1 vote really represents is a call to figure out what standardizing this signature means for diagnostics.

Another piece of diagnostics information that I'd like about a piece of middleware is if it represents a fork point in the chain.

Perhaps the best way to move forward then is to propose a set of conventions, or way for middleware to expose metadata about itself, that can be extensible and generally agreed upon. I'm not exactly sure how we'd be able to hang anything like that off a delegate, but I haven't given it any real thought yet either.



--
You received this message because you are subscribed to the Google Groups ".NET HTTP Abstractions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to net-http-abstrac...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Darrel Miller

unread,
May 7, 2014, 12:11:15 PM5/7/14
to net-http-a...@googlegroups.com
Nik,

I'd like to see the via header be used to identify middleware and
identify their characteristics. Something vaguely like this,

via: 1.1 CustomTrace (safe;add-environment-logger;), 1.1 ErrorHandling
(can-abort;update-body;), 1.1 CrossOrigin
(can-abort;modify-request-headers;)

Possibly these headers might only be generated on a TRACE method, or
the header could be stripped off at the end of the response pipeline
to avoid returning it to the client.

I think I'd rather see this kind of dynamic description of the actual
pipeline used by a request than build a reflection/introspection
mechanism of the pipeline structure.

Thoughts?

Darrel

Mark Rendle

unread,
May 7, 2014, 12:19:31 PM5/7/14
to net-http-a...@googlegroups.com
I'm very keen to use Glimpse in an OWIN pipeline, independent of the underlying builder.

For the record, I am in agreement with Seb that Action<MidFunc> is the correct Builder type, and this is what Fix now passes to the OwinAppSetup method.

Now, if I want to use Glimpse and get good pipeline information, such as is currently available with Katana due to it's integrated pipeline stuff, I would be willing to use a Glimpse extension around the standard builder in order to configure that.

In other words, Glimpse could provide a method:

public Action<MidFunc, GlimpseCategory> WrapBuilder(Action<MidFunc> builder)

That will work with any standard builder, and still allow Glimpse to gather the pipeline-specific information that it needs to report on stuff in a useful way.

I think.

With the other stuff that's going on in ASP.NET div right now, an alternative to the current Katana approach is going to be necessary anyway, right?

Mark


--
Mark Rendle
Founder, Zudio

Chris R

unread,
May 7, 2014, 1:18:20 PM5/7/14
to net-http-a...@googlegroups.com

Ryan, you’re correct that Katana’s IAppBuilder.Use supports a variety of signatures, including Func<AppFunc, AppFunc>, though Katana middleware primarily use the Type constructor pattern.

 

There’s minimal information to be had from just examining the types name, but I guess it’s better than nothing.  We’ve avoided lambdas for a similar reason in that concrete types provide more legible stack traces.

 

If you really want detailed information from a middleware, the middleware author is going to need to volunteer that information.  You’d want that info to be human readable.  You also don’t want providing that information to be a burden to the middleware developer.  The highest quality information the dev can provide will be in their published docs (readme.txt), and then their doc comments.  I think there’s only a very limited amount of information that would be useful for them to provide at runtime.

 

~Chris

Nik Molnar

unread,
May 8, 2014, 9:42:03 AM5/8/14
to net-http-a...@googlegroups.com
Darrel,

I like the direction you are going in, but I'd be hesitant to put the data in a header.

via would basically be unbounded, since users could have lots of middleware, and I know the spec doesn't care, but many web servers have limits to the size of HTTP headers.

Yes, I've caused 413's due too headers that were too large before. :P

Sebastien Lambla

unread,
May 8, 2014, 11:12:57 AM5/8/14
to net-http-a...@googlegroups.com
Action<MidFunc,IDictionary<string,object>> with middleware standard keys such as friendly name, and whatever else the middleware wants to pass back in private to the builder, should it have something to add there?

My builder tends to simply look at the passed lambda, seek the declaring type and format this for diagnostics purposes. What else would builders find useful to provide to the builder beyond a friendly name?

Reply all
Reply to author
Forward
0 new messages