44

I've got a piece of code (let's name it Code A) working in a framework and I want to make it work in another framework. The working piece of code makes a successful POST request using CURL like this (request with CURLOPT_VERBOSE on):

* Connected to android.clients.google.com (216.58.209.238) port 443 (#0)
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /Applications/MAMP/Library/OpenSSL/cert.pem
    CApath: none
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: C=US; ST=California; L=Mountain View; O=Google Inc; CN=*.google.com
*  start date: Jan 18 19:17:59 2017 GMT
*  expire date: Apr 12 18:51:00 2017 GMT
*  subjectAltName: host "android.clients.google.com" matched cert's "android.clients.google.com"
*  issuer: C=US; O=Google Inc; CN=Google Internet Authority G2
*  SSL certificate verify ok.
> POST /auth HTTP/1.1
Host: android.clients.google.com
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 97

The http framework here (yii2/httpclient, to be specific) has too many dependencies to bring it to the other project so I'm trying to recreate it on low level like this (let's name it Code B):

<?php 
$ch = curl_init();
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'post-data-here');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, 'https://android.clients.google.com/auth');
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/x-www-form-urlencoded; charset=UTF-8"]); // just because I'm desperate
curl_setopt($ch, CURLOPT_VERBOSE, true);
$content = curl_exec($ch);

I'm expecting that to have the same result, but this is what I get:

* Connected to android.clients.google.com (216.58.209.238) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.google.com
* Server certificate: Google Internet Authority G2
* Server certificate: GeoTrust Global CA
> POST /auth HTTP/1.1
Host: android.clients.google.com
Accept: */*
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 97
* upload completely sent off: 97 out of 97 bytes
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: Mon, 01 Jan 1990 00:00:00 GMT
< Date: Fri, 27 Jan 2017 12:07:12 GMT
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Server: GSE
< Alt-Svc: clear
< Accept-Ranges: none
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
<HTML>
<HEAD>
<TITLE>HTTP Version Not Supported</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>HTTP Version Not Supported</H1>
<H2>Error 505</H2>
</BODY>
</HTML>

And instead of a valid response I get "Error 505: HTTP Version Not Supported". The only difference I see is that the working code tries to "ALPN, offering http/1.1" while the latter code doesn't do that. And the certificate part after that, but it's never ever mentioned in code A so I'm not sure what does it do to provide it.

Both versions of code run on the same server, same version of PHP (5.6) and CURL (7.51.0). The difference in verbose log starts BEFORE any data being sent, so I guess it's not about any data or headers being set incorrectly.

What I tried so far (with little or no effect):

  1. curl_setopt($ch, CURLOPT_SSL_ENABLE_ALPN, whatever) - does not work because CURLOPT_SSL_ENABLE_ALPN is not defined at all (although it must be in this version of CURL, not sure what's wrong)
  2. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, whatever)
  3. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, whatever)
  4. curl_setopt($ch, CURLOPT_SSLVERSION, whatever)
  5. some other desperate silly stuff I'm even ashamed to show here

I've tried to learn the working code as deep as I could but it seems that it doesn't do anything over the simple HTTP POST. I tracked every curl_setopt it makes and it seems there are only these curl_setopt I used in my code, nothing extra. Still, it works and my code doesn't.

I've tried making the same using the command line:

$ curl https://android.clients.google.com/auth -v --http1.1 -X POST --no-alpn --no-npn --data "copypasted-post-data-from-code-B-and-yes-its-urlencoded"

Got the correct result:

*   Trying 216.58.209.238...
* TCP_NODELAY set
* Connected to android.clients.google.com (216.58.209.238) port 443 (#0)
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /Applications/MAMP/Library/OpenSSL/cert.pem
  CApath: none
...
> POST /auth HTTP/1.1
> Host: android.clients.google.com
> User-Agent: curl/7.51.0
> Accept: */*
> Content-Length: 97
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 97 out of 97 bytes
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: Mon, 01 Jan 1990 00:00:00 GMT
< Date: Fri, 27 Jan 2017 13:22:27 GMT
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Server: GSE
< Alt-Svc: clear
< Accept-Ranges: none
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
<
SID=BAD_COOKIE
LSID=BAD_COOKIE
Auth=here-s-the-data-i-need
Andrey
  • 993
  • 1
  • 11
  • 20
  • As i can see in the previous section there is an ssl certificate provided but you have not provided any certificate ..so the response would never be same.. beside that is there any issue with curl ?? – M A SIDDIQUI Jan 27 '17 at 12:55
  • Well yes, that too, but I don't provide that certificate path in code A either. I'm not sure what does it do to provide it and how do I repeat that. My guess is CURLOPT_CAINFO, CURLOPT_CAPATH (has no effect and it is not used anywhere in working code) – Andrey Jan 27 '17 at 13:05
  • SSL certificate is a kind of signature to make secure connection over TCP protocol .. But that not compulsory until the service provider rejects insecure connection.. All i am asking do you have any issue with response .. not response headers – M A SIDDIQUI Jan 27 '17 at 13:08
  • the response I get with Code B is

    HTTP Version Not Supported

    Error 505

    that's the problem :) Code A yields the desired response, no errors whatsoever
    – Andrey Jan 27 '17 at 13:11
  • just define it manually then ` `if(!defined('CURLOPT_SSL_ENABLE_ALPN')){define('CURLOPT_SSL_ENABLE_ALPN',226);}` and make sure curl_setopt(_array?) returns bool(true) – hanshenrik Jan 28 '17 at 13:17
  • tried that too (actually, passed 226 to curl_setopt instead of that), had no effect – Andrey Jan 29 '17 at 15:22

2 Answers2

51

Error 505: HTTP Version Not Supported is not an error string that is returned by curl/libcurl, that sounds like content that you receive from the server you're communicating with. If you would show us the full HTTP response including headers, we could probably have seen that.

So, all your playing around with different curl options was to no gain because curl worked fine every single time. You could also verify that with using the curl command line tool against the host you're trying to get to work:

curl https://android.clients.google.com/auth -v --http1.1 -X POST --no-alpn --no-npn

This command line shows that both the TLS and HTTP "layers" are fine.

Another question here got a similar error when they passed in the wrong data (not url encoded). Maybe you have something similar and because of your switch to a new framework you missed it?

Daniel Stenberg
  • 54,736
  • 17
  • 146
  • 222
  • Yes, you are right, the HTTP code is actually 200 and it seems like 505 is the error I only see in content. Tried to send actual POST from the command line using exact same string from code B (it's url encoded) and got the desired content. I'll add that to the question now – Andrey Jan 27 '17 at 13:24
0

Okay, I figured it out.

Actually, BEFORE that piece of code in Code B there was another request executed using yet another library (Httpful, to be exact) and it seems that request messed things up somehow. I did not know that something can affect a request performed with a clean curl_init().

Anyway, when I replaced all those Httpful calls prior to the request in question with low level ones, everything worked fine.

Andrey
  • 993
  • 1
  • 11
  • 20