(I edited the title, see EDIT below: GET path in the response contains spaces, requested path does not.)
I am working for a company that uses a "template system" to enable their markets (different countries) to host small web apps and making them "feel" like part of the main market website by pulling header, navigation, footer of the main website via curl:
So a php script uses curl to pull a special empty CMS page that contains only the relevant elements and placeholders for the web app to replace. This way, the web apps always have the current legal footer, header and navigation links to the main sections on the CMS and feel like being part of the cms albeit being hosted on their own somewhere else.
In this case, depending on an i18n parameter in the url, the script is curling a different template for each of ~50 markets.
Now the curious case: This is working for all templates but one, which is returning error 400 when being curled via https.
But when curled via http, 301 is returned, then another request to the previously faulty https URL is made, only this time it returns 200.
Does not work:
* Trying [server_ip]...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x562a5065a130)
* Connected to www.[company_name].pt ([server_ip]) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=*.[company_name].pt
* start date: Aug 23 19:02:44 2022 GMT
* expire date: Nov 21 19:02:43 2022 GMT
* subjectAltName: host "www.[company_name].pt" matched cert's "*.[company_name].pt"
* issuer: C=US; O=Let's Encrypt; CN=R3
* 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 0x562a5065a130)
> GET [path] HTTP/2
Host: www.[company_name].pt
User-Agent:Mozilla/5.0 (compatible; Integrator https://[app_name].[company_name].com)
Accept:text/html
Accept-Encoding:gzip,deflate
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive:115
Connection:keep-alive
Cache-Control:max-age=0
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
* The requested URL returned error: 400
* stopped the pause stream!
* Connection #0 to host www.[company_name].pt left intact
Does work:
* Trying [server_ip]...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x562a5066c0f0)
* Connected to www.[company_name].pt ([server_ip]) port 80 (#0)
> GET [path] HTTP/1.1
Host: www.[company_name].pt
User-Agent:Mozilla/5.0 (compatible; Integrator https://[app_name].[company_name].com)
Accept:text/html
Accept-Encoding:gzip,deflate
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive:115
Connection:keep-alive
Cache-Control:max-age=0
< HTTP/1.1 301 Moved Permanently
< Content-Type: text/html
< Content-Length: 185
< Connection: keep-alive
< Location: https://www.[company_name].pt/[path]
<
* Ignoring the response-body
* Connection #0 to host www.[company_name].pt left intact
* Issue another request to this URL: 'https://www.[company_name].pt/[path]'
* Trying [server_ip]...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x562a5066c0f0)
* Connected to www.[company_name].pt ([server_ip]) port 443 (#1)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=*.[company_name].pt
* start date: Aug 23 19:02:44 2022 GMT
* expire date: Nov 21 19:02:43 2022 GMT
* subjectAltName: host "www.[company_name].pt" matched cert's "*.[company_name].pt"
* issuer: C=US; O=Let's Encrypt; CN=R3
* 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 0x562a5066c0f0)
> GET [path]
Host: www.[company_name].pt
User-Agent:Mozilla/5.0 (compatible; Integrator https://[app_name].[company_name].com)
Accept:text/html
Accept-Encoding:gzip,deflate
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive:115
Connection:keep-alive
Cache-Control:max-age=0
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< content-type: text/html; charset=utf-8
< content-length: 15305
< date: Thu, 22 Sep 2022 09:25:35 GMT
< server: Apache
< x-robots-tag: noindex
< strict-transport-security: max-age=31536000
< x-content-type-options: nosniff
< x-frame-options: SAMEORIGIN
< x-xss-protection: 1; mode=block
< referrer-policy: strict-origin-when-cross-origin
< cache-control: public, max-age=3600
< expires: Thu, 22 Sep 2022 10:25:35 GMT
< pragma: public
< accept-ranges: bytes
< content-encoding: gzip
<
* Connection #1 to host www.[company_name].pt left intact
Calling the template in the browser works, with http and https.
curl from CLI works also for both http and https and returns 200, the error only occurs with PHP.
The script (a bit blown up for error search):
function curl($url){
$headers[] = "User-Agent:Mozilla/5.0 (compatible; Integrator https://[app_name].[company_name].com)";
$headers[] = "Accept:text/html";
$headers[] = "Accept-Encoding:gzip,deflate";
$headers[] = "Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7";
$headers[] = "Keep-Alive:115";
$headers[] = "Connection:keep-alive";
$headers[] = "Cache-Control:max-age=0";
/**/debug_to_console("init curl");
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_ENCODING, "gzip");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($curl, CURLOPT_SSLVERSION, 6);
curl_setopt($curl, CURLOPT_FAILONERROR, 1);
curl_setopt($curl, CURLOPT_VERBOSE, 1);
curl_setopt($curl, CURLOPT_STDERR, $verbose = fopen('php://temp', 'rw+'));
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
$data = curl_exec($curl);
if(curl_errno($curl)){
debug_to_console("curl url: " . $url);
debug_to_console("curl error: " . curl_error($curl));
//echo 'Request Error:' . curl_error($curl);
}
echo "Verbose information:\n<pre>", !rewind($verbose), htmlspecialchars(stream_get_contents($verbose)), "</pre>\n";
curl_close($curl);
return $data;
}
As mentioned, on other templates from other markets (on the same server) this is working fine. And I was told, that the instances are duplicated, so they all should have the same settings. Again, they are all on the same server. Unfortunately, I can't get them to check further until I'm pointing them into the right direction what to look at.
Tests with ssllabs.com showed no differences between the instances.
Being a Front End Dev, I'm really not getting any further at this point, after trying multiple suggestions for similar errors. I'm suspecting a wrong setting on that instance, but what instructions/hints can I give the server team?
Sorry I had to remove IPs and company/app names.
Thank you.
Edit: Not sure if this is relevant, for some reason, the line > GET /[path] HTTP/2 is different in the faulty curl attempt:
Every successful curl has one space between the path and HTTP/2, the faulty one has three spaces between the path and HTTP/2.
When using http instead of https, the line also has three spaces > GET /[path] HTTP/1.1), gets the 301 response and after redirect, the new attempt has only one space > GET /[path] HTTP/2.
The URL is not the problem, it is absolutely identical to other instances where there is no problem. Does the server add a space character itself on the initial curl attempt? What could cause added spaces? This has to be server side, the other instances don't mangle the initial GET path.
I have a very simple PHP CURL get request to retrieve a json file from my AWS CloudFront distribution.
public function get_directory() {
$json = 'https://d108fh6x7uy5wn.cloudfront.net/themes.json';
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $json);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$array = curl_exec($curl);
curl_close($curl);
$array = json_decode($array, true);
$array = $array['themes'];
return $array;
}
I then have a foreach loop which displays the information contained in the json file.
However, it takes 3.6 minutes for the page to load, it's VERY slow. If I use my S3 origin URL instead of the CF URL the page loads instantly.
I am using this same method on other servers without any issues. The issue only occurs on this server which is using the InterWorx hosting control panel software.
Here is the result of the CURL log:
* About to connect() to d108fh6x7uy5wn.cloudfront.net port 443 (#0)
* Trying 2600:9000:2132:4e00:14:45cf:19c0:21...
* Connection timed out
* Trying 2600:9000:2132:7000:14:45cf:19c0:21...
* Connection timed out
* Trying 2600:9000:2132:2400:14:45cf:19c0:21...
* Connection timed out
* Trying 2600:9000:2132:2c00:14:45cf:19c0:21...
* Connection timed out
* Trying 2600:9000:2132:9600:14:45cf:19c0:21...
* Connection timed out
* Trying 2600:9000:2132:3000:14:45cf:19c0:21...
* Connection timed out
* Trying 2600:9000:2132:aa00:14:45cf:19c0:21...
* Connection timed out
* Trying 2600:9000:2132:5c00:14:45cf:19c0:21...
* Connection timed out
* Trying 13.224.38.161...
* Connected to d108fh6x7uy5wn.cloudfront.net (13.224.38.161) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: CN=*.cloudfront.net
* start date: Mar 19 00:00:00 2021 GMT
* expire date: Mar 17 23:59:59 2022 GMT
* common name: *.cloudfront.net
* issuer: CN=Amazon,OU=Server CA 1B,O=Amazon,C=US
> GET /themes.json HTTP/1.1
Host: d108fh6x7uy5wn.cloudfront.net
Accept: */*
< HTTP/1.1 200 OK
< Content-Type: application/json
< Content-Length: 68529
< Connection: keep-alive
< Last-Modified: Tue, 21 Dec 2021 12:05:12 GMT
< Accept-Ranges: bytes
< Server: AmazonS3
< Date: Thu, 20 Jan 2022 09:52:02 GMT
< ETag: "8d0a0de6a39c797d137171347dd8ef52"
< X-Cache: Hit from cloudfront
< Via: 1.1 ce475d5a085e50a2b454f6aec0f8826e.cloudfront.net (CloudFront)
< X-Amz-Cf-Pop: YTO50-C1
< X-Amz-Cf-Id: Bmq-SnHe7-eqfiBf6a6qdoRMQExdhweFLipeL_PrZqM2slQG9OGWzg==
< Age: 34974
<
* Connection #0 to host d108fh6x7uy5wn.cloudfront.net left intact
It looks like the connection keeps timing out.
It's probably an issue with my CF distribution settings however I am a bit of a noob when it comes to AWS and am at a loss.
If anybody can point me in the right direction that would be greatly appreciated.
I've been trying to resolve this issue for 2 days and 5 minutes after posting my question I decided to change (what should of been obvious) a setting in my CloudFront distribution setting.
SOLUTION: Turn off the IPv6 setting.
Today i am having a weird problem with elasticsearch and curl. Elasticsearch is running fine but when i try to connect it with curl, it waits forever and no data returns.
I tried to get verbose output from curl, it can connect to elasticsearch, but after connect, it receives no data.
curl -v http://10.0.0.1:9200/_cat/indices
* Trying 10.0.0.1...
* TCP_NODELAY set
* Connected to 10.0.0.1 (10.0.0.1) port 9200 (#0)
> GET /_cat/indices HTTP/1.1
> Host: 10.0.0.1:9200
> User-Agent: curl/7.52.1-DEV
> Accept: */*
>
It waits forever. Also elasticsearch php is not working as same.
When i tried to open same url from same machine with lynx, it can get data successfully. But when i tried to open same url with wget, it waits forever too. Curl can connect other external elasticsearch hosts without problem. Also we can connect to this elasticsearch host from other hosts without problem.
The other weird thing is, when i tried to get /_cat/health with curl or wget, they can get the data.
curl -v http://10.0.0.1:9200/_cat/health
* Trying 10.0.0.1...
* TCP_NODELAY set
* Connected to 10.0.0.1 (10.0.0.1) port 9200 (#0)
> GET /_cat/health HTTP/1.1
> Host: 10.0.0.1:9200
> User-Agent: curl/7.52.1-DEV
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: text/plain; charset=UTF-8
< content-length: 63
<
1545812391 11:19:51 mycluster green 2 2 382 381 0 0 0 0 - 100.0%
* Curl_http_done: called premature == 0
* Connection #0 to host 10.0.0.1 left intact
There is no firewall on both machines. I tried to update curl, nothing changed. I also coudln't find any usefull information on syslog, elasticsearch logs etc.
Any idea why this happens?
This system is working perfectly for 4 months. I am having this issue since 2 days. Then i tried to upgrade system packages, rebooted the servers but nothing changed. I cleared the arp cache also.
Thank you.
Request to a single specific https endpoint fails on one of my servers with "NSS: client certificate not found (nickname not specified)" error from PHP CURL.
When I ran the same query from the command line with verbose option I got the same error, but after that I also got a valid response (after the error in the same CURL request).
Then I've enabled verbose option for my PHP CURL request and displayed it for testing like this:
$error = curl_error($curl);
if ($error) {
echo "cURL Error #:" . $error;
rewind($verbose);
$verboseLog = htmlspecialchars(stream_get_contents($verbose));
echo "<pre>";
print_r($verboseLog);
} else {
echo $response;
}
And got this as output:
cURL Error #:NSS: client certificate not found (nickname not specified)
* About to connect() to <address> port 8001 (#0)
* Trying <ip>... * connected
* Connected to <address> (<ip>) port 8001 (#0)
* skipping SSL peer certificate verification
* NSS: client certificate not found (nickname not specified)
* SSL connection using TLS_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: CN=*.<address>m,O=XXX A.S.,OU=IT,L=Umraniye,ST=Istanbul,C=TR
* start date: Jun 11 12:52:03 2015 GMT
* expire date: Jun 09 14:16:13 2018 GMT
* common name: *.<address>
* issuer: CN=GlobalSign Organization Validation CA - SHA256 - G2,O=GlobalSign nv-sa,C=BE
> POST /<url_part> HTTP/1.1
Host: <address>:8001
Accept: */*
Accept-Encoding: deflate, gzip
authorization: Basic UlBlahBlahnJrcjEyMzQ1Ng==
cache-control: no-cache
content-type: application/json
postman-token: eeefe018-7cac-1706-7b6d-847800a7ad0f
Content-Length: 333
< HTTP/1.1 200 OK
< set-cookie: sap-usercontext=sap-client=100; path=/
< set-cookie: SAP_SESSIONID_CFP_100=ohZvfO5ZOwq_LTE76Zgz9L-C0NdhlRHngM0AF6R3IFY%3d; path=/
< content-type: application/json; charset=utf-8
< content-length: 1150
< cache-control: max-age=0
< sap-cache-control: +180
< sap-isc-uagent: 0
<
* Connection #0 to host <address> left intact
* Closing connection #0
So, CURL request finished with the error, but in verbose log we see that after the error valid response was also fetched.
Here's my questions:
Is there any way to overcome this NSS issue as long as other different https endpoints work completely fine? Is it connected with endpoint's server configuration and can be resolved there?
If the first item fails, is it possible not to throw CURL NSS error, but get the response instead? CURLOPT_FAILONERROR option does not help.
CURL was built with NSS:
curl 7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Protocols: tftp ftp telnet dict ldap ldaps http file https ftps scp sftp
Features: GSS-Negotiate IDN IPv6 Largefile NTLM SSL libz
Update:
The second item can be closed, because this error is really strange: curl_error returns the error, but curl_exec returns valid response at the same time.
Try giving a absolute path to the certificate and add a "./" prefix. For example "./path-to-your-cert"
I created a really simple small php script that I was hoping to run in the command line on my MacOSX terminal.
If I just type echo "test" that gets outputted just fine.
Now I added a curl request to that script to grab some data from an API and then type print_r $result.
Unfortunately the result is never outputted into the command line. If I now run the exact same php file in my MAMP localhost, everything works fine and the content is outputted correctly.
This is my code:
require 'connection.php';
$store = new connection('danny', 'https://store-bgf5e.mybigcommerce.com/api/v2/', 'XXXXX');
$customers = $store->get('/customers');
foreach($customers AS $customer) {
print "------ Getting Adresses for: " . $customer['first_name'] . ' ' . $customer['last_name'] . ' -------';
if( isset($customer['addresses']['resource']) ) {
print_r($store->get($customer['addresses']['resource']));
}
}
exit;
I know that the API returns the addresses just fine so the call definitely works.
This is what Curl is saying:
* Trying 63.141.159.120...
* TCP_NODELAY set
* Connected to store-bgf5e.mybigcommerce.com (63.141.159.120) 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-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=AU; ST=New South Wales; L=Ultimo; O=Bigcommerce Pty Ltd; CN=*.mybigcommerce.com
* start date: Jun 4 00:00:00 2015 GMT
* expire date: Sep 1 12:00:00 2018 GMT
* subjectAltName: host "store-bgf5e.mybigcommerce.com" matched cert's "*.mybigcommerce.com"
* issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert SHA2 High Assurance Server CA
* SSL certificate verify ok.
> GET /api/v2/customers HTTP/1.1
Host: store-bgf5e.mybigcommerce.com
Authorization: Basic ZGFubnk6M2QyNjE5NTdjZGNjMTNlYmU1MTJiNDRiMjE1NGJiZGZmMGRjNTUxMw==
Accept: application/json
Content-Type: application/json
< HTTP/1.1 200 OK
< Server: openresty
< Date: Fri, 05 May 2017 19:26:27 GMT
< Content-Type: application/json
< Transfer-Encoding: chunked
< Connection: keep-alive
< Set-Cookie: fornax_anonymousId=0ff167a5-e158-4ff8-8962-67b19bd4271b; expires=Mon, 03-May-2027 19:26:27 GMT; path=/; domain=.store-bgf5e.mybigcommerce.com
< Last-Modified: Thu, 13 Apr 2017 22:34:54 +0000
< X-BC-ApiLimit-Remaining: 2147483622
< X-BC-Store-Version: 7.6.0
<
* Curl_http_done: called premature == 0
* Connection #0 to host store-bgf5e.mybigcommerce.com left intact
To me it looks like Curl is executing everything correctly. It's almost like the successful curl request prevents any other PHP code from running. I would expect to see addresses being printed out to the command line because of "print_r".
Thanks in advance for any tips you might have.
may you can try to var_dump your result and see what happend. For your curl response I did not see any content-length header, I think curl return empty string.
and please can you post your php code ?
Figured this out. I was using this framework to connect to the BigCommerce API: https://github.com/adambilsing/PHP-cURL-lib-for-Bigcommerce-API/blob/master/connection.php
In the http_parse_headers method of the connection class there is an if statement on line 54:
if ($retVal['X-Bc-Apilimit-Remaining'] <= 100) {
sleep(300);
}
This would trigger the script to go to sleep even if $retVal['X-Bc-Apilimit-Remaining'] is null.
Changing it to this fixed the issue:
if ( isset($retVal['X-Bc-Apilimit-Remaining']) && $retVal['X-Bc-Apilimit-Remaining'] <= 100) {
sleep(300);
}
It's pretty obvious once you look into it. I wasn't even aware that there is a sleep command in php.
Thanks for everybody who was trying to make sense of this :)