Skip to content

Save the server-port of the original request - #1865

Merged
farmdawgnation merged 13 commits into
lift:masterfrom
Bhashit:issue-1794-lazy-server-port-in-req-snapshot
Jul 10, 2017
Merged

Save the server-port of the original request#1865
farmdawgnation merged 13 commits into
lift:masterfrom
Bhashit:issue-1794-lazy-server-port-in-req-snapshot

Conversation

@Bhashit

@Bhashit Bhashit commented Jun 17, 2017

Copy link
Copy Markdown
Contributor

Issue #1794

store the server-port of the original request since it's used in a lazy val, and Jetty will recycle the request instance if the lazy val is realized too late

Bhashit added 2 commits June 17, 2017 21:13
… in a lazy val, and Jetty will recycle the request instance if the lazy val is realized too late

@farmdawgnation farmdawgnation left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall. One minor, opportunistic formatting change requested.

lazy val serverPort: Int = req.serverPort match {
case 80 => headers("X-SSL").flatMap(Helpers.asBoolean _).filter(a => a).map(a => 443).headOption getOrElse 80
lazy val serverPort: Int = _serverPort match {
case 80 => headers("X-SSL").flatMap(Helpers.asBoolean).filter(identity).map(a => 443).headOption getOrElse 80

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're here, can we change this to be a bit more readable?

case 80 =>
  headers("X-SSL")
    .flatMap(Helpers.asBoolean)
    .filter(identity)
    .map(a => 443)
    .headOption
    .getOrElse(80)

Also, what's the point of the filter?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting done.

And about that filter call: It seems to exist because the headers method returns a List[String] and we want to check if there is any true value.

But, now that I looked into it a bit more, I think there might be something wrong with how the headers method is used here. It doesn't return header values, it just returns the names of the headers, which are never going to be converted to a true value and the result of this entire chain is always 80.

I am not sure if I should push a change here or create a ticket.

@farmdawgnation

Copy link
Copy Markdown
Member

And about that filter call: It seems to exist because the headers method returns a List[String] and we want to check if there is any true value.

The filter call is probably there because the headers method returns one type and we need a different type. (e.g. got an Iterator need a List) Can you look into that?

But, now that I looked into it a bit more, I think there might be something wrong with how the headers method is used here. It doesn't return header values, it just returns the names of the headers, which are never going to be converted to a true value and the result of this entire chain is always 80.

Hm, that doesn't look good. I don't have time to look at it right now, but can you build out a unit test demonstrating that? If you're right, I think we should look to fix it here.

@Bhashit

Bhashit commented Jun 19, 2017

Copy link
Copy Markdown
Contributor Author

About the filter call, yeah, it is like that. I mean, just the way of checking if there really is a header named X-SSL.

About the headers method: I'll push some changes here later

@Bhashit

Bhashit commented Jun 25, 2017

Copy link
Copy Markdown
Contributor Author

@farmdawgnation Added a quick-n-dirty test, just for demo, to show that the current headers implementation is incorrect. Can be run with test-only net.liftweb.http.provider.servlet.OfflineRequestSnapshotSpec.

Also added a demo of a correct implementation. These are throwaway changes, and I'll clean them up if it looks okay to you.

Was also wondering why we are changing the server port of the original request anyway.

@Shadowfiend Shadowfiend left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a couple of notes while I was looking around.

}

private class OfflineRequestSnapshot(req: HTTPRequest, val provider: HTTPProvider) extends HTTPRequest {
private [servlet] class OfflineRequestSnapshot(req: HTTPRequest, val provider: HTTPProvider) extends HTTPRequest {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're making this private[servlet], I wonder if we just move it outside of HTTPRequestServlet and into its own file altogether? Not necessarily a pre-req for merging, just occurred to me while reading.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a good idea.


"A snapshot request should" in {
val mockHttpRequest = mock[HTTPRequest]
val mockProvider = mock[HTTPProvider]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could probably just be new HTTPProvider { val context = _ }---no huge difference, just a preference for using the language instead of mock magic when possible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting the context value to _ results in unbound placeholder parameter. I think that only works for class level vars.

Setting context to null doesn't feel right to me. I also thought about setting it to an instance of net.liftweb.http.LiftFilter, which implements the HTTPProvider trait, but then it would look like it has some purpose. So, considering these two scenarios, I think mock is probably appropriate here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, right you are! = null it would have to be.

It's not “right” in the sense that if this were live code it would be a bad idea. Since this is a test, I usually care a lot less.

Think of it this way: with the mock, if someone tried to call something on context, things would blow up (because there's no expectation set up for that mock). So in that sense it's the same as if you set a null there: interacting with the object in question will cause issues. But, we save ourselves whatever weird plumbing and other magic the mocking framework does, instead favoring straightforward language constructs.

@Bhashit Bhashit Jun 26, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that you said it, null seems okay. Pushed a change.


object OfflineRequestSnapshotSpec extends WebSpec with Mockito {

"A snapshot request should" in {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be "A snapshot request" should {. Also, I would suggest folding in the name of the class under spec, i.e. "An OfflineRequestSnapshot" should { or even (my personal preference) "OfflineRequestSnapshot" should {. This makes it easier when seeing a failed test to know exactly what class failed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely. I just wrote this up as a quick-n-dirty example to show the current implementation didn't work correctly. To be improved if approved. I guess this means I should go ahead and do the cleanup.

@Bhashit

Bhashit commented Jun 27, 2017

Copy link
Copy Markdown
Contributor Author

The Codacy issues are about converting private to private [this], and there's one about using that null we discussed about in a test.

@Shadowfiend

Copy link
Copy Markdown
Member

Fwiw I do think the private[this] advice is worth following. Not a big deal though.

@Bhashit

Bhashit commented Jun 27, 2017

Copy link
Copy Markdown
Contributor Author

Yeah. In fact, I started using private[this] as a default for my current project from today. Ha ha. Will be making that change here as well.

@Bhashit

Bhashit commented Jun 27, 2017

Copy link
Copy Markdown
Contributor Author

Found the param method to be incorrect in the same manner that the header method was. So, corrected it as well.

@farmdawgnation farmdawgnation left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting some minor changes in the test because I was confused while reading it, but overall this is looking good.


private[this] val xSSLHeader = HTTPParam(X_SSL, List("true")) :: Nil

private[this] def getRequestSnapshot(originalPort: Int, headers: List[HTTPParam] = xSSLHeader, params: List[HTTPParam] = Nil) = {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

headers should probably default to Nil otherwise it's not obvious in a casual reading of your tests that the SSL header is included.

val port80Req = getRequestSnapshot(originalPort = 80)
port80Req.serverPort shouldEqual 443
}
s"equal to the original request-port when the '$X_SSL' header is absent or not set to the string 'true' (case-insensitive), or if the original port is not 80" in {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks ripe for splitting into three separate specifications. :)

@farmdawgnation farmdawgnation added this to the 3.2.0-M1 milestone Jul 3, 2017
Set the default value for the `headers` param in `getRequestSnapshot` to Nil

@farmdawgnation farmdawgnation left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM. I'll let it sit for a day or two to get any additional feedback from interested parties, and then I'll merge it. 👍

Thanks for the solid contribution!

@farmdawgnation
farmdawgnation merged commit 30d48e0 into lift:master Jul 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants