Forward BinaryFileResponse between two Symfony2 apps - symfony

I have two Symfony apps (APIs) talking to each other via HTTP requests/responses using cURL PHP function. This works fine when they get small JSON responses, but the problem comes when getting and serving files. API1 (exposed to the Internet) needs to serve a file that is only accessible by API2(private, connected to API1 via VPN).
If I encode the content of the file in the first API and then pass it in the response body there is no problem, I can reconvert the file back to a stream and serve in the first API as a BinaryFileResponse. The problem comes with big files (>30MB), where the response body is huge and symfony's is not able to allocate that much memory.
Is there a way to forward or redirect a BinaryFileResponse from one API to the other, so the middle layer is invisible for the client?
These are the two pieces of code in each application:
Public API:
/**
*
* #Get("/api/login/getfile")
*/
public function testGetFilePrivate(Request $request)
{
$url = 'private/variantset/9/getfile';
$url = $this->container->getParameter('api2_url').$url;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 300000); //Set timeout in ms
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, TRUE);
$fileContent = base64_decode($data['filedata']);
$response = new Response($fileContent);
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$data['filename']
);
$response->headers->set('Content-Disposition', $disposition);
return $response;
}
Private API:
/**
* #Get("/api/private/variantset/{id}/getfile")
*/
public function getVariantsetDataFileById($id)
{
$variantset = $this->getVariantsetById($id);
if(!$variantset){
$response = array("getdata"=>"ko","error"=>"Variantset does not exists");
return new JsonResponse($response);
}
if($variantset->getCreated()){
$auth_variants_dir = $this->container->getParameter('auth_variants_path');
$file_path = $auth_variants_dir . '/' . $variantset->getDatafile() . '.gpg';
$data = [
"getdata"=>"ok",
"filename" => $variantset->getDatafile() . '.gpg',
"filedata" => base64_encode(file_get_contents($file_path))
];
$response = new Response();
$response->setContent($this->container->get('serializer')->serialize($data, 'json'));
$response->headers->set('Content-Type', 'application/json');
}else{
$response = new JsonResponse(array("getdata"=>"ko","error"=>"Variantset does not have a file yet"));
}
return $response;
}

Finally found the solution by combining the answers in
Streaming a large file using PHP
and
Manipulate a string that is 30 million characters long
Instead of using cURL PHP function, the HTTP stream wrapper is used to catch API2 response. This wrapperThe output is then parsed by using Symfony's StreamedResponse Class:
$response = new StreamedResponse(function() use($url) {
$handle = fopen($url, 'r');
while (!feof($handle)) {
$buffer = fread($handle, 1024);
echo $buffer;
flush();
}
fclose($handle);
});
$response->headers->set('Content-Type', 'application/octet-stream');
return $response;
I am still struggling on how to the content-type from the initial request, I will edit the response if I finally manage to get it properly. Any suggestions are welcomed.

Related

Passing params to c# WebMethod from PHP/SOAP

I've seen other posts about this but nothing works for me.
Parameters are always null.
Using php soap to call a c# web service (asmx) always results in null values from the service.
Please help! Driving me insane.
[WebMethod]
public string CreateContact(string param1, string param2)
{
return param1 + "-" + param2;
}
$client = new SoapClient('https://etc....?wsdl');
$params = array('param1' => 'abc','param2' => 'xyz');
$result = $client->CreateContact($params);
echo $result->CreateContactResult;
I've tried var_dump also
I don't know what you are trying to do ...
If that is PHP ... you've got multiple errors.
Try this:
<?php
$client = new SoapClient('http://www.thomas-bayer.com/axis2/services/BLZService?wsdl');
$params = array('param1' => 'abc','param2' => 'xyz');
try{
$result = $client->CreateContact($params);
echo $result->CreateContactResult;
} catch (Exception $e) {
echo $e->getMessage();
}
?>
Should return you the error:
Function ("CreateContact") is not a valid method for this service
Valid methods for SoapClient you can find here:
http://php.net/manual/en/class.soapclient.php
Regards,
Ɓukasz Konkol

how to get HTTPS headers from webhook requester

I have a webhook to receive updates that I am trying to configure. I need to get the bearer token from the header, but I am not able to retrieve it. Can someone shed some light on this issue? I am stumped!
receiving url is https://example.com/receive
$data = file_get_contents("php://input",true);
$events= json_decode($data, true);
If you're looking for an OAuth bearer token these are usually transferred in the request HTTP Authorization header. In PHP these can be a little tricky to read since different web servers have different approaches to reading the Authorization header.
There's a good example of how to read a bearer token in this answer. Copied here for convenience:
<?PHP
/**
* Get hearder Authorization
* */
function getAuthorizationHeader() {
$headers = null;
if (isset($_SERVER['Authorization'])) {
$headers = trim($_SERVER["Authorization"]);
} else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI
$headers = trim($_SERVER["HTTP_AUTHORIZATION"]);
} elseif (function_exists('apache_request_headers')) {
$requestHeaders = apache_request_headers();
// Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
$requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
if (isset($requestHeaders['Authorization'])) {
$headers = trim($requestHeaders['Authorization']);
}
}
return $headers;
}
/**
* get access token from header
* */
function getBearerToken() {
$headers = getAuthorizationHeader();
// HEADER: Get the access token from the header
if (!empty($headers)) {
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
return $matches[1];
}
}
return null;
}
?>

Controller unit test in slim3

At the outset, I would like to say - I'm new in unit testing in PHP (phpunit).
In my new project (slim3 framework) I would like to test my controllers for example LoginController.
My idea is (in unit test method)
Create instance of LoginController
Mock some services in controller (DI)
Execute method which is response for request (in my controllers method __invoke)
My problem is about parameters for __invoke method.
In Slim3 callable method for request has two first params:
RequestInterface $request and ResponseInterface $response
How can I create this parameters in my unit test class? I was searching for some examples for this issue but without success.
Any suggestions?
I've found some code in Slim3 tests to mock request:
protected function requestFactory()
{
$uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123');
$headers = new Headers();
$cookies = array(
'user' => 'john',
'id' => '123',
);
$env = Slim\Http\Environment::mock();
$serverParams = $env->all();
$body = new Body(fopen('php://temp', 'r+'));
$request = new Request('GET', $uri, $headers, $cookies, $serverParams, $body);
return $request;
}
But I'm not sure that is good way.
Thanks for any help
I wrote up one solution here: https://akrabat.com/testing-slim-framework-actions/
I use Environment::mock() to create a $request and then I can run the action. Making each route callable a class where all dependencies are injected into the constructor makes this all much easier too.
Essentially, a test looks like this:
class EchoActionTest extends \PHPUnit_Framework_TestCase
{
public function testGetRequestReturnsEcho()
{
// instantiate action
$action = new \App\Action\EchoAction();
// We need a request and response object to invoke the action
$environment = \Slim\Http\Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
'QUERY_STRING'=>'foo=bar']
);
$request = \Slim\Http\Request::createFromEnvironment($environment);
$response = new \Slim\Http\Response();
// run the controller action and test it
$response = $action($request, $response, []);
$this->assertSame((string)$response->getBody(), '{"foo":"bar"}');
}
}

Symfony2 Doctrine - Flushing in kernel.response listener flushs bad data

In order to do some logging for my Symfony2 app, I created a service that logs any connection, here is the method called on kernel.response :
public function log(FilterResponseEvent $event)
{
$log = new Log();
$request = $event->getRequest();
$response = $event->getResponse();
//fill the Log entity with stuff from request & response data
$manager = $this->container->get('doctrine.orm.entity_manager');
$manager->persist($log);
$manager->flush();
}
All of this seems fine, however when I execute a test like this one (patch with empty data to trigger a failure):
$this->client->request(
'PATCH',
'/users/testificate',
array(
'firstName' => '',
)
);
Which calls this action :
protected function processForm($item, $method = 'PATCH')
{
$form = $this->createForm(new $this->form(), $item, array('method' => $method));
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
$response = new Response();
// Set the `Location` header only when creating new resources
if ($method == 'POST') {
$response->setStatusCode(201);
$response->headers->set('Location',
$this->generateUrl(
'get_' . strtolower($class), array('slug' => $item->getId()),
true // absolute
)
);
}
else {
$response->setStatusCode(204);
}
$this->em->flush();
return $response;
}
$this->em->detach($item);
return RestView::create($form, 400);
}
Although the test fails, the entity is patched, and of course it must not.
After some search what I've learnt is:
The parameters enter the form validator
The validation fails, thus returning a 400 http code without flushing the entity
However during the validation process, the entity gets hydrated with the invalid data
When the service is called on kernel.response, the $manager->flush(); flush all the data... including the bad data provided by the PATCH test.
What I've tried thus far:
1) Do a $manager->clear(); before $manager->persist(); ... doesn't change anything
2) Do a $manager->detach($item); if the form validation failed... doesn't change anything
Thanks !
I recently stumbled across problems with flushing in kernel.response when upgrading from Doctrine 2.3.4 to the latest 2.4 branch. Try flusing the log entities from kernel.terminate. Leave any modifications to the Response in kernel.response.

Adding extra new parameter to header as response in symfony2

I want to send response to the client which should include some details in header which is common, let say userID and others datails in the body. How to add such new parameters to header of response,
I tried,
public function postAPIAction()
{
$jsonData = $this->getRequest()->getContent();
$decodePostRequest = json_decode($jsonData, true);
// processing is involved........
$uniqueKey=$this->generateUniqueKey();
$response = new Response();
$response->headers->add(array('userId' => $uniqueKey));
return new Response(json_encode(array('errorcode' => '1'), true));
}
which is not working.
You have to return the response you've set the headers on instead of creating a new one in the return statement.
You're creating a new response in your return.
You should use the response you created before.
$response = new Response();
$response->headers->add(array('userId' => $uniqueKey));
$response->setContent(...);
return $response;

Resources