eclipse-vertx / vert.x

Vert.x is a tool-kit for building reactive applications on the JVM
http://vertx.io
Other
14.32k stars 2.08k forks source link

[HTTP2] io.vertx.core.http.HttpClientResponse#headers returning invalid header (:status) #2937

Closed sumnulu closed 5 years ago

sumnulu commented 5 years ago

Returned map contains the key ':Status' with the value HTTP response status code i.e. 200.

vietj commented 5 years ago

can you show some code @sumnulu ?

sumnulu commented 5 years ago

Sure. Any http request has problem. Also ":" char is not valid character in http headers.

vertx version 3.7.0

package sumnulu
import io.vertx.core.http.HttpVersion
import io.vertx.scala.core.http.{HttpClient, HttpClientOptions, HttpClientRequest}
import io.vertx.scala.core.Vertx
//import io.vertx.scala.core.net.OpenSSLEngineOptions

import scala.concurrent.{Await, Future}
import scala.concurrent.duration._

object Main extends App {

  val vertx: Vertx = Vertx.vertx()

  val clientOptions = HttpClientOptions()
    .setTrustAll(true)
    .setSsl(true)
    .setVerifyHost(false)
    .setTryUseCompression(false)
    .setProtocolVersion(HttpVersion.HTTP_2)
    .setUseAlpn(true)
//    .setOpenSslEngineOptions(OpenSSLEngineOptions())

  val client: HttpClient = vertx.createHttpClient(clientOptions)

  val someRandomHttp2Url = "https://1906714720.rsc.cdn77.org/http2/tiles_final/tile_44.png"

  val request: HttpClientRequest = client.getAbs(someRandomHttp2Url)

  request.handler { response =>
    println(s"response ${response.version()}")
    val headers = response.headers()
    println(headers.names().map(k => k -> headers.get(k)).mkString("\n"))
  }

  request.end()
  println("request")

  Await.result(Future.never, 5.seconds)

}

sout:

request
response HTTP_2
(:status,Some(200))
(cache-control,Some(no-cache))
(x-cache,Some(HIT))
(x-age,Some(2267651))
(access-control-allow-origin,Some(*))
(etag,Some("5626d94d-fb3"))
(content-length,Some(4019))
(content-type,Some(image/png))
(date,Some(Wed, 08 May 2019 20:30:14 GMT))
(server,Some(CDN77-Turbo))
(accept-ranges,Some(bytes))

that random url is not special, and it is not returning the offending key.

➜  shared git:(develop) ✗ curl -v https://1906714720.rsc.cdn77.org/http2/tiles_final/tile_44.png -o /dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 185.87.24.171...
* TCP_NODELAY set
* Connected to 1906714720.rsc.cdn77.org (185.87.24.171) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [230 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [102 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [3879 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [300 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [37 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=GB; L=London; O=DataCamp Limited; CN=rsc.cdn77.org
*  start date: Mar 26 00:00:00 2019 GMT
*  expire date: May 28 12:00:00 2019 GMT
*  subjectAltName: host "1906714720.rsc.cdn77.org" matched cert's "*.rsc.cdn77.org"
*  issuer: C=US; O=DigiCert Inc; CN=DigiCert SHA2 Secure Server CA
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fd585800000)
> GET /http2/tiles_final/tile_44.png HTTP/2
> Host: 1906714720.rsc.cdn77.org
> User-Agent: curl/7.54.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 200
< date: Wed, 08 May 2019 20:36:25 GMT
< content-type: image/png
< content-length: 4019
< etag: "5626d94d-fb3"
< cache-control: no-cache
< access-control-allow-origin: *
< server: CDN77-Turbo
< x-cache: HIT
< x-age: 2268022
< accept-ranges: bytes
<
{ [4019 bytes data]
100  4019  100  4019    0     0  20232      0 --:--:-- --:--:-- --:--:-- 20297
* Connection #0 to host 1906714720.rsc.cdn77.org left intact
vietj commented 5 years ago

it is actually a pseudo header of HTTP, I guess this hsould be sanitized

vietj commented 5 years ago

with the current code, this can be expected as pseudo headers are made available and user can filter them using the ":" prefix - I'm wondering how correct versus practical this is

sumnulu commented 5 years ago

I think sanitization should not left to the user for coherency . Without sanitization header validation code will throw throw new IllegalArgumentException("a header name cannot contain the following prohibited characters: =,;: \\t\\r\\n\\v\\f: " + value); exception, that makes response headers not coherent with request headers.

io.vertx.core.http.impl.headers.VertxHttpHeaders#add0

  private void add0(int h, int i, final CharSequence name, final CharSequence value) {
    if (!io.vertx.core.http.HttpHeaders.DISABLE_HTTP_HEADERS_VALIDATION) {
      HttpUtils.validateHeader(name, value);
    }
    // Update the hash table.
    VertxHttpHeaders.MapEntry e = entries[i];
    VertxHttpHeaders.MapEntry newEntry;
    entries[i] = newEntry = new VertxHttpHeaders.MapEntry(h, name, value);
    newEntry.next = e;

    // Update the linked list.
    newEntry.addBefore(head);
  }