http2 request returning bad request

2019-08-17 09:06发布

问题:

I have a C program of socket which has to use http2 protocol. It is returning Bad Request - HTTP Error 400. The request is badly formed.

Initially, I sent a request like the following through curl 7.64:

curl -v -http2 -i https://mywebsite.xyz 

and i got the following output:

* Expire in 0 ms for 6 (transfer 0x55cc25ec95c0)
* Expire in 1 ms for 1 (transfer 0x55cc25ec95c0)
* Expire in 0 ms for 1 (transfer 0x55cc25ec95c0)
* Expire in 1 ms for 1 (transfer 0x55cc25ec95c0)
* Expire in 0 ms for 1 (transfer 0x55cc25ec95c0)
* Expire in 0 ms for 1 (transfer 0x55cc25ec95c0)
* Expire in 1 ms for 1 (transfer 0x55cc25ec95c0)
* Expire in 0 ms for 1 (transfer 0x55cc25ec95c0)
* Expire in 0 ms for 1 (transfer 0x55cc25ec95c0)
* Expire in 2 ms for 1 (transfer 0x55cc25ec95c0)
* Expire in 1 ms for 1 (transfer 0x55cc25ec95c0)
* Expire in 1 ms for 1 (transfer 0x55cc25ec95c0)
* Expire in 2 ms for 1 (transfer 0x55cc25ec95c0)
*   Trying 10.8.0.253...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x55cc25ec95c0)
* Connected to mysite.xyz (10.8.0.253) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=US; postalCode=XXXX; ST=XX; L=XXXXX; street=XXXXXXXXXXXX; O=XXXXXX; OU=PremiumSSL Wildcard; CN=*.XXXXXXX
*  start date: Nov 20 00:00:00 2018 GMT
*  expire date: Feb 21 23:59:59 2021 GMT
*  subjectAltName: host "mysite.xyz" matched cert's "*.mysite.xyz"
*  issuer: C=GB; ST=Greater Manchester; L=Salford; O=COMODO CA Limited; CN=COMODO RSA Organization Validation 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 0x55cc25ec95c0)
> GET / HTTP/2
> Host: mysite.xyz
> User-Agent: curl/7.64.0
> Accept: */*
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200 
HTTP/2 200 
< content-type: text/html
content-type: text/html
< last-modified: Mon, 01 Jul 2019 17:57:17 GMT
last-modified: Mon, 01 Jul 2019 17:57:17 GMT
< accept-ranges: bytes
accept-ranges: bytes
< etag: "c7c5406c3630d51:0"
etag: "c7c5406c3630d51:0"
< server: Microsoft-IIS/10.0
server: Microsoft-IIS/10.0
< date: Mon, 01 Jul 2019 19:45:53 GMT
date: Mon, 01 Jul 2019 19:45:53 GMT
< content-length: 51
content-length: 51

< 
<html>
<head></head>
</body>Hello</body>
* Connection #0 to host mysite.xyz left intact
</html>

From the above request-response log, I took the following:

GET / HTTP/2
Host: mysite.xyz
User-Agent: curl/7.64.0
Accept: */*

and created a request like the following:

char* inputString = "GET / HTTP/2\r\nHost: mysite.xyz\r\nUser-Agent: curl/7.64.0\r\nAccept: */*\r\n\r\n"

But, this time I am getting error like:

Connected with ECDHE-RSA-AES256-GCM-SHA384 encryption
Received: "HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=us-ascii
Server: Microsoft-HTTPAPI/2.0
Date: Mon, 01 Jul 2019 20:28:48 GMT
Connection: close
Content-Length: 311

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Bad Request</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Bad Request</h2>
<hr><p>HTTP Error 400. The request is badly formed.</p>
</BODY></HTML>

回答1:

HTTP/2 is not a "simple" text based protocol like HTTP/1. This means that you cannot just send a HTTP request as text to the TCP socket but must instead use the binary representation of the request as defined by the (fairly complex) HTTP/2 standard. What curl shows in the debug output is just a text interpretation of the request but not the actual representation on the wire. For more see RFC 7540. Note that you additionally to the binary encapsulation need to set the ALPN extension in TLS to h2 in order to announce to the server that you will use HTTP/2.

In your specific case the request was send as text-based (no ALPN h2 set and no HTTP/2 encapsulation) but with an unknown protocol specification for text-based, i.e. HTTP/2 instead of HTTP/1.1 or similar. The server therefore correctly treated the request as invalid.