0

I am trying to execute a python script from a Laravel 8 app. The python script uses the Laravel app's REST APIs so it has to authenticate.

The python script runs great on it's own if its run at the command line (dev platform is Windows 10) but if run via shell_exec() or exec() or symfony/process() it takes down the Laravel app to the point where I need to restart the app on a different port or reboot with no errors in console, laravel log.

If I take out the 'requests.post()' from the python script and replace it with print("Hello World") it works fine with the shell_exec()

Question:Does anyone see what may be happening? This seems like a permissions issue but I'm not sure.

Footnote: I tried Symfony but was getting an error that many others reported "failed to get random numbers to initialize Python runtime" This seems like a common problem on windows dev boxes. Figured this problem out but the result is the same. For anyone that sees this python initialization error in the future: On a windows build environment you need to add the environment variable SystemRoot as follows: $process = new Process([$python, $process_path, $argument1, $argument2], null, ['SystemRoot' => 'C:\WINDOWS']);

Controller

    public function startprocess(Request $request)
    {

        $python = "C:/Users/dev/AppData/Local/Programs/Python/Python38/python.exe";
        $process_path = "C:/Apache24/htdocs/project/resources/python/lambda/test.py";
        $complete_path = $python." ".$process_path;
        $output = [];
        $return_var = "";

//        exec($complete_path, $output, $return_var);
        $process = new Process([$python, $process_path], null, ['SystemRoot' => 'C:\WINDOWS']);
        $process->run();

        return view('admin.scheduledtasks_data', compact('output', 'return_var', 'complete_path'))->render();
    }

Python code

import sys
import requests
from requests.exceptions import HTTPError
import re

url_login = 'http://127.0.0.1:8089/api/apilogin'

data = {
    'email': 'myemail@email.com',
    'password': 'password'}

returned_data = requests.post(url_login, data=data)  #<<THIS TAKES DOWN THE SERVER

Here is what the API resolves to

   public function APIlogin(Request $request)
    {

        $user = User::where('email', $request->email)->first();

        if (!$user || !Hash::check($request->password, $user->password)) {
            return response([
                'message' => ['Please submit a valid email address and password combination.']
            ], 404);
        }

        $token = $user->createToken('user-token')->plainTextToken;

        $response = [
            'user' => $user,
            'token' => $token
        ];

        return response($response, 201);
    }

Here is what a good request looks like from the command line

send: b'POST /api/apilogin HTTP/1.1 
Host: 127.0.0.1:8098 
User-Agent: python-requests/2.23.0 
Accept-Encoding: gzip, deflate 
Accept: */* 
Connection: keep-alive 
Content-Length: 48 
Content-Type: application/x-www-form-urlencoded'
send: b'email= (deleted) &password= (deleted)'
reply: 'HTTP/1.1 201 Created'
header: Host: 127.0.0.1:8098
header: Date: Fri, 13 Nov 2020 19:07:10 GMT
header: Connection: close
header: X-Powered-By: PHP/7.4.2
header: Cache-Control: no-cache, private
header: Date: Fri, 13 Nov 2020 19:07:10 GMT
header: Content-Type: application/json
header: X-RateLimit-Limit: 60
header: X-RateLimit-Remaining: 59

here is the response when I call it from laravel app using requests.post(url_login, data=data, timeout=3) with the 'timeout' added but the login never happens. Without the timeout the request never returns and server goes down

HTTP/1.1 200 OK
Host: 127.0.0.1:8102
Date: Fri, 13 Nov 2020 23:05:38 GMT
Connection: close
X-Powered-By: PHP/7.4.2
Content-Type: text/html; charset=UTF-8
Cache-Control: no-cache, private
Date: Fri, 13 Nov 2020 23:05:38 GMT
Set-Cookie: XSRF-TOKEN= (deleted); 
    expires=Sat, 14-Nov-2020 01:05:38 GMT; 
    Max-Age=7200; 
    path=/
Set-Cookie: icollect_session=(deleted); 
    expires=Sat, 14-Nov-2020 01:05:38 GMT; 
    Max-Age=7200; 
    path=/; 
    httponly
<td>
<textarea class="form-control" id="FormControlTextarea" rows="10" cols="300">
send: b&#039;POST /api/apilogin HTTP/1.1\r\n
Host: 127.0.0.1:8102\r\n
User-Agent: python-requests/2.23.0\r\n
Accept-Encoding: gzip, deflate\r\n
Accept: */*\r\n
Connection: keep-alive\r\n
Content-Length: 48\r\n
Content-Type: application/x-www-form-urlencoded\r\n\r\n&#039;
send: b&#039;email=(deleted)&amp;password=(deleted);
</textarea>
</td>

update: The post never hits api.php so the problem must be something related to the python script not being able to post back to the app it was called from... but why?

update: I am using JS ajax to call the PHP controller that spawns the python script. If I use the PHP symfony process() call in the controller to call the python script that contains the request.post() the ajax eventually errors out after 1 minute with a 500 error once Symfony throws a timing error and then the python script continues and works. Now just to understand why and how to redesign this...

{message: "Maximum execution time of 60 seconds exceeded",…}
exception: "Symfony\Component\ErrorHandler\Error\FatalError"
file: "C:\Apache24\htdocs\laravel\vendor\symfony\process\Pipes\WindowsPipes.php"
line: 145
message: "Maximum execution time of 60 seconds exceeded"
trace: []
scottsuhy
  • 315
  • 4
  • 13

1 Answers1

0

Got rid of Symfony process() and changed to this. What was happening is the laravel app was blocking the request.post from completing hence the lockup of the server...

Found here--thank you @LucaM

function execInBackground($cmd) {
    if (substr(php_uname(), 0, 7) == "Windows"){
        pclose(popen("start /B ". $cmd, "r")); 
    }
    else {
        exec($cmd . " > /dev/null &");  
    }
} 
scottsuhy
  • 315
  • 4
  • 13