-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
finagle-http: ensure server returns 400 with non-ascii characters in URL
Problem HTTP requests that contain invalid (non-ascii) characters in the URI are not consistently handled between HTTP/1.1 and HTTP/2 servers. Solution Add filtering earlier in the Netty pipeline that will ensure that HTTP requests containing invalid characters are rejected with a `400 Bad Request` response. Result Finagle HTTP servers will more consistently handle requests containing invalid characters, regardless of HTTP version. JIRA Issues: CSL-8018 Differential Revision: https://phabricator.twitter.biz/D312009
- Loading branch information
Showing
18 changed files
with
316 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
finagle-http2/src/main/scala/com/twitter/finagle/http2/Http2PipelineInitializer.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.twitter.finagle.http2 | ||
|
||
import com.twitter.finagle.Stack | ||
import com.twitter.finagle.http2.transport.{H2Filter, H2UriValidatorHandler} | ||
import com.twitter.finagle.netty4.http.handler.UriValidatorHandler | ||
import com.twitter.finagle.param.Timer | ||
import io.netty.channel.ChannelHandlerContext | ||
|
||
private[http2] object Http2PipelineInitializer { | ||
|
||
/** | ||
* Install Finagle specific filters and handlers common across all HTTP/2 only pipelines | ||
* | ||
* @param ctx | ||
* @param params | ||
* @param codecName The name of the handler where the remaining handlers will be added after | ||
*/ | ||
def setup(ctx: ChannelHandlerContext, params: Stack.Params, codecName: String): Unit = { | ||
// we insert immediately after the Http2MultiplexCodec#0, which we know are the | ||
// last Http2 frames before they're converted to Http/1.1 | ||
val timer = params[Timer].timer | ||
ctx.pipeline | ||
.addAfter(codecName, H2Filter.HandlerName, new H2Filter(timer)) | ||
ctx.pipeline | ||
.addAfter(H2Filter.HandlerName, H2UriValidatorHandler.HandlerName, H2UriValidatorHandler) | ||
|
||
ctx.pipeline | ||
.remove(UriValidatorHandler.HandlerName) | ||
|
||
} | ||
|
||
} |
36 changes: 36 additions & 0 deletions
36
finagle-http2/src/main/scala/com/twitter/finagle/http2/transport/H2UriValidatorHandler.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package com.twitter.finagle.http2.transport | ||
|
||
import com.twitter.finagle.netty4.http.util.UriUtils | ||
import io.netty.channel.ChannelHandler.Sharable | ||
import io.netty.channel.{ChannelHandlerContext, ChannelInboundHandlerAdapter} | ||
import io.netty.handler.codec.http.HttpResponseStatus | ||
import io.netty.handler.codec.http2.{DefaultHttp2ResetFrame, Http2HeadersFrame} | ||
import io.netty.util.ReferenceCountUtil | ||
|
||
/** | ||
* HTTP URI validation that acts upon [[Http2HeadersFrame]] messages in the Netty HTTP/2 pipeline. | ||
* | ||
* @see [[com.twitter.finagle.netty4.http.handler.UriValidatorHandler]] for HTTP 1.1 handling | ||
*/ | ||
@Sharable | ||
final private[http2] object H2UriValidatorHandler extends ChannelInboundHandlerAdapter { | ||
|
||
val HandlerName: String = "h2UriValidationHandler" | ||
|
||
override def channelRead(ctx: ChannelHandlerContext, msg: Object): Unit = msg match { | ||
case headers: Http2HeadersFrame => | ||
if (!UriUtils.isValidUri(headers.headers().path())) { | ||
ReferenceCountUtil.release(msg) | ||
|
||
// If the URI isn't valid, we want to retain consistency between our HTTP/2 and HTTP/1 | ||
// pipelines by returning a 400 Bad Request response instead of continuing down the | ||
// Netty pipeline, which has inconsistent behavior | ||
val frame = new DefaultHttp2ResetFrame(HttpResponseStatus.BAD_REQUEST.code()) | ||
ctx.writeAndFlush(frame) | ||
} else { | ||
ctx.fireChannelRead(msg) | ||
} | ||
case _ => | ||
ctx.fireChannelRead(msg) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
...-http2/src/test/scala/com/twitter/finagle/http2/transport/H2UriValidatorHandlerTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.twitter.finagle.http2.transport | ||
|
||
import io.netty.channel.embedded.EmbeddedChannel | ||
import io.netty.handler.codec.http2.{Http2Headers, Http2HeadersFrame, Http2ResetFrame} | ||
import org.mockito.Mockito._ | ||
import org.scalatest.FunSuite | ||
import org.scalatest.mockito.MockitoSugar | ||
|
||
class H2UriValidatorHandlerTest extends FunSuite with MockitoSugar { | ||
|
||
test("Accepts valid URI") { | ||
val channel = new EmbeddedChannel(H2UriValidatorHandler) | ||
|
||
val frame = mock[Http2HeadersFrame] | ||
val headers = mock[Http2Headers] | ||
when(frame.headers()).thenReturn(headers) | ||
when(headers.path()).thenReturn("/abc.jpg") | ||
|
||
assert(channel.writeInbound(frame)) | ||
assert(channel.readInbound[Http2HeadersFrame].headers().path() == "/abc.jpg") | ||
} | ||
|
||
test("Rejects invalid URI") { | ||
val channel = new EmbeddedChannel(H2UriValidatorHandler) | ||
|
||
val frame = mock[Http2HeadersFrame] | ||
val headers = mock[Http2Headers] | ||
when(frame.headers()).thenReturn(headers) | ||
when(headers.path()).thenReturn("/DSC02175拷貝.jpg") | ||
|
||
assert(channel.writeInbound(frame) == false) | ||
assert(channel.readOutbound[Http2ResetFrame].errorCode() == 400) | ||
} | ||
|
||
} |
Oops, something went wrong.