diff --git a/app/Http/Controllers/Admin/ContactsController.php b/app/Http/Controllers/Admin/ContactsController.php
index 17e4a8a7..211f9fa8 100644
--- a/app/Http/Controllers/Admin/ContactsController.php
+++ b/app/Http/Controllers/Admin/ContactsController.php
@@ -6,11 +6,11 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Contact;
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\BadResponseException;
use Illuminate\Filesystem\Filesystem;
+use Illuminate\Http\Client\RequestException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Arr;
+use Illuminate\Support\Facades\Http;
use Illuminate\View\View;
class ContactsController extends Controller
@@ -113,14 +113,13 @@ class ContactsController extends Controller
$avatar = null;
$contact = Contact::findOrFail($contactId);
if ($contact->homepage !== null && mb_strlen($contact->homepage) !== 0) {
- $client = resolve(Client::class);
try {
- $response = $client->get($contact->homepage);
- } catch (BadResponseException $e) {
+ $response = Http::throw()->get($contact->homepage);
+ } catch (RequestException $e) {
return redirect('/admin/contacts/'.$contactId.'/edit')
->with('error', 'Bad resposne from contact’s homepage');
}
- $mf2 = \Mf2\parse((string) $response->getBody(), $contact->homepage);
+ $mf2 = \Mf2\parse($response->body(), $contact->homepage);
foreach ($mf2['items'] as $microformat) {
if (Arr::get($microformat, 'type.0') === 'h-card') {
$avatarURL = Arr::get($microformat, 'properties.photo.0.value');
@@ -129,8 +128,8 @@ class ContactsController extends Controller
}
if ($avatarURL !== null) {
try {
- $avatar = $client->get($avatarURL);
- } catch (BadResponseException $e) {
+ $avatar = Http::throw()->get($avatarURL);
+ } catch (RequestException $e) {
return redirect('/admin/contacts/'.$contactId.'/edit')
->with('error', 'Unable to download avatar');
}
@@ -141,7 +140,7 @@ class ContactsController extends Controller
if ($filesystem->isDirectory($directory) === false) {
$filesystem->makeDirectory($directory);
}
- $filesystem->put($directory.'/image', $avatar->getBody());
+ $filesystem->put($directory.'/image', $avatar->body());
return view('admin.contacts.getavatarsuccess', [
'homepage' => parse_url($contact->homepage, PHP_URL_HOST),
diff --git a/app/Http/Controllers/IndieAuthController.php b/app/Http/Controllers/IndieAuthController.php
index 45b488da..eeb59770 100644
--- a/app/Http/Controllers/IndieAuthController.php
+++ b/app/Http/Controllers/IndieAuthController.php
@@ -5,13 +5,12 @@ declare(strict_types=1);
namespace App\Http\Controllers;
use App\Services\TokenService;
-use Exception;
-use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Uri;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Validator;
use Illuminate\View\View;
use Random\RandomException;
@@ -199,15 +198,13 @@ class IndieAuthController extends Controller
}
// Otherwise we need to check the redirect_uri is in the client_id's redirect_uris
- $guzzle = resolve(Client::class);
-
try {
- $clientInfo = $guzzle->get($clientId);
- } catch (Exception) {
+ $clientInfo = Http::throw()->get($clientId);
+ } catch (\Throwable) {
return false;
}
- $clientInfoParsed = \Mf2\parse($clientInfo->getBody()->getContents(), $clientId);
+ $clientInfoParsed = \Mf2\parse($clientInfo->body(), $clientId);
$redirectUris = $clientInfoParsed['rels']['redirect_uri'] ?? [];
diff --git a/app/Jobs/DownloadWebMention.php b/app/Jobs/DownloadWebMention.php
index 341c35c8..0cb073d5 100644
--- a/app/Jobs/DownloadWebMention.php
+++ b/app/Jobs/DownloadWebMention.php
@@ -4,14 +4,14 @@ declare(strict_types=1);
namespace App\Jobs;
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\FileSystem\FileSystem;
+use Illuminate\Http\Client\RequestException;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Facades\Http;
class DownloadWebMention implements ShouldQueue
{
@@ -29,15 +29,15 @@ class DownloadWebMention implements ShouldQueue
/**
* Execute the job.
*
- * @throws GuzzleException
+ * @throws RequestException
* @throws FileNotFoundException
*/
- public function handle(Client $guzzle): void
+ public function handle(): void
{
- $response = $guzzle->request('GET', $this->source);
- // 4XX and 5XX responses should get Guzzle to throw an exception,
- // Laravel should catch and retry these automatically.
- if ($response->getStatusCode() === 200) {
+ // 4XX and 5XX responses should throw so Laravel can catch and
+ // retry these automatically.
+ $response = Http::throw()->get($this->source);
+ if ($response->status() === 200) {
$filesystem = new FileSystem;
$filename = storage_path('HTML').'/'.$this->createFilenameFromURL($this->source);
// backup file first
@@ -56,7 +56,7 @@ class DownloadWebMention implements ShouldQueue
// save new HTML
$filesystem->put(
$filename,
- (string) $response->getBody()
+ $response->body()
);
// remove backup if the same
if ($filesystem->exists($filenameBackup)) {
diff --git a/app/Jobs/ProcessLike.php b/app/Jobs/ProcessLike.php
index 49302885..3ed065c1 100644
--- a/app/Jobs/ProcessLike.php
+++ b/app/Jobs/ProcessLike.php
@@ -5,14 +5,13 @@ declare(strict_types=1);
namespace App\Jobs;
use App\Models\Like;
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Arr;
+use Illuminate\Support\Facades\Http;
use Jonnybarnes\WebmentionsParser\Authorship;
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
@@ -32,13 +31,11 @@ class ProcessLike implements ShouldQueue
/**
* Execute the job.
- *
- * @throws GuzzleException
*/
- public function handle(Client $client, Authorship $authorship): int
+ public function handle(Authorship $authorship): int
{
- $response = $client->request('GET', $this->like->url);
- $mf2 = \Mf2\parse((string) $response->getBody(), $this->like->url);
+ $response = Http::throw()->get($this->like->url);
+ $mf2 = \Mf2\parse($response->body(), $this->like->url);
if (Arr::has($mf2, 'items.0.properties.content')) {
$this->like->content = $mf2['items'][0]['properties']['content'][0]['html'];
}
diff --git a/app/Jobs/ProcessWebMention.php b/app/Jobs/ProcessWebMention.php
index 6677b285..6cb276f8 100644
--- a/app/Jobs/ProcessWebMention.php
+++ b/app/Jobs/ProcessWebMention.php
@@ -7,13 +7,12 @@ namespace App\Jobs;
use App\Exceptions\RemoteContentNotFoundException;
use App\Models\Note;
use App\Models\WebMention;
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\GuzzleException;
-use GuzzleHttp\Exception\RequestException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Http\Client\ConnectionException;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Facades\Http;
use Jonnybarnes\WebmentionsParser\Exceptions\InvalidMentionException;
use Jonnybarnes\WebmentionsParser\Parser;
use Mf2;
@@ -36,18 +35,20 @@ class ProcessWebMention implements ShouldQueue
* Execute the job.
*
* @throws RemoteContentNotFoundException
- * @throws GuzzleException
* @throws InvalidMentionException
*/
- public function handle(Parser $parser, Client $guzzle): void
+ public function handle(Parser $parser): void
{
try {
- $response = $guzzle->request('GET', $this->source);
- } catch (RequestException $e) {
+ $response = Http::get($this->source);
+ } catch (ConnectionException) {
throw new RemoteContentNotFoundException;
}
- $this->saveRemoteContent((string) $response->getBody(), $this->source);
- $microformats = Mf2\parse((string) $response->getBody(), $this->source);
+ if ($response->failed()) {
+ throw new RemoteContentNotFoundException;
+ }
+ $this->saveRemoteContent($response->body(), $this->source);
+ $microformats = Mf2\parse($response->body(), $this->source);
$webmentions = WebMention::where('source', $this->source)->get();
foreach ($webmentions as $webmention) {
// check webmention still references target
diff --git a/app/Jobs/SaveProfileImage.php b/app/Jobs/SaveProfileImage.php
index 0bcbd4e7..aa7d8af7 100644
--- a/app/Jobs/SaveProfileImage.php
+++ b/app/Jobs/SaveProfileImage.php
@@ -4,13 +4,14 @@ declare(strict_types=1);
namespace App\Jobs;
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\RequestException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Http\Client\ConnectionException;
+use Illuminate\Http\Client\RequestException;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Arr;
+use Illuminate\Support\Facades\Http;
use Jonnybarnes\WebmentionsParser\Authorship;
use Jonnybarnes\WebmentionsParser\Exceptions\AuthorshipParserException;
@@ -55,12 +56,10 @@ class SaveProfileImage implements ShouldQueue
&& parse_url($photo, PHP_URL_HOST) !== 'pbs.twimg.com'
&& parse_url($photo, PHP_URL_HOST) !== 'twitter.com'
) {
- $client = resolve(Client::class);
-
try {
- $response = $client->get($photo);
- $image = $response->getBody();
- } catch (RequestException) {
+ $response = Http::throw()->get($photo);
+ $image = $response->body();
+ } catch (ConnectionException|RequestException) {
// we are opening and reading the default image so that
$default = public_path().'/assets/profile-images/default-image';
$handle = fopen($default, 'rb');
diff --git a/app/Jobs/SaveScreenshot.php b/app/Jobs/SaveScreenshot.php
index 4661ccfe..b72da7b0 100755
--- a/app/Jobs/SaveScreenshot.php
+++ b/app/Jobs/SaveScreenshot.php
@@ -5,14 +5,15 @@ declare(strict_types=1);
namespace App\Jobs;
use App\Models\Bookmark;
-use GuzzleHttp\Client;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
+use Illuminate\Http\Client\PendingRequest;
+use Illuminate\Http\Client\Response;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
-use JsonException;
class SaveScreenshot implements ShouldQueue
{
@@ -27,77 +28,68 @@ class SaveScreenshot implements ShouldQueue
/**
* Execute the job.
- *
- *
- * @throws JsonException
*/
public function handle(): void
{
- // A normal Guzzle client
- $client = resolve(Client::class);
- // A Guzzle client with a custom Middleware to retry the CloudConvert API requests
- $retryClient = resolve('RetryGuzzle');
+ $cloudConvert = Http::baseUrl('https://api.cloudconvert.com/v2')
+ ->withToken(config('services.cloudconvert.token'))
+ ->throw();
// First request that CloudConvert takes a screenshot of the URL
- $takeScreenshotJobResponse = $client->request('POST', 'https://api.cloudconvert.com/v2/capture-website', [
- 'headers' => [
- 'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
- ],
- 'json' => [
- 'url' => $this->bookmark->url,
- 'output_format' => 'png',
- 'screen_width' => 1440,
- 'screen_height' => 900,
- 'wait_until' => 'networkidle0',
- 'wait_time' => 100,
- ],
+ $takeScreenshotJobResponse = $cloudConvert->post('/capture-website', [
+ 'url' => $this->bookmark->url,
+ 'output_format' => 'png',
+ 'screen_width' => 1440,
+ 'screen_height' => 900,
+ 'wait_until' => 'networkidle0',
+ 'wait_time' => 100,
]);
- $taskId = json_decode($takeScreenshotJobResponse->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->id;
+ $taskId = $takeScreenshotJobResponse->json('data.id');
// Now wait till the status job is finished
- $screenshotJobStatusResponse = $retryClient->request('GET', 'https://api.cloudconvert.com/v2/tasks/'.$taskId, [
- 'headers' => [
- 'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
- ],
- 'query' => [
- 'include' => 'payload',
- ],
- ]);
+ $screenshotJobStatusResponse = $this->pollUntilFinished($cloudConvert, $taskId);
- $finishedCaptureId = json_decode($screenshotJobStatusResponse->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->id;
+ $finishedCaptureId = $screenshotJobStatusResponse->json('data.id');
// Now we can create a new job to request thst the screenshot is exported to a temporary URL we can download the screenshot from
- $exportImageJob = $client->request('POST', 'https://api.cloudconvert.com/v2/export/url', [
- 'headers' => [
- 'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
- ],
- 'json' => [
- 'input' => $finishedCaptureId,
- 'archive_multiple_files' => false,
- ],
+ $exportImageJob = $cloudConvert->post('/export/url', [
+ 'input' => $finishedCaptureId,
+ 'archive_multiple_files' => false,
]);
- $exportImageJobId = json_decode($exportImageJob->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->id;
+ $exportImageJobId = $exportImageJob->json('data.id');
// Again, wait till the status of this export job is finished
- $finalImageUrlResponse = $retryClient->request('GET', 'https://api.cloudconvert.com/v2/tasks/'.$exportImageJobId, [
- 'headers' => [
- 'Authorization' => 'Bearer '.config('services.cloudconvert.token'),
- ],
- 'query' => [
- 'include' => 'payload',
- ],
- ]);
+ $finalImageUrlResponse = $this->pollUntilFinished($cloudConvert, $exportImageJobId);
// Now we can download the screenshot and save it to the storage
- $finalImageUrl = json_decode($finalImageUrlResponse->getBody()->getContents(), false, 512, JSON_THROW_ON_ERROR)->data->result->files[0]->url;
+ $finalImageUrl = $finalImageUrlResponse->json('data.result.files.0.url');
- $finalImageUrlContent = $client->request('GET', $finalImageUrl);
+ $finalImageUrlContent = Http::throw()->get($finalImageUrl);
- Storage::disk('public')->put('/assets/img/bookmarks/'.$taskId.'.png', $finalImageUrlContent->getBody()->getContents());
+ Storage::disk('public')->put('/assets/img/bookmarks/'.$taskId.'.png', $finalImageUrlContent->body());
$this->bookmark->screenshot = $taskId;
$this->bookmark->save();
}
+
+ /**
+ * Poll a CloudConvert task until it reports a "finished" status.
+ */
+ private function pollUntilFinished(PendingRequest $client, string $taskId): Response
+ {
+ $attempts = 0;
+
+ do {
+ $response = $client->get('/tasks/'.$taskId, ['include' => 'payload']);
+ $finished = $response->json('data.status') === 'finished';
+ if (! $finished) {
+ $attempts++;
+ usleep(1_000_000); // 1 second, matches CloudConvert's own polling guidance
+ }
+ } while (! $finished && $attempts < 5);
+
+ return $response;
+ }
}
diff --git a/app/Jobs/SendWebMentions.php b/app/Jobs/SendWebMentions.php
index 827aaf0a..d8e962e3 100644
--- a/app/Jobs/SendWebMentions.php
+++ b/app/Jobs/SendWebMentions.php
@@ -5,8 +5,6 @@ declare(strict_types=1);
namespace App\Jobs;
use App\Models\Note;
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Header;
use GuzzleHttp\Psr7\UriResolver;
use GuzzleHttp\Psr7\Utils;
@@ -14,6 +12,7 @@ use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
use Mf2\Parser;
@@ -32,8 +31,6 @@ class SendWebMentions implements ShouldQueue
/**
* Execute the job.
- *
- * @throws GuzzleException
*/
public function handle(): void
{
@@ -43,12 +40,9 @@ class SendWebMentions implements ShouldQueue
foreach ($urls as $url) {
$endpoint = $this->discoverWebmentionEndpoint($url);
if ($endpoint !== null) {
- $guzzle = resolve(Client::class);
- $guzzle->post($endpoint, [
- 'form_params' => [
- 'source' => $this->note->uri,
- 'target' => $url,
- ],
+ Http::asForm()->post($endpoint, [
+ 'source' => $this->note->uri,
+ 'target' => $url,
]);
}
}
@@ -56,8 +50,6 @@ class SendWebMentions implements ShouldQueue
/**
* Discover if a URL has a webmention endpoint.
- *
- * @throws GuzzleException
*/
public function discoverWebmentionEndpoint(string $url): ?string
{
@@ -71,10 +63,9 @@ class SendWebMentions implements ShouldQueue
$endpoint = null;
- $guzzle = resolve(Client::class);
- $response = $guzzle->get($url);
+ $response = Http::get($url);
// check HTTP Headers for webmention endpoint
- $links = Header::parse($response->getHeader('Link'));
+ $links = Header::parse($response->header('Link'));
foreach ($links as $link) {
if (array_key_exists('rel', $link) && mb_stristr($link['rel'], 'webmention')) {
return $this->resolveUri(trim($link[0], '<>'), $url);
@@ -82,7 +73,7 @@ class SendWebMentions implements ShouldQueue
}
// failed to find a header so parse HTML
- $html = (string) $response->getBody();
+ $html = $response->body();
if ($html === '') {
return null;
diff --git a/app/Jobs/SyndicateNoteToBluesky.php b/app/Jobs/SyndicateNoteToBluesky.php
index a306801b..582ef760 100644
--- a/app/Jobs/SyndicateNoteToBluesky.php
+++ b/app/Jobs/SyndicateNoteToBluesky.php
@@ -5,13 +5,12 @@ declare(strict_types=1);
namespace App\Jobs;
use App\Models\Note;
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Facades\Http;
class SyndicateNoteToBluesky implements ShouldQueue
{
@@ -31,29 +30,18 @@ class SyndicateNoteToBluesky implements ShouldQueue
/**
* Execute the job.
- *
- * @throws GuzzleException
*/
- public function handle(Client $guzzle): void
+ public function handle(): void
{
- $response = $guzzle->request(
- 'POST',
- 'https://brid.gy/publish/webmention',
- [
- 'headers' => [
- 'Accept' => 'application/json',
- ],
- 'form_params' => [
- 'source' => $this->note->uri,
- 'target' => 'https://brid.gy/publish/bluesky',
- ],
- 'http_errors' => false,
- ]
- );
+ // no ->throw()/->retry() here — Bridgy would duplicate-publish on retry, see $tries above
+ $response = Http::acceptJson()->asForm()->post('https://brid.gy/publish/webmention', [
+ 'source' => $this->note->uri,
+ 'target' => 'https://brid.gy/publish/bluesky',
+ ]);
- $body = json_decode((string) $response->getBody(), true);
+ $body = $response->json();
- if ($response->getStatusCode() === 201) {
+ if ($response->status() === 201) {
$this->note->bluesky_url = $body['url'];
$this->note->save();
@@ -61,7 +49,7 @@ class SyndicateNoteToBluesky implements ShouldQueue
}
throw new \RuntimeException(
- 'Bridgy publish to Bluesky failed: '.($body['error'] ?? (string) $response->getBody())
+ 'Bridgy publish to Bluesky failed: '.($body['error'] ?? $response->body())
);
}
}
diff --git a/app/Jobs/SyndicateNoteToMastodon.php b/app/Jobs/SyndicateNoteToMastodon.php
index 456680e2..3f5cfcd4 100644
--- a/app/Jobs/SyndicateNoteToMastodon.php
+++ b/app/Jobs/SyndicateNoteToMastodon.php
@@ -5,13 +5,12 @@ declare(strict_types=1);
namespace App\Jobs;
use App\Models\Note;
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
+use Illuminate\Support\Facades\Http;
class SyndicateNoteToMastodon implements ShouldQueue
{
@@ -31,29 +30,18 @@ class SyndicateNoteToMastodon implements ShouldQueue
/**
* Execute the job.
- *
- * @throws GuzzleException
*/
- public function handle(Client $guzzle): void
+ public function handle(): void
{
- $response = $guzzle->request(
- 'POST',
- 'https://brid.gy/publish/webmention',
- [
- 'headers' => [
- 'Accept' => 'application/json',
- ],
- 'form_params' => [
- 'source' => $this->note->uri,
- 'target' => 'https://brid.gy/publish/mastodon',
- ],
- 'http_errors' => false,
- ]
- );
+ // no ->throw()/->retry() here — Bridgy would duplicate-publish on retry, see $tries above
+ $response = Http::acceptJson()->asForm()->post('https://brid.gy/publish/webmention', [
+ 'source' => $this->note->uri,
+ 'target' => 'https://brid.gy/publish/mastodon',
+ ]);
- $body = json_decode((string) $response->getBody(), true);
+ $body = $response->json();
- if ($response->getStatusCode() === 201) {
+ if ($response->status() === 201) {
$this->note->mastodon_url = $body['url'];
$this->note->save();
@@ -61,7 +49,7 @@ class SyndicateNoteToMastodon implements ShouldQueue
}
throw new \RuntimeException(
- 'Bridgy publish to Mastodon failed: '.($body['error'] ?? (string) $response->getBody())
+ 'Bridgy publish to Mastodon failed: '.($body['error'] ?? $response->body())
);
}
}
diff --git a/app/Models/Note.php b/app/Models/Note.php
index af7d2c3d..89ce6b63 100644
--- a/app/Models/Note.php
+++ b/app/Models/Note.php
@@ -7,7 +7,6 @@ namespace App\Models;
use App\CommonMark\Generators\MentionGenerator;
use App\CommonMark\Renderers\MentionRenderer;
use App\Observers\NoteObserver;
-use GuzzleHttp\Client;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
@@ -20,6 +19,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Http;
use Jonnybarnes\IndieWeb\Numbers;
use Laravel\Scout\Searchable;
use League\CommonMark\Environment\Environment;
@@ -363,18 +363,15 @@ class Note extends Model
$latLng = $latitude.','.$longitude;
return Cache::get($latLng, function () use ($latLng, $latitude, $longitude) {
- $guzzle = resolve(Client::class);
- $response = $guzzle->request('GET', 'https://nominatim.openstreetmap.org/reverse', [
- 'query' => [
+ $response = Http::withHeaders(['User-Agent' => 'jonnybarnes.uk, email jonny@jonnybarnes.uk'])
+ ->get('https://nominatim.openstreetmap.org/reverse', [
'format' => 'json',
'lat' => $latitude,
'lon' => $longitude,
'zoom' => 18,
'addressdetails' => 1,
- ],
- 'headers' => ['User-Agent' => 'jonnybarnes.uk via Guzzle, email jonny@jonnybarnes.uk'],
- ]);
- $json = json_decode((string) $response->getBody());
+ ]);
+ $json = $response->object();
if (isset($json->address->suburb)) {
$locality = $json->address->suburb;
if (isset($json->address->city)) {
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index ba42853e..d1e28bcf 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -2,10 +2,6 @@
namespace App\Providers;
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\ConnectException;
-use GuzzleHttp\HandlerStack;
-use GuzzleHttp\Middleware;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
@@ -82,38 +78,6 @@ class AppServiceProvider extends ServiceProvider
);
});
- // Configure Guzzle
- $this->app->bind('RetryGuzzle', function () {
- $handlerStack = HandlerStack::create();
- $handlerStack->push(Middleware::retry(
- function ($retries, $request, $response, $exception) {
- // Limit the number of retries to 5
- if ($retries >= 5) {
- return false;
- }
-
- // Retry connection exceptions
- if ($exception instanceof ConnectException) {
- return true;
- }
-
- // Retry on server errors
- if ($response && $response->getStatusCode() >= 500) {
- return true;
- }
-
- // Finally for CloudConvert, retry if status is not final
- return json_decode($response, false, 512, JSON_THROW_ON_ERROR)->data->status !== 'finished';
- },
- function () {
- // Retry after 1 second
- return 1000;
- }
- ));
-
- return new Client(['handler' => $handlerStack]);
- });
-
// Turn on Eloquent strict mode when developing
Model::shouldBeStrict(! $this->app->isProduction());
}
diff --git a/app/Services/BookmarkService.php b/app/Services/BookmarkService.php
index 25017e16..b873610f 100644
--- a/app/Services/BookmarkService.php
+++ b/app/Services/BookmarkService.php
@@ -8,10 +8,8 @@ use App\Exceptions\InternetArchiveException;
use App\Jobs\ProcessBookmark;
use App\Models\Bookmark;
use App\Models\Tag;
-use GuzzleHttp\Client;
-use GuzzleHttp\Exception\ClientException;
-use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Arr;
+use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;
class BookmarkService
@@ -55,21 +53,19 @@ class BookmarkService
* Given a URL, attempt to save it to the Internet Archive.
*
* @throws InternetArchiveException
- * @throws GuzzleException
*/
public function getArchiveLink(string $url): string
{
- $client = resolve(Client::class);
- try {
- $response = $client->request('GET', 'https://web.archive.org/save/'.$url);
- } catch (ClientException $e) {
+ $response = Http::get('https://web.archive.org/save/'.$url);
+
+ if ($response->clientError()) {
// throw an exception to be caught
throw new InternetArchiveException;
}
- if ($response->hasHeader('Content-Location')) {
- if (Str::startsWith(Arr::get($response->getHeader('Content-Location'), 0), '/web')) {
- return $response->getHeader('Content-Location')[0];
- }
+
+ $contentLocation = $response->header('Content-Location');
+ if ($contentLocation !== '' && Str::startsWith($contentLocation, '/web')) {
+ return $contentLocation;
}
// throw an exception to be caught
diff --git a/tests/Feature/Admin/ContactsTest.php b/tests/Feature/Admin/ContactsTest.php
index 9fc338eb..44320b9d 100644
--- a/tests/Feature/Admin/ContactsTest.php
+++ b/tests/Feature/Admin/ContactsTest.php
@@ -6,12 +6,9 @@ namespace Tests\Feature\Admin;
use App\Models\Contact;
use App\Models\User;
-use GuzzleHttp\Client;
-use GuzzleHttp\Handler\MockHandler;
-use GuzzleHttp\HandlerStack;
-use GuzzleHttp\Psr7\Response;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
+use Illuminate\Support\Facades\Http;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
@@ -141,14 +138,12 @@ class ContactsTest extends TestCase
HTML;
- $file = fopen(__DIR__.'/../../aaron.png', 'rb');
- $mock = new MockHandler([
- new Response(200, ['Content-Type' => 'text/html'], $html),
- new Response(200, ['Content-Type' => 'image/png'], $file),
+ $file = file_get_contents(__DIR__.'/../../aaron.png');
+ Http::fake([
+ '*' => Http::sequence()
+ ->push($html, 200, ['Content-Type' => 'text/html'])
+ ->push($file, 200, ['Content-Type' => 'image/png']),
]);
- $handler = HandlerStack::create($mock);
- $client = new Client(['handler' => $handler]);
- $this->app->instance(Client::class, $client);
$user = User::factory()->make();
$contact = Contact::factory()->create([
'homepage' => 'https://tantek.com',
@@ -165,12 +160,9 @@ class ContactsTest extends TestCase
#[Test]
public function getting_remote_avatar_fails_gracefully_with_remote_not_found(): void
{
- $mock = new MockHandler([
- new Response(404),
+ Http::fake([
+ '*' => Http::response('', 404),
]);
- $handler = HandlerStack::create($mock);
- $client = new Client(['handler' => $handler]);
- $this->app->instance(Client::class, $client);
$user = User::factory()->make();
$contact = Contact::factory()->create();
@@ -187,13 +179,11 @@ class ContactsTest extends TestCase
HTML;
- $mock = new MockHandler([
- new Response(200, ['Content-Type' => 'text/html'], $html),
- new Response(404),
+ Http::fake([
+ '*' => Http::sequence()
+ ->push($html, 200, ['Content-Type' => 'text/html'])
+ ->push('', 404),
]);
- $handler = HandlerStack::create($mock);
- $client = new Client(['handler' => $handler]);
- $this->app->instance(Client::class, $client);
$user = User::factory()->make();
$contact = Contact::factory()->create();
diff --git a/tests/Feature/IndieAuthTest.php b/tests/Feature/IndieAuthTest.php
index 534fe452..b32f4420 100644
--- a/tests/Feature/IndieAuthTest.php
+++ b/tests/Feature/IndieAuthTest.php
@@ -5,14 +5,11 @@ declare(strict_types=1);
namespace Tests\Feature;
use App\Models\User;
-use GuzzleHttp\Client;
-use GuzzleHttp\Handler\MockHandler;
-use GuzzleHttp\HandlerStack;
-use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\Psr7\UriResolver;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Http;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
@@ -269,12 +266,9 @@ class IndieAuthTest extends TestCase