I am kinda new to PHP however I used JSP a lot before (I have quite information) and everything was easier with Java classes.
So, now, I want to perform a POST request on a HTTPS page (not HTTP) and need to get returned cookies and past it to another GET request and return the final result. Aim is to make a heavy page for mobile phones more compatible to view in a mobile browser by bypassing the login page and directly taking to the pages which are also served in an ajax user interface.
I am stuck, my code does not work, it says it is Bad Request.
Bad Request
Your browser sent a request that this
server could not understand. Reason:
You're speaking plain HTTP to an
SSL-enabled server port. Instead use
the HTTPS scheme to access this URL,
please.
<?php
$content = '';
$flag = false;
$post_query = 'SOME QUERY'; // name-value pairs
$post_query = urlencode($post_query) . "\r\n";
$host = 'HOST';
$path = 'PATH';
$fp = fsockopen($host, '443');
if ($fp) {
fputs($fp, "POST $path HTTP/1.0\r\n");
fputs($fp, "Host: $host\r\n");
fputs($fp, "Content-length: ". strlen($post_query) ."\r\n\r\n");
fputs($fp, $post_query);
while (!feof($fp)) {
$line = fgets($fp, 10240);
if ($flag) {
$content .= $line;
} else {
$headers .= $line;
if (strlen(trim($line)) == 0) {
$flag = true;
}
}
}
fclose($fp);
}
echo $headers;
echo $content;
?>
From past experience, I've never used PHP's internal functions like fsocketopen() for external data posting. The best way to do these actions are using CURL, which gives much more ease and is massively more powerful for developers to leverage.
for example, look at these functions
http://php.net/curl_setopt
and look at the one with URL, POST, POSTDATA, and COOKIESFILES which is for .JAR, which you get then retrieve and that you can use file_get_contents() to send the data using GET.
Related
I am trying to convert this code snippet from PHP to Python (programming newbie) and am finding difficulty in doing so:
The PHP that I am trying to convert is as follows:
$fp = fsockopen($whmcsurl, 80, $errno, $errstr, 5);
if ($fp) {
$querystring = "";
foreach ($postfields AS $k=>$v) {
$querystring .= "$k=".urlencode($v)."&";
}
$header="POST ".$whmcsurl."modules/servers/licensing/verify.php HTTP/1.0\r\n";
$header.="Host: ".$whmcsurl."\r\n";
$header.="Content-type: application/x-www-form-urlencoded\r\n";
$header.="Content-length: ".#strlen($querystring)."\r\n";
$header.="Connection: close\r\n\r\n";
$header.=$querystring;
$data="";
#stream_set_timeout($fp, 20);
#fputs($fp, $header);
$status = #socket_get_status($fp);
while (!#feof($fp)&&$status) {
$data .= #fgets($fp, 1024);
$status = #socket_get_status($fp);
}
#fclose ($fp);
}
It corresponding Python code that I wrote is as follows:
fp = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
fp.connect(("my ip", 80))
if (fp):
querystring = ""
#print postfields
for key in postfields:
querystring = querystring+key+"="+urllib.quote(str(postfields[key]))+"&"
header = "POST "+whmcsurl+"modules/servers/licensing/verify.php HTTP/1.0\r\n"
header+="Content-type: application/x-www-form-urlencoded\r\n"
header+="Content-length: "+str(len(querystring))+"\r\n"
header+="Connection: close\r\n\r\n"
#header+=querystring
data=""
request = urllib2.Request(whmcsurl,querystring,header)
response = urllib2.urlopen(request)
data = response.read()
Here, I am faced with the following error:
request = urllib2.Request(whmcsurl,querystring,header)
File "/usr/lib64/python2.6/urllib2.py", line 200, in __init__
for key, value in headers.items():
AttributeError: 'str' object has no attribute 'items'
So I am guessing that Python is expecting a dictionary for the header. But the PHP sends it as a string.
May I know how to solve this issue?
Thanks in advance
You are overcomplicating things, by quite some distance. Python takes care of most of this for you. There is no need to open a socket yourself, nor do you need to build headers and the HTTP opening line.
Use the urllib.request and urllib.parse modules to do the work for you:
from urllib.parse import urlopen
from urllib.request import urlopen
params = urlencode(postfields)
url = whmcsurl + 'modules/servers/licensing/verify.php'
response = urlopen(url, params)
data = response.read()
urlopen() takes a second parameter, the data to be sent in a POST request; the library takes care of calculating the length of the body, and sets the appropriate headers. Most of all, under the hood it uses another library, httplib, to take care of the socket connection and producing valid headers and a HTTP request line.
The POST body is encoded using urllib.parse.urlencode(), which also takes care of proper quoting for you.
You may also want to look into the external requests library, which provides an easier-to-use API still:
import requests
response = requests.post(whmcsurl + 'modules/servers/licensing/verify.php', params=params)
data = response.content # or response.text for decoded content, or response.json(), etc.
your headers should look like this
headers = { "Content-type" : "application/x-www-form-urlencoded" };
Here's my code:
$language = $_GET['soundtype'];
$word = $_GET['sound'];
$word = urlencode($word);
if ($language == 'english') {
$url = "<the first url>";
} else if ($language == 'chinese') {
$url = "<the second url>";
}
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"User-Agent: <my user agent>"
)
);
$context = stream_context_create($opts);
$page = file_get_contents($url, false, $context);
header('Content-Type: audio/mpeg');
echo $page;
But I've found that this runs terribly slow.
Are there any possible methods of optimization?
Note: $url is a remote url.
It's slow because file_get_contents() reads the entire file into $page, PHP waits for the file to be received before outputting the content. So what you're doing is: downloading the entire file on the server side, then outputting it as a single huge string.
file_get_contents() does not support streaming or grabbing offsets of the remote file. An option is to create a raw socket with fsockopen(), do the HTTP request, and read the response in a loop, as you read each chunk, output it to the browser. This will be faster because the file will be streamed.
Example from the Manual:
$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
header('Content-Type: audio/mpeg');
$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);
}
The above is looping while there is still content available, on each iteration it reads 128 bytes and then outputs it to the browser. The same principle will work for what you're doing. You'll need to make sure that you don't output the response HTTP headers which will be the first few lines, because since you are doing a raw request, you will get the raw response with headers included. If you output the response headers you will end up with a corrupt file.
Instead of downloading the whole file before outputting it, consider streaming it out like this:
$in = fopen($url, 'rb', false, $context);
$out = fopen('php://output', 'wb');
header('Content-Type: video/mpeg');
stream_copy_to_stream($in, $out);
If you're daring, you could even try (but that's definitely experimental):
header('Content-Type: video/mpeg');
copy($url, 'php://output');
Another option is using internal redirects and making your web server proxy the request for you. That would free up PHP to do something else. See also my post regarding X-Sendfile and friends.
As explained by #MrCode, first downloading the file to your server, then passing it on to the client will of course incur a doubled download time. If you want to pass the file on to the client directly, use readfile.
Alternatively, think about if you can't simply redirect the client to the file URL using a header("Location: $url") so the client can get the file directly from the source.
I am working on creating a system which is secured by passing tokens from page to page to verify the validity of requests. The token is to be generated on the login page (as it only needs to be generated once) and then passed through to the main page. However, there is an intermediate PHP script which is being run to log the user in, and I am not sure how to take the posted token and pass it on to the main page.
In short, I need to post the token from the login page to the intermediary script, and then from the script to the main page, and I'm not sure how to do that.
If all 3 pages are on the same host, use a cookie or a session.
Store the data in a cookie/session instead:
session_start();
$_SESSION['token'] = 'yourTokenHere'
See PHP Sessions
I wrote this a long time ago:
function postheader($url, $server, $cookies, $daten)
{
$temp=array();
$out = "POST ".$url." HTTP/1.1\r\n";
$out .= "Host: ".$server."\r\n";
if(count($cookies)>0)
{
$out .= "Cookie: ";
foreach($cookies as $name=>$value)
{
$temp[] = $name."=".$value;
}
$out .= implode("; ",$temp);
$out .= "\r\n";
}
$out .= "User-Agent: Mozilla/4.0\r\n";
$out .= "Content-type: application/x-www-form-urlencoded\r\n";
$temp=array();
foreach($daten as $key=>$data)
{
$temp[] = $key."=".urlencode($data);
}
$temp=implode("&",$temp);
$out .= "Content-Length: ".strlen($temp)."\r\n";
$out .= "\r\n";
$out .= $temp."\r\n";
return $out;
}
$fp = fsockopen($server, 80, $errno, $errstr, 30);
if (!$fp)
die( "$errstr ($errno)<br />\n");
$out=postheader($url, $server, $cookies, $daten);
//echo $out;
fwrite($fp, $out);
while (!feof($fp))
{
$string.= fgets($fp, 128);
}
fclose($fp);
$daten should be a one dimensional associative array containing the data you want to send via the post request. For cookies, just add an empty array.
$server is the host address, and $url is only the address on the server, e.g.
$server="stackoverflow.com";
$url="/post.php";
$string will contain the whole response including the headers. If you don't want them, do a substring from the first "\r\n\r\n" occurence.
You can use Client side cookies:
<?php
setcookie("token", $myToken);
?>
The biggest fault with this method is that it absolutely requires the user to have cookies enabled on their browser. In highly secure situations a user may not have cookies enabled so this method wouldn't work for them.
Use $_SESSION global variable:
<?php
$_SESSION['token'] = $myToken;
?>
As previous variant, it requires cookies to be enabled.
Add $_GET parameter to URI string:
http://example.com?token=f4FArqk53Gwr4fESC73FedG48Trd3YEj
The biggest fault of this method is that your URI string will look very complex.
Also state information will be lost if user will manually modify URI.
Or pass everything in Hidden form fields:
Unfortunately it requires you to submit a form on every page.
You can do it with jQuery.
Some examples are here.
On further consideration, it looks like I was making things much more complicated than they needed to be. Thank you all for your help, but the solution seems to be that I should be a bit more intelligent.
sorry for the title but I'm not reaaly sure how to call this.
I am registered to a ssm service which enables sending automatic sms with a php script.
the script basically builds an xml string with all of the sms parameters (sendername, ..).
then it uses this to send it :
$sms_host = "api.inforu.co.il"; // Application server's URL;
$sms_port = 80; // Application server's PORT;
////.... generating query
$sms_path = "/SendMessageXml.ashx"; // Application server's PATH;
$fp = fsockopen($sms_host, $sms_port, $errno, $errstr, 30); // Opens a socket to the Application server
if (!$fp){ // Verifies that the socket has been opened and sending the message;
echo "$errstr ($errno)<br />\n";
echo "no error";
} else {
$out = "GET $sms_path?$query HTTP/1.1\r\n";
$out .= "Host: $sms_host\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
while (!feof($fp)){
echo fgets($fp, 128);
}
fclose($fp);
the query is fine if I paste this
$url = "http://api.inforu.co.il/SendMessageXml.ashx?" . $query;
directly in the browser, then the sms gets send.
so the problem is that I'm getting an error
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /SendMessageXml.ashx
You have to urlencode() your $query, if you paste it to browser, the browser will encode it for you, but when you are dealing with a socket, you have to do it yourself.
I would like to create a batch script, to go through 20,000 links in a DB, and weed out all the 404s and such. How would I get the HTTP status code for a remote url?
Preferably not using curl, since I dont have it installed.
CURL would be perfect but since you don't have it, you'll have to get down and dirty with sockets. The technique is:
Open a socket to the server.
Send an HTTP HEAD request.
Parse the response.
Here is a quick example:
<?php
$url = parse_url('http://www.example.com/index.html');
$host = $url['host'];
$port = $url['port'];
$path = $url['path'];
$query = $url['query'];
if(!$port)
$port = 80;
$request = "HEAD $path?$query HTTP/1.1\r\n"
."Host: $host\r\n"
."Connection: close\r\n"
."\r\n";
$address = gethostbyname($host);
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, $address, $port);
socket_write($socket, $request, strlen($request));
$response = split(' ', socket_read($socket, 1024));
print "<p>Response: ". $response[1] ."</p>\r\n";
socket_close($socket);
?>
UPDATE: I've added a few lines to parse the URL
If im not mistaken none of the php built-in functions return the http status of a remote url, so the best option would be to use sockets to open a connection to the server, send a request and parse the response status:
pseudo code:
parse url => $host, $port, $path
$http_request = "GET $path HTTP/1.0\nHhost: $host\n\n";
$fp = fsockopen($host, $port, $errno, $errstr, $timeout), check for any errors
fwrite($fp, $request)
while (!feof($fp)) {
$headers .= fgets($fp, 4096);
$status = <parse $headers >
if (<status read>)
break;
}
fclose($fp)
Another option is to use an already build http client class in php that can return the headers without fetching the full page content, there should be a few open source classes available on the net...
This page looks like it has a pretty good setup to download a page using either curl or fsockopen, and can get the HTTP headers using either method (which is what you want, really).
After using that method, you'd want to check $output['info']['http_code'] to get the data you want.
Hope that helps.
You can use PEAR's HTTP::head function.
http://pear.php.net/manual/en/package.http.http.head.php