Sending a HTTP POST request from Python (trying to convert from PHP) - php

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" };

Related

PHP cURL Upload file without processing

I am trying to upload a file to a Rest API (Tableau Server Rest API) endpoint using PHP cURL.
The file upload procedure exists of three steps:
Initiate file upload (request upload token)
Append file upload (upload file data)
Publish resource (save the file)
I was having issues with the server giving me a 500 status code on the second step. After contacting the support we figured out that the problem is most likely that the curl request seems to using the -data flag instead of the --data-binary flag which means some kind of encoding happens on the request body which should not be happening. This causes the server to respond with a 500 status code instead of an actual error message...
I would like to know how i can make a cURL request with the --data-binary flag in PHP.
Relevant sections of my current code:
// more settings
$curl_opts[CURLOPT_CUSTOMREQUEST] = $method;
$curl_opts[CURLOPT_RETURNTRANSFER] = true;
$curl_opts[CURLOPT_HTTPHEADER] = $headers;
$curl_opts[CURLOPT_POSTFIELDS] = $body;
//more settings
curl_setopt_array( $this->ch, $curl_opts );
$responseBody = curl_exec( $this->ch );
$method is "PUT", $headers contains an array with Content-Type: multipart/mixed; boundary=boundary-string and the $body is constructed like this:
$boundary = md5(date('r', time()));
$body = "--$boundary\n";
$body .= "Content-Disposition: name='request_payload'\nContent-Type: text/xml\n\n";
$body .= "\n--$boundary\n";
$body .= "Content-Disposition: name='tableau_file'; filename='$fileName'\nContent-Type: application/octet-stream\n\n";
$body .= file_get_contents( $path );
$body .= "\n--$boundary--";
Where the $boundary is the same as the boundary-string in the content-type header.
i am aware this is a kinda/very messy way to construct my body, i am planning to use Mustache as soon as i can actually upload my files :S
(i would like to mention that this is my first post here, please be gentle...)
CURLOPT_POSTFIELDS can accept an array of key=value field pairs. DON'T build your own mime body.
This is all you should have, really:
$data = array(
'tableau_file' => '#/path/to/file';
^---tell curl this field is a file
etc..
);
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $data);
I ended up trying to implement a "--data-binary" using the exec function in PHP. Before i could move to this i had to cleanup some of my request body building code. This cleanup involved moving from string building ($body .= "this" . $that . "\n";) to a template engine (Mustache). Somehow this change fixed the problem.

How to speed up file_get_contents?

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.

HTTPS Post Request via PHP and Cookies

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.

How to redirect a live data stream adding to it another header and returning it on demand? (PHP)

I have a url like http://localhost:8020/stream.flv
On request to my php sctipt I want to return (be something like a proxy) all data I can get from that URL (so I mean my php code should get data from that url and give it to user) and my header and my beginning of file.
So I have my header and some data I want to write in the beginning of response like
# content headers
header("Content-Type: video/x-flv");
# FLV file format header
if($seekPos != 0)
{
print('FLV');
print(pack('C', 1));
print(pack('C', 1));
print(pack('N', 9));
print(pack('N', 9));
}
How to do such thing?
Here's the code I used to do something very similar, I also forwarded all headers from the original flv to the end user but you can just remove that section if you want to set your own.
$video = fopen(URL, "rb");
// Forward headers, $http_response_header is populated by fopen call
foreach ($http_response_header AS $header) {
header($header);
}
// Output contents of flv
while (!feof($video)) {
print (fgets($video));
}
fclose($video);
See the manual entries for fopen and fread.
I've accomplished this using libcurl,
$url = 'http://localhost:8020/stream.flv';
$ch = curl_init();
curl_exec($ch);
curl_close($ch);

How do you get the HTTP status code for a remote domain in php?

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

Categories