Skip to content

Commit

Permalink
Added basic functionality to show HTTP request info, should close bug…
Browse files Browse the repository at this point in the history
  • Loading branch information
chorry committed Dec 14, 2022
1 parent eaf6569 commit 3a0c4cc
Show file tree
Hide file tree
Showing 21 changed files with 480 additions and 4 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ It runs without installation on multiple platforms via docker and supports [symf
- [Monolog server](#4-monolog-server)
- [Inspector](#5-compatible-with-inspector-reports)
- [Spatie Ray debug tool](#6-spatie-ray-debug-tool)
- [HTTP Requests dump server](#7-http-requests-dump-server)
2. [Installation](#installation)
- [Docker image](#docker-image)
3. [Configuration](#configuration)
Expand Down Expand Up @@ -213,6 +214,9 @@ Json, Xml, Carbon, File, Table, Image, Html, Text, Notifications, Phpinfo, Excep
Show jobs, Show cache, Model, Show views, Markdown, Collections, Env, Response, Request, Ban, Charles, Remove, Hide/Show events,
Application log, Show Http client requests, Mailable

## 7. HTTP Requests dump server
Buggregator can receive HTTP requests and store them for inspection.

### Laravel settings

Please make sure `ray.php` config published to the project root.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public function handle($event): void
'POST' => 'blue',
'PUT' => 'yellow',
'DELETE' => 'red',
'HEAD' => 'white',
},
'responseColor' => match (true) {
$statusCode >= 500 => 'red',
Expand Down
2 changes: 2 additions & 0 deletions app/Modules/Events/Application/Resources/EventResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

class EventResource extends JsonResource
{
public bool $preserveKeys = true;

public function toArray($request)
{
return [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

namespace Modules\HttpDump\Interfaces\Http\Controllers;

use App\Commands\FindProjectByName;
use App\Commands\HandleReceivedEvent;
use App\Contracts\Command\CommandBus;
use App\Contracts\Query\QueryBus;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Interfaces\Http\Controllers\Controller;
use Spatie\RouteAttributes\Attributes\Any;
use Spatie\RouteAttributes\Attributes\Where;
use Symfony\Bridge\PsrHttpMessage\Factory\UploadedFile;
use Symfony\Component\Console\Output\ConsoleOutput;

class StoreEventAction extends Controller
{
#[Any(uri: 'httpdump/store/{extraData?}', name: 'httpdump.event.store')]
#[Where('extraData', '(.*)')]
public function __invoke(
Request $request,
CommandBus $commands,
QueryBus $queryBus,
ConsoleOutput $output,
): void {
$project = $queryBus->ask(new FindProjectByName('default'));
$projectId = $project->getId();

$data = $this->prepareRequest($request);
$commands->dispatch(
new HandleReceivedEvent(
(int) $projectId,
'httpdump',
$data,
true
)
);
}

private function prepareRequest(Request $request)
{
return [
'received_at' => now()->toDateTimeString(),
'request' => [
'method' => $request->getMethod(),
'uri' => $request->getRequestUri(),
'headers' => $request->headers->all(),
'body' => $request->getContent(),
'query' => $request->query->all(),
'post' => $this->getPostData($request),
],
];
}

private function getPostData(Request $request): array
{
$contentType = current(Arr::get($request->headers->all(), 'content-type', [])) ?: 'no content type';

if ($contentType === 'application/x-www-form-urlencoded') {
return $request->request->all();
}

if ($contentType === 'application/json') {
return json_decode($request->getContent(), true);
}

if (str_starts_with($contentType, 'multipart/form-data') && $request->allFiles() !== []) {
return array_map(function (UploadedFile $file) {
return [
'originalName' => $file->getClientOriginalName(),
'mime' => $file->getClientMimeType(),
'size' => $file->getSize(),
];
}, collect($request->files->all())->flatten()->toArray());
}

return [];
}
}
22 changes: 22 additions & 0 deletions app/Modules/HttpDump/ServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Modules\HttpDump;

use Modules\HttpDump\Contracts\EventHandler as EventHandlerContract;

final class ServiceProvider extends \Illuminate\Support\ServiceProvider
{
public function register()
{
$this->app->bind(EventHandlerContract::class, function () {
return new EventHandler($this->app, []);
});
}

public function boot()
{
$this->loadViewsFrom(__DIR__.'/resources/views', 'httpdump');
}
}
1 change: 1 addition & 0 deletions config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
Modules\Ray\ServiceProvider::class,
Modules\Sentry\ServiceProvider::class,
Modules\User\ServiceProvider::class,
Modules\HttpDump\ServiceProvider::class,

/*
* Application Service Providers...
Expand Down
6 changes: 6 additions & 0 deletions config/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
],
],
],
'httpdump' => [
'http' => [
'index' => 'HttpDump/Index',
'show' => 'HttpDump/Show',
],
],
'sentry' => [
'http' => [
'index' => 'Sentry/Index',
Expand Down
22 changes: 22 additions & 0 deletions resources/js/Components/HttpDump/Event.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<template>
<!-- Используется на первой вкладке со всеми событиями -->
<Event :event="event" class="event--httpdump">
<Link as="div" :href="event.route.show" class="link flex-grow">
<p>{{ event.event.request.method }} {{ event.event.request.uri }}</p>
</Link>
</Event>
</template>

<script>
import Event from "../Event";
import {Link} from '@inertiajs/inertia-vue3'
export default {
components: {
Link, Event
},
props: {
event: Object
}
}
</script>
19 changes: 19 additions & 0 deletions resources/js/Components/HttpDump/List/Item.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<div class="relative">
<Link as="div" :href="event.route.show" class="link flex-grow">
<p>{{ event.event.request.method }} {{ event.event.request.uri }}</p>
<span class="text-xs"> {{ event.date }}</span>
</Link>
</div>
</template>

<script>
import {Link} from '@inertiajs/inertia-vue3'
export default {
components: { Link },
props: {
event: Object
}
}
</script>
24 changes: 24 additions & 0 deletions resources/js/Components/HttpDump/Show/Headers.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<section class="py-5 px-4 md:px-6 lg:px-8 border-b">
<h1 class="mb-1 text-lg bold">Headers</h1>
<Table>
<TableRow :title="title" v-for="(value, title) in event.request.headers">
{{ value[0] || value}}
</TableRow>
</Table>
</section>
</template>

<script>
import Table from "@/Components/UI/Table";
import TableRow from "@/Components/UI/TableRow";
export default {
components: {
Table, TableRow
},
props: {
event: Object
}
}
</script>
24 changes: 24 additions & 0 deletions resources/js/Components/HttpDump/Show/PostData.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<section class="py-5 px-4 md:px-6 lg:px-8 border-b">
<h1 class="mb-1 text-lg bold">POST Data</h1>
<Table>
<TableRow :title="title" v-for="(value, title) in event.request.post">
{{ value }}
</TableRow>
</Table>
</section>
</template>

<script>
import Table from "@/Components/UI/Table";
import TableRow from "@/Components/UI/TableRow";
export default {
components: {
Table, TableRow
},
props: {
event: Object
}
}
</script>
24 changes: 24 additions & 0 deletions resources/js/Components/HttpDump/Show/QueryParameters.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<section class="py-5 px-4 md:px-6 lg:px-8 border-b">
<h1 class="mb-1 text-lg bold">Query Parameters</h1>
<Table>
<TableRow :title="title" v-for="(value, title) in event.request.query">
{{ value }}
</TableRow>
</Table>
</section>
</template>

<script>
import Table from "@/Components/UI/Table";
import TableRow from "@/Components/UI/TableRow";
export default {
components: {
Table, TableRow
},
props: {
event: Object
}
}
</script>
21 changes: 21 additions & 0 deletions resources/js/Components/HttpDump/Show/RequestBody.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<section class="py-5 px-4 md:px-6 lg:px-8 border-b">
<h1 class="mb-1 text-lg bold">Request Body</h1>
<p>
<code>
{{ event.request.body }}
</code>
</p>
</section>
</template>

<script>
export default {
components: {
},
props: {
event: Object
}
}
</script>
7 changes: 7 additions & 0 deletions resources/js/Components/Layout/Sidebar/Left.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ export default {
icon: '<svg class="fill-current" xmlns="http:https://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" clip-rule="evenodd" d="M19 3H5a3 3 0 0 0-3 3v12a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3V6a3 3 0 0 0-3-3ZM5 1a5 5 0 0 0-5 5v12a5 5 0 0 0 5 5h14a5 5 0 0 0 5-5V6a5 5 0 0 0-5-5H5Z" /><path fill-rule="evenodd" clip-rule="evenodd" d="M7 12c0-.6.4-1 1-1h10a1 1 0 1 1 0 2H8a1 1 0 0 1-1-1Z" /><path fill-rule="evenodd" clip-rule="evenodd" d="M5 8c0-.6.4-1 1-1h10a1 1 0 1 1 0 2H6a1 1 0 0 1-1-1ZM5 16c0-.6.4-1 1-1h10a1 1 0 1 1 0 2H6a1 1 0 0 1-1-1Z"/></svg>',
supIcon: computed(() => this.unReadEvents.filter(i => i === 'inspector').length > 0),
},
{
href: route('events.type', 'httpdump'),
title: 'HTTP Dump',
state: (url) => this.$page.url.startsWith(url),
icon: '<svg viewBox="0 0 24 24" fill="none" xmlns="http:https://www.w3.org/2000/svg"><path d="M14.8284 12L16.2426 13.4142L19.071 10.5858C20.6331 9.02365 20.6331 6.49099 19.071 4.9289C17.509 3.3668 14.9763 3.3668 13.4142 4.9289L10.5858 7.75732L12 9.17154L14.8284 6.34311C15.6095 5.56206 16.8758 5.56206 17.6568 6.34311C18.4379 7.12416 18.4379 8.39049 17.6568 9.17154L14.8284 12Z" fill="currentColor" /><path d="M12 14.8285L13.4142 16.2427L10.5858 19.0711C9.02372 20.6332 6.49106 20.6332 4.92896 19.0711C3.36686 17.509 3.36686 14.9764 4.92896 13.4143L7.75739 10.5858L9.1716 12L6.34317 14.8285C5.56212 15.6095 5.56212 16.8758 6.34317 17.6569C7.12422 18.4379 8.39055 18.4379 9.1716 17.6569L12 14.8285Z" fill="currentColor"/><path d="M14.8285 10.5857C15.219 10.1952 15.219 9.56199 14.8285 9.17147C14.4379 8.78094 13.8048 8.78094 13.4142 9.17147L9.1716 13.4141C8.78107 13.8046 8.78107 14.4378 9.1716 14.8283C9.56212 15.2188 10.1953 15.2188 10.5858 14.8283L14.8285 10.5857Z" fill="currentColor"/></svg>',
supIcon: computed(() => this.unReadEvents.filter(i => i === 'httpdump').length > 0),
},
// {
// href: route('performance'),
// title: 'Performance',
Expand Down
9 changes: 8 additions & 1 deletion resources/js/EventFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import SmtpEvent from "./Smtp/event";
import VarDumpEvent from "./VarDump/event";
import InspectorEvent from "./Inspector/event";
import SentryTransactionEvent from "./SentryTransaction/event";
import HttpDumpEvent from "./HttpDump/event";
import {store} from "./store";

const eventTypes = {
Expand All @@ -21,7 +22,8 @@ const eventTypes = {
smtp: json => new SmtpEvent(json.payload, json.uuid, json.timestamp),
inspector: json => new InspectorEvent(json.payload, json.uuid, json.timestamp),
sentrytransaction: json => new SentryTransactionEvent(json.payload, json.uuid, json.timestamp, json.projectId, json.transactionId),
'var-dump': json => new VarDumpEvent(json.payload, json.uuid, json.timestamp)
'var-dump': json => new VarDumpEvent(json.payload, json.uuid, json.timestamp),
httpdump: json => new HttpDumpEvent(json.payload, json.uuid, json.timestamp)
}

export default {
Expand All @@ -48,6 +50,8 @@ export default {
store.commit('sentryTransaction/pushEvent', event)
} else if (event instanceof InspectorEvent) {
store.commit('inspector/pushEvent', event)
} else if (event instanceof HttpDumpEvent) {
store.commit('httpdump/pushEvent', event)
}
store.commit('pushUnreadEvent', event.app)
store.commit('pushEvent', event)
Expand All @@ -66,6 +70,9 @@ export default {
if (e.payload.type === 'inspector') {
store.commit('inspector/clearEvents')
}
if (e.payload.type === 'httpdump') {
store.commit('httpdump/clearEvents')
}

store.commit('clearEvents', e.payload.type)
})
Expand Down
15 changes: 15 additions & 0 deletions resources/js/HttpDump/event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Event} from "@/Event"

export default class extends Event{
labels = []
app = 'httpdump'

constructor(event, id, timestamp) {
super(event, id, timestamp)
this.labels.push(this.event.request.method)
}

get type() {
return 'httpdump'
}
}

0 comments on commit 3a0c4cc

Please sign in to comment.