Laravel server sent events not connecting properly - laravel

I'm trying to implement server sent events in a Laravel project.
I created an endpoint in the backend that looks like this:
public function stream(Request $request)
{
$response = new StreamedResponse(function () use ($request) {
$i = 0;
while (true) {
$i++;
echo 'data: ' . $i . "\n\n";
ob_flush();
flush();
if (connection_aborted()) {
break;
}
usleep(20000);
}
});
$response->headers->set('Content-Type', 'text/event-stream');
$response->headers->set('X-Accel-Buffering', 'no');
$response->headers->set('Cache-Control', 'no-cache');
return $response;
}
The frontend part:
<script>
console.log("script started");
const eventSource = new EventSource('/stream');
eventSource.onmessage = function(event) {
console.log(event);
}
</script>
When I run the project with the builtin Laravel webserver the event stream seems to be working correctly and it looks like this in the console of the browser:
The problem is that all the other requests to my backend are blocked. So I tried using nginx and apache webserver. But unfortunately I'm running into a different problem then. The connection does not establish (readyState 0) and approximately every 5 seconds a new request to the /stream endpoint is generated:
I tried updating the nginx configuration as described here:
Nginx configuration
and I used other implementations of sse:
sse in laravel
I'm running out of ideas why this is not working. For this project websockets and polling are not an alternative. Hopefully someone of you has an idea and can help me with this.

Related

Laravel "405 Method Not Allowed" on CCAvenue response return URL callback

I am adding CcAvenue gateway in laravel 5.3 on PHP 7.2, everything working fine till the payment page of CcAvenue, but after payment is done or payment canceled by the user, the return response URL is showing the following error
"Oops! An Error Occurred
The server returned a "405 Method Not Allowed".
Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused."
My return URL is this: https:// www.domainname.com/booking/cancel/cc_checkout_gateway?c=f4b7d25d6e894a44725fff59adafcf82
Code in the Routes file
use Illuminate\Support\Facades\Route;
// Booking
Route::group(['prefix'=>config('booking.booking_route_prefix')],function(){
Route::post('/addToCart','BookingController#addToCart');
Route::post('/doCheckout','BookingController#doCheckout')->name('booking.doCheckout');
Route::get('/confirm/{gateway}','BookingController#confirmPayment');
Route::get('/cancel/{gateway}','BookingController#cancelPayment');
Route::get('/{code}','BookingController#detail');
Route::get('/{code}/checkout','BookingController#checkout');
Route::get('/{code}/check-status','BookingController#checkStatusCheckout');
//ical
Route::get('/export-ical/{type}/{id}','BookingController#exportIcal')->name('booking.admin.export-ical');
//inquiry
Route::post('/addEnquiry','BookingController#addEnquiry');
});
Route::group(['prefix'=>'gateway'],function(){
Route::get('/confirm/{gateway}','NormalCheckoutController#confirmPayment')->name('gateway.confirm');
Route::get('/cancel/{gateway}','NormalCheckoutController#cancelPayment')->name('gateway.cancel');
Route::get('/info','NormalCheckoutController#showInfo')->name('gateway.info');
});
Code in BookingController.php
public function cancelPayment(Request $request, $gateway)
{
$gateways = get_payment_gateways();
if (empty($gateways[$gateway]) or !class_exists($gateways[$gateway])) {
return $this->sendError(__("Payment gateway not found"));
}
$gatewayObj = new $gateways[$gateway]($gateway);
if (!$gatewayObj->isAvailable()) {
return $this->sendError(__("Payment gateway is not available"));
}
return $gatewayObj->cancelPayment($request);
}
Code in Gateway CcCheckoutGateway.php
public function cancelPayment(Request $request)
{
$c = $request->query('c');
$booking = Booking::where('code', $c)->first();
if (!empty($booking) and in_array($booking->status, [$booking::UNPAID])) {
$payment = $booking->payment;
if ($payment) {
$payment->status = 'cancel';
$payment->logs = \GuzzleHttp\json_encode([
'customer_cancel' => 1
]);
$payment->save();
// Refund without check status
$booking->tryRefundToWallet(false);
}
return redirect($booking->getDetailUrl())->with("error", __("You cancelled the payment"));
}
if (!empty($booking)) {
return redirect($booking->getDetailUrl());
} else {
return redirect(url('/'));
}
}
After too much R&D I found that my routes code is allowing method is GET & HEAD, but Ccavenue response URL is sending the response in POST method
I have tried every possible solution changed
Route::get('/cancel/{gateway}','BookingController#cancelPayment');
to
Route::post('/cancel/{gateway}','BookingController#cancelPayment');
and
Route::any('/cancel/{gateway}','BookingController#cancelPayment');
but after that it showing error 419: page expired
Please tell me how can I resolve the above issue.

Unable to download a file (pdf) from Laravel backend via Vue.js (Axios post)

I have a multi-step form in Vue, I am posting the results once I collect all of the information, to a Laravel controller. This is an authenticated area of the site. I am using Passport. So essentially I have a Vue SPA that is the admin area of a website built within the Laravel 5.7 framework.
Vue file:
axios.post('/api/quotes/finalize', this.wizardModel)
.then(response => {
if (response.data.success) {
//
}
})
.catch(err => {
if (err.response.status == 401) {
window.location.href = '/login';
}
swal.showValidationError(
`Request failed: ${error}`
)
})
The controller gets data and makes a pdf. All of that is working. It then has three actions to look at - email the PDF, email the PDF with CC, and download the PDF.
public function finalizeQuote(Request $request)
{
$data = $request->all();
$data['phone'] = $this->formatTelephone($data['phone']);
$dateStamp = date('Ymdhis', strtotime('now'));
$fileName = 'quote-' . $dateStamp . '.pdf';
$html = View::make('quotes.quote', compact('data'))->render();
$conv = new Converter();
$conv->addPage($html)
->save('storage/' . $fileName);
if ($data['actions']['emailPDF']) {
$message = (new Proposal('storage/' . $fileName))
->onConnection('redis')
->onQueue('emails');
if ($data['actions']['carbonCopy']) {
Mail::to($data['emailAddress'])
->cc(Auth::user()->email)
->queue($message);
} else {
Mail::to($data['emailAddress'])->queue($message);
}
}
if ($data['actions']['downloadPDF']) {
return response()->download(public_path('storage/' . $fileName));
}
}
So in dev tools I see the pdf file in the response. No download occurs in the browser. I am sure I am just missing something fundamental here.
Ajax requests alone cannot invoke a download. You could have a hidden form that posts your data to your api endpoint, probably the quickest solution.
Axios has a built in way to do this easily
axios({
method: 'post',
url: '/api/quotes/finalize',
data: wizardModelFormData,
config: { headers: {'Content-Type': 'multipart/form-data' }}
})
Instead of sending json in the request, you would have to send form data. It is pretty easy to setup a form data object, and a quick google on FormData should be enough.
Another way is to do something like this. Have the endpoint return a download url for your file,
if ($data['actions']['downloadPDF']) {
return response()->json(["download_url" => "/api/download/" . $fileName]);
}
and on the client side, use window.location to set the browser location to this api endpoint that only returns files (in this case /api/download/{fileName}). The browser would then download any file returned from that location you set (if you have the headers setup correctly).
This would mean you would have to implement the /api/download/{fileName} route, but that is where you would put something like this.
return response()->download(public_path('storage/' . $fileName));
Obviously that is just a simple explanation and it doesn't need to be implemented exactly the same way, but it gets the idea across.
Now you would want to make sure that you don't put any sensitive files in storage/ and have some permissions setup.
Axios is used for XHR requests. Typically, downloading files is done through normal GET requests but here you have an XHR post request to download files.
To download files over AJAX, some folks use libraries and some create
a blob link from the response, append it to DOM and then, trigger a
click on the link.
You can refer this gist and SO post for further details.
Personally, I prefer simple non-XHR get requests for file downloads.

Laravel Echo Server can not be authenticated, got HTTP status 403

I got laravel-echo-server and Laravel 5 application with vuejs, and I'm trying to connect front end to back end via sockets.
I've managed to connect everything together via Echo.channel() method, however it will not subscribe with Echo.private()
This is what I got so far :
Once the page loads I call :
I initilise the Echo via
window.Echo = new Echo({
broadcaster: 'socket.io',
host: window.location.hostname + ':6001',
csrfToken : Laravel.csrfToken,
auth : {
headers : {
Authorization : "Bearer b6f96a6e99e90dzzzzzzz"
}
}
});
Then I create a new event via vue resourse
Vue.http.get('/api/errors/get');
This fires laravel event(new \App\Events\ErrorsEvent()); event
Which Broadcasts the event privately via
public function broadcastOn()
{
return new PrivateChannel('errors');
}
At this point laravel-echo-server responds with
Channel: private-errors
Event: App\Events\ErrorsEvent
CHANNEL private-errors
Then i try to subscribe to the channel with echo by running
Echo.private('errors')
.listen('ErrorsEvent', (e) => {
this.errors = e.errors;
console.log(e);
});
At which laravel-echo-server responds with
[14:47:31] - xxxxxxxxxxxxxx could not be authenticated to private-errors
Client can not be authenticated, got HTTP status 403
Does anybody know if I'm missing something here?
EDIT
This is my BroadcastServiceProvider
public function boot(Request $request)
{
Broadcast::routes();
/*
* Authenticate the user's personal channel...
*/
Broadcast::channel('App.User.*', function ($user, $userId) {
return (int) $user->id === (int) $userId;
});
}
Also, I've discovered that if I try to subscribe to
Echo.private('App.User.2')
.listen('ErrorsEvent', (e) => {
this.errors = e.errors;
console.log(e);
});
It connects and everything is ok, however it still refuses to connect with the errors channel
My issue was that I hadn't noticed there are two BroadcastServiceProvider::class entries in app/config.php
I had only checked the first one. Once I uncommented App\Providers\BroadcastServiceProvider::class I didn't need to specify the bearer token.
I believe the reason you couldn't connect on the errors channel in your above config is that you need to change your call in your BroadcastServiceProvider (or routes/channels.php) to
Broadcast::channel('errors', function ($user) {
return true; //replace with suitable auth check here
});

React Native Websocket Android not connecting to server

I'm trying to use React Native's built in Websocket support. I have this code:
var hostname = "192.168.X.XXX";
var port = "4567";
const webSocket = new WebSocket("ws://" + hostname + ":" + port + "/chat");
and I have this:
componentWillMount: function(){
console.log("MOUNTING CHAT COMPONENT");
webSocket.onopen = function(){
console.log("binding to chat room");
bindToChatRoom();
setTimeout(keepWebSocketOpen, 30000);
ws_connected = true;
};
webSocket.onmessage = function (msg) {
this.handleMsg(msg);
};
webSocket.onclose = function () {
console.log("WebSocket connection closed");
ws_connected = false;
};
webSocket.onerror = function (e){
//add some error handling -> DO THIS
console.log(e.message);
};
},
I'm running a local server using sparkjava, with this:
webSocket("/chat", ChatWebSocketHandler.class);
etc. I've tested the server, and I can connect to it (and send messages) both through both my web browser on my laptop AND on my phone with URL 192.168.x.xxx:4567/chat . However, when I run the react native app, I never see the console message "binding to chat room". Am I missing something? How come this websocket never opens?
Just want to note, I also tried connecting here:
ws://echo.websocket.org
which is described here:
http://www.websocket.org/echo.html
This is a known working websocket, so I know it's purely a react native issue...
Turns out that I had the line of code
const webSocket = new WebSocket("ws://echo.websocket.org");
in the wrong place. If you move it into componentWillMount, it works just fine.

Google api php redirect_uri_mismatch

When using the following composer package bitgandtter/google-api for google php api client since I'm using it in combination with laravel 4 I get the following error redirect_uri_mismatch. My code looks like this(which is located under app/lib using the PSR-0 spec):
class Google {
private $client;
private $token;
public function __construct($code)
{
$this->client = new Google_Client();
$this->client->setApplicationName("camelCaseD");
$this->client->setClientId('SOMENUMBERS.apps.googleusercontent.com');
$this->client->setClientSecret('PRECIOUS');
$this->client->setRedirectUri('http://localhost:9000/auth/google');
$this->client->authenticate($code);
}
}
My routes are:
Route::group(['prefix' => 'auth'], function()
{
Route::post('google', function()
{
$postInput = file_get_contents('php://input');
try
{
$google = new Google($postInput);
} catch (Exception $e)
{
return Redirect::to('signin')->with('error', $e->getMessage());
}
});
});
I'm using the official google plus sign in button to log the user in then passing the authorization code to my server via $.ajax().
Here's what my api console settings look like:
I got that similar error. To resolve mine, I went to google console and reset the secret. I also made sure the Authorized JavaScript origins was set to the correct address.
http:// localhost :900/auth/google
is a directory or a page?
Maybe if it is a directory, the final url is different (like http:// localhost :900/auth/google/index.php) and Google does a control between 2 string, but they are different.

Resources