Sending HTTP/2 request using PHP - php

My problem statement is similar to the one mentioned here , except that the request type I need server to respond to is HTTP/2.
I am opening a telnet connection through a PHP script and sending a request:
<?php
$fp = fsockopen("10.20.0.120", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET /mng/1000.txt HTTP/2 \r\n";
$out .= "Host: 10.20.0.120\r\n";
$out .= "Connection: Close\r\n";
fwrite($fp, $out);
$out = "\r\n";
$out .= "\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
?>
I am aware that in case of HTTP/2 the header frame must be in binary format, hence I need to know if I could do it using PHP.
I tried with PHPs pack method, but it didn't work. When I execute the above script, this is the output it gives:
Client(root)#php above_file.php
HTTP/1.1 400 Bad Request
Content-Length: 0
...and when the request is of type HTTP/1.1 (and executed on another setup replacing HTTP/2 with HTTP/1.1), the output I receive is as expected:
Client(root)#php above_file.php
HTTP/1.1 200 OK
Date: Wed, 27 Sep 2017 11:31:57 GMT
Server: Apache/2.2.22 (Fedora)
Last-Modified: Wed, 20 Feb 2013 21:27:15 GMT
ETag: "a4f5b-3e8-4d62e9f40f9be"
Accept-Ranges: bytes
Content-Length: 12
Connection: close
Content-Type: text/plain; charset=UTF-8
Hello world!

Related

Why does tlstest.paypal.com work from browser but not from my PHP code (useful for Paypal IPN)?

After 2018 June 30th, Paypal won't accept non-TLS 1.2 + HTTP 1.1 requests anymore.
They created the URL https://tlstest.paypal.com/ to test if connections are OK. If we open this URL in a browser, we get a successful:
PayPal_Connection_OK
Quesiton: why does it fail when connecting from PHP with the following code? (I get no response at all, the browser is still in waiting "state" like this, so it doesn't even arrive at echo $errno; echo $errstr;)
<?php
$req = ''; // usually I use $req = 'cmd=_notify-validate'; for IPN
$header .= "POST / HTTP/1.1\r\n";
$header .= "Host: tlstest.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen('tls://tlstest.paypal.com', 443, $errno, $errstr, 30);
if (!$fp) {
echo $errno;
echo $errstr;
} else {
fputs($fp, $header);
while (!feof($fp))
{
$res = fgets($fp, 1024);
echo $res;
}
fclose($fp);
}
?>
Note:
this is not a duplicate of Paypal IPN HTTP/1.1 - doesn't provide any response at all, it is an empty string, the latter is outdated.
I have PHP 5.6.33-0+deb8u1 (cli) (built: Jan 5 2018 15:46:26) and openssl version text: OpenSSL 1.0.1t 3 May 2016
It works on my side by changing tls:// to ssl:// which makes absolutely no sense to me, but this is also why using fsockopen is a too low level library to just do HTTP exchanges with it (you should use a proper HTTP client library) and at the same time not configurable enough regarding TLS stuff.
With
$fp = fsockopen('tls://tlstest.paypal.com', 443, $errno, $errstr, 30);
I get :
HTTP/1.1 426 Unknown
Server: AkamaiGHost
Mime-Version: 1.0
Content-Type: text/html
Content-Length: 267
Expires: Fri, 22 Jun 2018 19:49:46 GMT
Date: Fri, 22 Jun 2018 19:49:46 GMT
Connection: keep-alive
Upgrade: TLS/1.2
<HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD><BODY>
<H1>Access Denied</H1>
You don't have permission to access "http://tlstest.paypal.com/" on this server.<P>
Reference #18.8024a17.1529696986.1fc51318
</BODY>
</HTML>
but with $fp = fsockopen('ssl://tlstest.paypal.com', 443, $errno, $errstr, 30);
I get:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Date: Fri, 22 Jun 2018 20:05:35 GMT
Connection: keep-alive
And then it hangs, probably because it is a keep-alive connection and the buffer is smaller than 1024 so that you do not get the following body content.
Which is probably "PayPal_Connection_OK", as it exactly matches the length displayed in Content-Length.
This again shows that you should use an HTTP client library instead of trying to (badly) reimplement HTTP on top of fsockopen.
For completeness, here is a working code (full credit to PatrickMevzek's answer):
<?php
$req = '';
$header = "POST / HTTP/1.1\r\n";
$header .= "Host: tlstest.paypal.com\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen('ssl://tlstest.paypal.com', 443, $errno, $errstr, 3);
if (!$fp) {
echo $errno;
echo $errstr;
} else {
fputs($fp, $header);
while (!feof($fp))
{
$res = fgets($fp, 21); // 21 because length of "PayPal_Connection_OK"
echo $res;
}
fclose($fp);
}
?>
Here is the answer from server:
# php -f test.php
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 20
Date: Fri, 22 Jun 2018 20:19:56 GMT
Connection: keep-alive
PayPal_Connection_OK

live audio stream socket get stuck in browser

I'm trying to setup a page where several (private) streams can be listened from.
Unfortunately I'm not able to get it running. I tried Using php to opening live audio stream on android already, but for some reason the browser get stuck when loading the script.
See below script with an example of a working host (see http://icecast.omroep.nl/radio4-bb-mp3)
Could someone please enlighten me.
Tnx in advance!
$host = "icecast.omroep.nl";
$port = 80;
$sub = "/radio4-bb-mp3";
$sock = fsockopen($host,$port, $errno, $errstr, 30);
if (!$sock){
throw new Exception("$errstr ($errno)");
}
header("Content-type: audio/mpeg");
header("Connection: close");
fputs($sock, "GET $sub HTTP/1\r\n");
fputs($sock, "Host: $host \r\n");
fputs($sock, "Accept: */*\r\n");
fputs($sock, "Icy-MetaData:1\r\n");
fputs($sock, "Connection: close\r\n\r\n");
fpassthru($sock);
fclose($sock);
Following comments the solution you are looking is:
<?php
$host = "icecast.omroep.nl";
$sub = "/radio4-bb-mp3";
header("Location: http://{$host}{$sub}");
Now I will explain what was wrong with your code
You have a problem with the headers. You are adding your own headers and the remote headers as part of the body.
icecast.omroep.nl headers
HTTP/1.0 200 OK
Content-Type: audio/mpeg
Date: Sat, 24 Mar 2018 16:01:23 GMT
icy-br:192
ice-audio-info: samplerate=48000;channels=2;bitrate=192
icy-br:192
icy-genre:Classical
icy-metadata:1
icy-name:NPO Radio4
icy-pub:0
icy-url:http://www.radio4.nl
Server: Icecast 2.4.0-kh8
Cache-Control: no-cache, no-store
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type
Access-Control-Allow-Methods: GET, OPTIONS, HEAD
Connection: Close
Expires: Mon, 26 Jul 1997 05:00:00 GMT
icy-metaint:16000
Given your script index.php
<?php
$host = "icecast.omroep.nl";
$port = 80;
$sub = "/radio4-bb-mp3";
$sock = fsockopen($host,$port, $errno, $errstr, 30);
if (!$sock){
throw new Exception("$errstr ($errno)");
}
fputs($sock, "GET $sub HTTP/1\r\n");
fputs($sock, "Host: $host \r\n");
fputs($sock, "Accept: */*\r\n");
fputs($sock, "Icy-MetaData:1\r\n");
fputs($sock, "Connection: close\r\n\r\n");
fpassthru($sock);
fclose($sock);
request.txt
GET /
[Blank line]
Serving your script
$ php -S 0.0.0.0:8000 index.php
Your script response:
$ (nc 127.0.0.1 8000 < request.txt) | head -n 27
HTTP/0.9 200 OK
Date: Sat, 24 Mar 2018 16:01:23 +0000
Connection: close
X-Powered-By: PHP/7.1.14
Content-type: text/html; charset=UTF-8
HTTP/1.0 200 OK
Content-Type: audio/mpeg
Date: Sat, 24 Mar 2018 16:01:23 GMT
icy-br:192
ice-audio-info: samplerate=48000;channels=2;bitrate=192
icy-br:192
icy-genre:Classical
icy-metadata:1
icy-name:NPO Radio4
icy-pub:0
icy-url:http://www.radio4.nl
Server: Icecast 2.4.0-kh8
Cache-Control: no-cache, no-store
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type
Access-Control-Allow-Methods: GET, OPTIONS, HEAD
Connection: Close
Expires: Mon, 26 Jul 1997 05:00:00 GMT
icy-metaint:16000
PHP is adding his own headers.
You need to process the headers received from http://icecast.omroep.nl/radio4-bb-mp3 and return them using the method header() and then you can do the fpassthru().
HTTP separate the headers from the body with a new line: https://www.rfc-editor.org/rfc/rfc2616#section-6
[header]
CRLF
[body]
So it should be easy to parse line by line and calling header() until CRLF (empty line) is found and then trigger the fpassthru().

Getting page with fsockopen

I'm trying to access http://www.example.com:4380/apid/request?method=getXMLTable&name=1&ui=UI&id=12345. It should give back output as XML.
This is the code:
<?
$host = "www.example.com";
$path = "/apid/request?method=getXMLTable&name=1&ui=UI&id=12345";
$port = 4380;
$fp = fsockopen($host, $port, $errno, $errstr, 30);
$buffer ="";
if (!$fp) {
echo "ERR!!<br>";
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET ".$path." HTTP/1.1\r\n";
$out .= "Host: ".$host."\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
$buffer .= fgets($fp, 1024);
}
fclose($fp);
}
?>
Anyhow I'm getting not the XML output, but this response:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Last-Modified: Wed, 02 Apr 2014 16:16:43 GMT
Cache-Control: no-store
Cache-Control: no-cache
Cache-Control: must-revalidate
Cache-Control: pre-check=0
Cache-Control: post-check=0
Cache-Control: max-age=0
Pragma: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/xml
Transfer-Encoding: chunked
Vary: Accept-Encoding
Date: Wed, 02 Apr 2014 16:16:43 GMT
Connection: close 2000 0
I can access pages like 'www.example.com/path/to/file' with the presented code without any problems. I guess I make any mistake with port(not standard http ) or request. Every clue would be very welcome.
EDIT:
I can't use any http module! Socket is a must in this case!
I have tried with the code below, but I get nothing in $body:
list($header, $body) = explode("\n\n", $buffer, 2);
echo http_chunked_decode($body);
You are getting a Transfer-Encoding: chunked response. You need to use http_chunked_decode to decode it (you can find a PHP version here, if you don't have pecl_http).
list($header, $body) = explode("\n\n", $buffer, 2);
echo http_chunked_decode($body);
But as the other commenter said, you should really use file_get_contents if you have allow_url_fopen enabled or curl otherwise. They handle the chunked encoding for you.

fsockopen returning server error(0)

I'm checking fsockopen in Ubuntu server 13.04 with this code:
<?php
$fp = fsockopen("www.google.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: www.example.com\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
?>
and the server returning
php_network_getaddresses: getaddrinfo failed: System error (0)
Any help with this?
There is no problem in your code - it's fine and working!
Most probably the firewall is blocking 80 port and this is why you can't connect.
Check your connection from the console and see what you get:
ping google.com
EDIT 1:
Most likely you have a problem in your /etc/resolv.conf or /etc/hosts. To solve this you could refer to: Ping: Unknown host. If you can't just post output of those files and we'll see of what I could do!
This means that your script cannot resolve the hostname to an IP address. Probably there is a problem with your dns configuration.
When I try on my Ubuntu it works:
<HTTP/1.1 302 Found
Location: http://www.google.com/
Cache-Control: private
Content-Type: text/html; charset=UTF-8
X-Content-Type-Options: nosniff
Date: Sun, 12 Jan 2014 13:55:40 GMT
Server: sffe
Content-Length: 219
X-XSS-Protection: 1; mode=block
Alternate-Protocol: 80:quic
Connection: close
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
here.
</BODY></HTML>
The issue might come from your connection rather than your code. Check e.g. if your behind a firewall.

Fsockopen not working when initiated by server on itself

I use a Yii extension called ERunActions to run background process such as sending mails to users. This extension internally uses fsockopen to accomplish this task. It all works fine in development but in server using fsockopen redirects me to http://example.com/cgi-sys/defaultwebpage.cgi
I read the documentation for fsockopen in php.net - http://us3.php.net/function.fsockopen
and ran the following code in my dev environment (I replaced example.com with my site URL), everything works fine & the HTML content of my home page is shown, but when i run the same code in server I get redirected to the cgi URL. Any help will be greatly appreciated thank you.
<?php
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
$out = "GET / HTTP/1.1\r\n";
$out .= "Host: www.example.com\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)) {
echo fgets($fp, 128);
}
fclose($fp);
}
?>
I found this in the access log
HTTP/1.1 200 OK Date: Wed, 27 Nov 2013 11:25:17 GMT Server: Apache/2.2.25 (Unix) mod_ssl/2.2.25 OpenSSL/1.0.0-fips mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 Last-Modified: Mon, 02 Sep 2013 10:19:00 GMT ETag: "43bdf-6f-4e563e69a0d03" Accept-Ranges: bytes Content-Length: 111 Connection: close Content-Type: text/html

Categories