Skip to content

Commit

Permalink
feat: Support to view network connections and kill them
Browse files Browse the repository at this point in the history
  • Loading branch information
ci010 committed Mar 29, 2024
1 parent 0aa3a61 commit e884827
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 60 deletions.
157 changes: 98 additions & 59 deletions xmcl-keystone-ui/src/views/AppTaskDialogTaskView.vue
Expand Up @@ -16,72 +16,109 @@
>
<v-icon>close</v-icon>
</v-btn>
<template #extension>
<v-tabs
v-model="tab"
centered
>
<v-tab>{{ t('task.name', 2) }}</v-tab>
<v-tab>{{ t('task.connections' ) }}</v-tab>
</v-tabs>
</template>
</v-toolbar>

<v-card-text class="max-h-[400px] overflow-auto">
<div
v-for="[o, s] of Object.entries(stat)"
:key="o"
>
<div>
{{ o }}
</div>
<div>
{{ s }}
</div>
</div>
<div
v-if="visible.length === 0"
class="mt-4"
>
{{ t('task.empty') }}
</div>
<v-treeview
v-model="data.tree"
hoverable
transition
:open="data.opened"
:items="visible"
activatable
item-key="id"
item-children="children"
>
<template #append="{ item }">
<TaskDialogNodeStatus
:item="item"
:show-number="data.hovered[item.id]"
@pause="pause(item)"
@resume="resume(item)"
@cancel="cancel(item)"
/>
</template>

<template #label="{ item }">
<v-tabs-items v-model="tab">
<v-tab-item
:key="0"
>
<div
style="padding: 5px 0px;"
@click="onTaskClick($event, item)"
@mouseenter.prevent="data.hovered[item.id] = true"
@mouseleave.prevent="data.hovered[item.id] = false"
v-if="visible.length === 0"
class="mt-4"
>
{{ t('task.empty') }}
</div>
<v-treeview
v-model="data.tree"
hoverable
transition
:open="data.opened"
:items="visible"
activatable
item-key="id"
item-children="children"
>
<span style="max-width: 100px;">
{{ tTask(item.path, item.param) }}
<template #append="{ item }">
<TaskDialogNodeStatus
:item="item"
:show-number="data.hovered[item.id]"
@pause="pause(item)"
@resume="resume(item)"
@cancel="cancel(item)"
/>
</template>

<template #label="{ item }">
<div
style="padding: 5px 0px;"
@click="onTaskClick($event, item)"
@mouseenter.prevent="data.hovered[item.id] = true"
@mouseleave.prevent="data.hovered[item.id] = false"
>
<span style="max-width: 100px;">
{{ tTask(item.path, item.param) }}

<span v-if="item.isGrouped">
({{ item.groupedCount }} similar is collapsed)
</span>
<span v-if="item.isGrouped">
({{ item.groupedCount }} similar is collapsed)
</span>

</span>
<div style="color: grey; font-size: 12px; font-style: italic; max-width: 400px;">
{{ item.time.toLocaleString() }}
</div>
<div
style="color: grey; font-size: 12px; font-style: italic; max-width: 400px; word-wrap: normal; overflow-wrap: break-word; white-space: normal;"
</span>
<div style="color: grey; font-size: 12px; font-style: italic; max-width: 400px;">
{{ item.time.toLocaleString() }}
</div>
<div
style="color: grey; font-size: 12px; font-style: italic; max-width: 400px; word-wrap: normal; overflow-wrap: break-word; white-space: normal;"
>
<AppTaskDialogTaskViewMessage :value="item.message ? item.message : item.from || item.to || ''" />
</div>
</div>
</template>
</v-treeview>
</v-tab-item>
<v-tab-item
:key="1"
>
<v-list
dense
two-lines
>
<v-list-item
v-for="[o, s] of Object.entries(stat)"
:key="o"
>
<AppTaskDialogTaskViewMessage :value="item.message ? item.message : item.from || item.to || ''" />
</div>
</div>
</template>
</v-treeview>
<v-list-item-content>
<v-list-item-title>
{{ o }}
</v-list-item-title>
<v-list-item-subtitle>
{{ s }}
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-btn
icon
small
@click="destroyPool(o)"
>
<v-icon>
close
</v-icon>
</v-btn>
</v-list-item-action>
</v-list-item>
</v-list>
</v-tab-item>
</v-tabs-items>
</v-card-text>
<div class="flex-grow" />
<v-card-actions class="flex flex-grow-0">
Expand Down Expand Up @@ -118,10 +155,12 @@ interface TaskItemOrGroup extends TaskItem {
groupedCount: number
}
const tab = ref(0)
const { tasks: all, pause, resume, cancel, clear } = injection(kTaskManager)
const { t } = useI18n()
const tTask = useTaskName()
const { getNetworkStatus } = useService(BaseServiceKey)
const { getNetworkStatus, destroyPool } = useService(BaseServiceKey)
const stat: Ref<Record<string, PoolStats>> = ref({})
setInterval(() => {
Expand Down
2 changes: 2 additions & 0 deletions xmcl-runtime-api/src/services/BaseService.ts
Expand Up @@ -44,6 +44,8 @@ export interface PoolStats {
export interface BaseService {
getNetworkStatus(): Promise<Record<string, PoolStats>>

destroyPool(origin: string): Promise<void>

validateDataDictionary(path: string): Promise<undefined | 'noperm' | 'bad' | 'nondictionary' | 'exists'>

getSessionId(): Promise<string>
Expand Down
4 changes: 4 additions & 0 deletions xmcl-runtime/base/BaseService.ts
Expand Up @@ -27,6 +27,10 @@ export class BaseService extends AbstractService implements IBaseService {
})
}

destroyPool(origin: string) {
return this.app.registry.get(kNetworkInterface).then(s => s.destroyPool(origin))
}

getNetworkStatus(): Promise<Record<string, PoolStats>> {
return this.app.registry.get(kNetworkInterface).then(s => s.getDownloadAgentStatus())
}
Expand Down
1 change: 1 addition & 0 deletions xmcl-runtime/network/networkInterface.ts
Expand Up @@ -10,4 +10,5 @@ export interface NetworkInterface {
registerClientFactoryInterceptor(interceptor: (origin: URL, options: Agent.Options) => Dispatcher | undefined): Dispatcher
registerOptionsInterceptor(interceptor: (opts: Dispatcher.DispatchOptions) => void | Promise<void>): void
getDownloadAgentStatus(): Record<string, PoolStats>
destroyPool(origin: string): Promise<void>
}
12 changes: 11 additions & 1 deletion xmcl-runtime/network/pluginNetworkInterface.ts
Expand Up @@ -13,6 +13,7 @@ import { ProxyAgent, ProxySettingController } from './dispatchers/proxyDispatche
import { buildHeaders } from './dispatchers/utils'
import { kDownloadOptions, kNetworkInterface } from './networkInterface'
import { kUserAgent } from './userAgent'
import { setTimeout as timeout } from 'timers/promises'

type DispatchOptions = Dispatcher.DispatchOptions

Expand Down Expand Up @@ -205,7 +206,7 @@ export const pluginNetworkInterface: LauncherAppPlugin = (app) => {
Agent: [...downloadAgentOptions.interceptors.Agent],
Client: [...downloadAgentOptions.interceptors.Client],
},
headersTimeout: 15_000,
headersTimeout: 45_000,
connectTimeout: 45_000,
bodyTimeout: 60_000,
connect,
Expand Down Expand Up @@ -269,6 +270,15 @@ export const pluginNetworkInterface: LauncherAppPlugin = (app) => {
dispatchInterceptors.unshift(interceptor)
},
getDownloadAgentStatus: getAgentStatus,
async destroyPool(origin) {
// @ts-ignore
const clients = downloadProxy.agent[kClients] as Map<string, Dispatcher>
const pool = clients.get(origin)
await Promise.race([pool?.close().then(() => true), timeout(500).then(() => false)]).then((closed) => {
if (!closed) return pool?.destroy()
})
clients.delete(origin)
},
})

app.registryDisposer(async () => {
Expand Down

0 comments on commit e884827

Please sign in to comment.