[swift-server-dev] Next HTTP API meeting

Brent Royal-Gordon brent at architechies.com
Tue Mar 28 05:35:42 CDT 2017


> On Mar 27, 2017, at 6:23 PM, Helge Heß via swift-server-dev <swift-server-dev at swift.org> wrote:
> 
>> I've got a PR for one of those, as well as for non-`inout` argument passing: <https://github.com/tanner0101/request-types/pull/2 <https://github.com/tanner0101/request-types/pull/2>>
> 
> "A HybridRequest is a struct with a private nested class”
> 
> That way you have the overhead of ARC *and* a struct :-) Well if your struct is just the pointer, so why not use the class in the first place? Just to get copy behavior which I don’t think played a huge role in the discussion

Ideally, the *only* consideration should be whether we want value semantics (each portion of the system has a separate copy of the request; mutating one doesn't affect the others) or we want reference semantics (all portions share the same copy; mutating one affects the others; you probably need to synchronize access somehow, which could be tricky). The point of the hybrid design is that, if a design using reference semantics would be faster but we'd prefer value semantics instead, hybrid ought to give us the speed benefits of both.

> (well, someone even claimed non-copy behavior actually reduces bugs ;-)

I saw that claim, but to tell the truth, I find it so astonishing that I don't think I can believe it without hearing what kinds of bugs were happening due to copying. The person making that claim has been asked to clarify and hasn't yet, so...I simply don't believe it right now.

> I was thinking more along the lines of using UnsafePointer with an own (unprotected) retain count. Not sure whether that is even possible in Swift. (the idea being that the RC of a request isn’t modified by multiple threads ever).

It's not possible to maintain your own reference count because you can't detect when a struct is copied. (Swift does not have anything equivalent to a C++ copy constructor.)

>> Is the use of `inout` here purely a performance hack, or is it semantically significant?
> 
> That depends on the higher level framework. Some store values (like a restored session) in the request, others may use secondary objects for that.
> 
>> That is, are you *supposed* to write into the request in ways that middlewares above you will see?
> 
> You mean below in the sequence? Yes. This is what Node does. E.g. middleware could parse Basic Auth and store a REMOTE_USER variable into the request. Or parse a session cookie, retrieve that and then attach the session to the request.

Well, but if you added something to the session, you wouldn't want it to be visible to middleware *earlier* in the chain, right?

To illustrate what I mean, suppose your request type has a `userInfo` dictionary that middlewares are supposed to use to attach additional information to a request. Let's further suppose—for the sake of simplicity—that your middleware stack is synchronous (the same considerations apply if it's not, just a little bit differently):

	protocol WebResponder: class {
		func respond(to request: WebRequest) -> WebResponse
		var next: WebResponder?
	}

Then a session middleware would look something like this:

	class Session {
		private static let key = "SessionMiddleware.Session"
		
		init(from request: WebRequest) { … }
		func write(to response: inout WebResponse) { … }
		
		private var storage: [String: Any]
		…
	}
	
	class SessionMiddleware: WebResponder {
		func respond(to request: WebRequest) -> WebResponse {
			let session = Session(from: request)
			var requestWithSession = request
			requestWithSession.userInfo[Session.key] = session
			
			var response = next!.respond(to: requestWithSession)
			
			session.write(to: &response)
			return response
		}
	}
		
If `WebRequest` is a value type, then this is invisible to the middleware that comes before SessionMiddleware and all the previous ones (except for whatever headers `session.write(to:)` might have added to the response). If `WebRequest` is a reference type, then this is *visible* to those middlewares, all the way up to the original web server. I don't think that would really make sense—`request` ought to reflect the request they processed, not whatever rewriting or modification subsequent middlewares might have done.

Now, having said all that, `WebRequest` would probably be a type from whatever framework provided `WebResponder`, not the low-level HTTP parsing type. If the intent is for this type to be used *only* for the HTTP parser to communicate with its direct user, and *especially* if we only need to create the type or we only need to create and read it (not mutate it), then the choice of value or reference semantics doesn't matter very much.

But honestly, I hope we're planning to be a little more ambitious than that. I know it's not in scope at this point, but in the long term, I think we would benefit from a common web server interface and middleware stack. My model here is Ruby's Rack, which has been a *huge* boon for web development on Ruby: it lets you mix-and-match servers and frameworks, and it also means that a lot of basic functionality (like parameter and cookie parsing, logfile generation, error pages, static file serving, even routing if you want) can be shared between multiple frameworks as common middleware. Rack makes it really easy to get a web framework off the ground in Ruby; in the long run, I think Swift would benefit from something similar.

> It is questionable whether this is actually a good design, but this is what Node does and people are accustomed to. Hence some implement it that way.
> 
>> If so, I question whether that's a good idea—it seems to me that communication back up the stack should be through the response, not the request.
> 
> Something a web framework needs to decide. Personally I prefer the WebObjects setup where the request is just the request and the response is just the response, and all framework state is attached to a separate context.


That certainly might make sense. It's sensible to think of the method/path/headers as one of many pieces of data comprising a "request"; if so, the main question is simply which of the two things gets the name "Request" and what the other one gets called.

(But if that's the approach we're going to take—you're expected to make this type a property in your Context/Environment/RequestResponseCycle/Whatever type—then I still think this either needs to be a value type, or a reference type that's designed to be compatible with COW wrappers. You can always box a value-semantics type to give it reference semantics, but some reference-semantics designs can be pretty difficult to give value semantics to.)

-- 
Brent Royal-Gordon
Architechies

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-server-dev/attachments/20170328/00c2494a/attachment.html>


More information about the swift-server-dev mailing list