5
votes

I'm using the Google PHP API Client v2.0 in an app I'm running on Google App Engine on localhost. When I publish my app to Google App Engine production, I haven't gotten the error, but it's intermittent so it's hard to know if I'm just getting lucky in production.

My Google API client is attempting to write a file to my Google Drive. This code has been working well for a couple of months, but it suddenly stopped working this morning. I checked the Google API status and they aren't reporting any outages. When I execute any of the API calls, I get this vague error response:

Fatal error: fopen(): Invalid response from API server. in /Users/me/googleappengine/stuff-otherstuff-111111/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php on line 312

The file does get created on Google Drive, so my API call is getting through, but whatever response is coming back to my script is causing a fatal crash.

For a few minutes it started working again, now it is crashing again. I can't really find any solutions to the error Invalid response from API server anywhere... and it is intermittent. It doesn't appear to be something to do with my code, but Google says it's not having any kind of outage.

What am I missing? How can I fix this?

<?php
include_once('code-base.php');
require_once('vendor/autoload.php');

$client = new Google_Client();
$client->setApplicationName(getSetting('APP_NAME'));
$client->setAuthConfig(json_decode(getSetting('localhost_google_client_secret'), true));
$client->setAccessType("offline");
$client->setScopes(array(Google_Service_Drive::DRIVE));

$client->setHttpClient(new GuzzleHttp\Client(['verify'=>'ca-bundle.crt']));

$accesstoken = json_decode(getSetting('localhost_google_oauth_token'), true);

$client->setAccessToken($accesstoken);

// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
    print "access token is expired\n";

    $refreshtoken = getSetting('localhost_google_refresh_token');

    print "refresh token: $refreshtoken\n";

    $client->refreshToken($refreshtoken);
    $newtokenjson = json_encode($client->getAccessToken());

    print "new token: $newtokenjson\n";

    printf("before: %s\n\nafter: %s\n\n", $accessToken, $newtokenjson);
    //file_put_contents($credentialsPath, $newtokenjson);
    updateSetting('localhost_google_oauth_token', $newtokenjson);
}

print "after checking access token expired\n";

print "before drive service\n";
$driveservice = new Google_Service_Drive($client);
print "after drive service\n";

$parentfolderid = getSetting('GDRIVE_EXPORT_DUMP');
$title = "00005 test gdrive.csv";
$filetype = 'text/csv';
$contents = "The quick brown fox jumped over the lazy dog.";

$file = new Google_Service_Drive_DriveFile();
$file->setName($title);
$file->setDescription($title);
$file->setMimeType($filetype);

$file->setParents(array($parentfolderid));

try {

    if ($contents == null) {
        print "contents of file are null, creating empty file\n";
        $createdFile = $driveservice->files->create($file);
        print "after creating empty file\n";

    } else {
        $contentsarray = array('data' => $contents, 'mimeType' => $filetype, 'uploadType' => 'media');
        if ($options != null) {
            foreach($options as $key => $value) {
                $contentsarray[$key] = $value;
            }
        }
        print "file contents: ".var_export($contentsarray, true)."\n";
        $createdFile = $driveservice->files->create($file, $contentsarray);
        print "after create file with contents\n";
    }

    return $createdFile;
} catch (Exception $e) {
    print "EXCEPTION: An error occurred: " . $e->getMessage()."\n";
}

EDIT: My code seems to be failing consistently now with the Invalid response from API server message. It only fails at a point where the code communicates with Google. Those two places are when I call refreshToken() (which only happens rarely) and the other is when I call create().

I had my friend run this exact code on his machine with the same setup (as far as we can tell... same code, same libs, same oauth token, etc.) and it works for him.

Notes: The getSetting() function the above code just pulls some strings from my database that I use for configuration purposes. Also, the setHttpClient() call is needed because this is running inside the Google App Engine which runs on PHP 5.5 which has a screwed up CA Bundle and it requires me to provide a proper one.

What could be causing it to fail for me every time but work for my friend?

1
you will need to post the code that is causing an error. - DaImTo
It isn't any particular piece of code. It happens intermittently and can happen on any line of code that makes an api call to Google. One time the error happens on the line that executes a Remote Apps Scripts call, another time it happens when I'm making a Google Drive call to fetch a list of files, another time it'll happen on a Google Drive call to create a file. But then all of those same calls will work the next 20 times I run the code. - Ryan Skalla
There's not enough information about what is causing the error in the question. I'm assuming this is the PHP API client you're using. Based on the fopen error in the StreamHandler code, I suspect the error occurs with either creating or reading a file from Drive. Please provide a minimal code sample that can reproduce this error and we'd be happy to look into it. - Nicholas
@Nicholas I updated the code block to add a minimum example. - Ryan Skalla
I could not reproduce this issue as I do not have access to the your exact cert bundle and other included code. Nevertheless, given that this exact code is successful on your peer's device, this would suggest that the issue lies either with the certificate bundle, the execution environment, operating system or maybe even a proxy. How are these different between your two systems? - Nicholas

1 Answers

-1
votes

Before you can answer "what is causing this error" you'd first need to answer "what is the error". Usually, transient network errors are normal and would not force the app to crash with a fatal error, even if they happened constantly (eg. in the event of an outage or in your case, some local problem).

Guzzle certainly doesn't treat errors as fatal, as it sets its default HTTP context to ignore_errors => true (and returns the response anyway). The google-api-php-client does not change the default behavior of ignore_errors, nor does google-api-php-client-services, nor the development server, nor the PHP SDK. The text Invalid response from API server does not appear anywhere in the source code for any of the aforementioned projects and isn't a documented error response from any of the APIs.

So given that, it would suggest the source of Invalid response from API server is somewhere within the application code itself. My guess is that there is a similar type of catch-all exception handling block in the application code as you have above which is swallowing any kind of non-success response, and bailing out with an opaque error message as a fatal.

The best answer I can give is to look for source of the error message in your code, then tell it to log the details instead of obscure them, which may reveal what the underlying problem is and how to fix it.