Get header info from php://input - php

I have a file(image) from device that is send via put
PUT /r.php HTTP/1.1
User-Agent: PHS/2.0.6
Host: localhost
Accept: */*
Content-Name: cam20141020084031.jpg,10001019
Content-Length: 35183
Expect: 100-continue
This is how I get picture that was send:
$res = file_get_contents("php://input");
$file = fopen('1.jpg', "w");
fputs($file, $res);
fclose($file);
I need to get content-name separately too. I can't find anywhere how can I get it. Can anyone help?
UPDATE
$res = file_get_contents("php://input");
$vars=parse_str($res,$post_vars);
$headers = getallheaders();
$contentName=$headers['Content-Name'])
$file = fopen('1.jpg', "w");
$fileVar= fopen('1.txt', "w");
fputs($file, $res);
fputs($fileVar,$res);
fclose($fileVar);
fclose($file);
Strange, but this code seems to be loading forever.
UPDATE 1
When I print_r it I understand that it's not headers of put request it's headers of page. Not what I need.

You can try using getallheaders() function, which exists for sole purpose of retrieving request headers:
$headers = getallheaders();
var_dump($headers['Content-Name']);
Note that it might be best to preprocess keys to take care of headers such as Content-name (note the change of letter case near -).

Related

php log complete request

Is it possible to log the request with php?
I also want to log the images, js, css file requests.
<img src="foo.png">
<link rel="stylesheet" href="foo.css">
I currently use this code but it only gives me the current request uri and not the other files etc.
I also rewrited all request to my index.php wich i have this line of code in.
file_put_contents('foo.txt', $_SERVER['REQUEST_URI'] . PHP_EOL, FILE_APPEND | LOCK_EX);
Here is one option to draw all http requests to a file. This applies to all requests that travel with the HTTP protocol
$myFile = "requestslog.txt";
$fh = fopen($myFile, 'a') or die("can't open file");
fwrite($fh, "\n\n--------------------------------------
-------------------------\n");
foreach($_SERVER as $h=>$v)
if(ereg('HTTP_(.+)',$h,$hp))
fwrite($fh, "$h = $v\n");
fwrite($fh, "\r\n");
fwrite($fh, file_get_contents('php://input'));
fclose($fh);
echo "<html><head /><body><iframe src=\"$myFile\"
style=\"height:100%; width:100%;\"></iframe></body></html>"
Yes, it is. You can make all the requests pass through a php script, so that you can log the action. For example, a simple image request like http://url.com/img.jpg would became http://url.com/index.php?action=download&file=img.jpg, and the script would handle the logging, the file download and correct headers.
Also take into account that your http server might be logging the request already, take a look into the access_log of apache if you are using it.
I prefer the approach listed on this gist.
<?php
// https://gist.github.com/magnetikonline/650e30e485c0f91f2f40
class DumpHTTPRequestToFile {
public function execute($targetFile) {
$data = sprintf(
"%s %s %s\n\nHTTP headers:\n",
$_SERVER['REQUEST_METHOD'],
$_SERVER['REQUEST_URI'],
$_SERVER['SERVER_PROTOCOL']
);
foreach ($this->getHeaderList() as $name => $value) {
$data .= $name . ': ' . $value . "\n";
}
$data .= "\nRequest body:\n";
file_put_contents(
$targetFile,
$data . file_get_contents('php://input') . "\n"
);
echo("Done!\n\n");
}
private function getHeaderList() {
$headerList = [];
foreach ($_SERVER as $name => $value) {
if (preg_match('/^HTTP_/',$name)) {
// convert HTTP_HEADER_NAME to Header-Name
$name = strtr(substr($name,5),'_',' ');
$name = ucwords(strtolower($name));
$name = strtr($name,' ','-');
// add to list
$headerList[$name] = $value;
}
}
return $headerList;
}
}
(new DumpHTTPRequestToFile)->execute('./dumprequest.txt');
You can quickly copy the above file by doing curl -O https://gist.githubusercontent.com/magnetikonline/650e30e485c0f91f2f40/raw/cbc114d0af29eaad80f75b69732d757971c71fd0/dumprequest.php > dumprequest.php.
The output will be something like
GET /dumprequest.php HTTP/1.1
HTTP headers: Authorization: User-Agent: PostmanRuntime/7.29.0 Accept:
/ Cache-Control: no-cache Host: somehost.com Accept-Encoding: gzip,
deflate, br Connection: keep-alive
Request body: hi=ricardo
// Reports all errors
error_reporting(E_ALL);
// Do not display errors for the end-users (security issue)
ini_set('display_errors','Off');
// Set a logging file
ini_set('error_log','request_log_file.log');
Note- request_log_file.log - you can set your full file path here if needed.
Hope this will help you!

Why does my php upload not work?

TL;DR: why does reuploding the uploaded data not work?
I' trying to upload the data a user uploaded to my file to another server. This means, I want to post my post data.
I want to upload some data to upload.php, which then should get posted to test.php (which simply outputs the raw post data). And due to memory saving, I wanted it to work without generating the post whole message as string. Thus I also don't want to use curl.
test.php
<?php
echo 'foo', file_get_contents('php://input'), 'bar';
upload.php
<?php
//new line variable
$NL = "\r\n";
//open the posted data as a resource
$client_upload = fopen('php://input', 'r');
//open the connection to the other server
$server_upload = fsockopen('ssl://example.com', 443);
//write the http headers to the socket
fwrite($server_upload, 'POST /test.php HTTP/1.1' . $NL);
fwrite($server_upload, 'Host: example.com' . $NL);
fwrite($server_upload, 'Connection: close' . $NL);
//header/body divider
fwrite($server_upload, $NL);
//loop until client upload reached the end
while (!feof($client_upload)) {
fwrite($server_upload, fread($client_upload, 1024));
}
//close the client upload resource - not needed anymore
fclose($client_upload);
//intitalize response variable
$response = '';
//loop until server upload reached the end
while (!feof($server_upload)) {
$response .= fgets($server_upload, 1024);
}
//close the server upload resource - not needed anymore
fclose($server_upload);
//output the response
echo $response;
When I post { "test": true } (from Fiddler) to test.php file, it outputs foo{ "test": true }bar.
Now when I try to do the same for upload.php, I just get foobar (and the http headers from test.php) but not the uploaded content.
Finally I managed to fix this error. Apparently the other server (and mine) depend on the http header Content-Length.
As you can see in my answer, I was not sending (neither calculating) this header. So when I finally now calculated and sent the Content-Length header everything worked. This are the missing lines:
$content_length = fstat($client_upload)['size'];
fwrite($server_upload, 'Content-Length: ' . $content_length . $NL);
The reupload (of the uploaded data to my server) didn't work, because the other server just read the body as long as it was specified in the Content-Length header. Because I was not sending this header, it didn't work.

How to get the contents of application/json type in php?

I used file_get_contents('php://input') but sometimes it won't work. I checked the headers using getallheaders() and the Content-Length has always the size of the content but sometimes file_get_contents('php://input') returns null.
Try This:
$handle = fopen('php://input','r');
$jsonInput = fgets($handle);
$decoded = json_decode($jsonInput,true);
//$decoded['tag'] you data

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.

Seeking in remote flv files using PHP

I'm string to seek in a remotely hosted FLV file and have it stream locally. Streaming from start works, but when I try to 'seek', player stops.
I'm using this script to seek to remote file
$fp = fsockopen($host, 80, $errno, $errstr, 30);
$out = "GET $path_to_flv HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Range: bytes=$pos-$end\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
$content = false;
while (!feof($fp))
{
$data = fgets($fp, 1024);
if($content) echo $data;
if($data == "\r\n")
{
$content = true;
header("Content-Type: video/x-flv");
header("Content-Length: " . (urlfilesize($file) - $pos));
if($pos > 0)
{
print("FLV");
print(pack('C', 1));
print(pack('C', 1));
print(pack('N', 9));
print(pack('N', 9));
}
}
}
fclose($fp);
Any ideas ?
UPDATE
so apparently, even though the server signals it accepts range requests (with the Accept-Ranges: bytes), it does not actually do so. to see if there is another way to make the flv seekable, let's have a look at the communication between flash player and server (i use wireshark for this):
the request when starting the player is:
GET /files/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/ HTTP/1.1
Host: xxxxxx.megavideo.com
<some more headers>
<no range header>
this is answered with a response like that:
HTTP/1.0 200 OK
Server: Apache/1.3.37 (Debian GNU/Linux) PHP/4.4.7
Content-Type: video/flv
ETag: "<video-id>"
Content-Length: <length of complete video>
<some more headers>
<the flv content>
now when i seek in the flash player, another request is sent. it is almost the same as the initial one, with the following difference:
GET /files/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/8800968 HTTP/1.1
<same headers as first request>
which gets answered with a response almost the same as the initial one, with a difference only in the Content-Length header.
which lets me assume that the 8800968 at the end of the request url is the "seek range" (the byte offset in the file after seeking) we are looking for, and the second response Content-Length is the initial Content-Length (the length of the whole file) minus this range. which is the case indeed.
with this information, it should be possible to get what you want. good luck!
UPDATE END
this will only work if the server supports HTTP RANGE requests. if it does, it will return a 206 Partial Content response code with a Content-Range header and your requested range of bytes. check for these in the response to your request.

Categories