|
|
@@ -99,6 +99,11 @@ const NETWORK_OPTIONS: { value: string; label: string }[] = [
|
|
|
{ value: 'xhttp', label: 'XHTTP' },
|
|
|
];
|
|
|
|
|
|
+// Hysteria appends an extra `hysteria` network branch to the selector
|
|
|
+// — only when the parent protocol is hysteria. Wire-side this matches
|
|
|
+// the legacy modal's `isHysteria ? [...NETWORKS, 'hysteria'] : NETWORKS`.
|
|
|
+const HYSTERIA_NETWORK_OPTION = { value: 'hysteria', label: 'Hysteria' };
|
|
|
+
|
|
|
// Per-network bootstrap. Mirrors the legacy class constructors so the
|
|
|
// initial state for each transport matches what xray-core expects.
|
|
|
function newStreamSlice(network: string): Record<string, unknown> {
|
|
|
@@ -136,6 +141,24 @@ function newStreamSlice(network: string): Record<string, unknown> {
|
|
|
xPaddingBytes: '100-1000', scMaxEachPostBytes: '1000000',
|
|
|
},
|
|
|
};
|
|
|
+ case 'hysteria':
|
|
|
+ return {
|
|
|
+ network: 'hysteria',
|
|
|
+ hysteriaSettings: {
|
|
|
+ version: 2,
|
|
|
+ auth: '',
|
|
|
+ congestion: '',
|
|
|
+ up: '0',
|
|
|
+ down: '0',
|
|
|
+ initStreamReceiveWindow: 8388608,
|
|
|
+ maxStreamReceiveWindow: 8388608,
|
|
|
+ initConnectionReceiveWindow: 20971520,
|
|
|
+ maxConnectionReceiveWindow: 20971520,
|
|
|
+ maxIdleTimeout: 30,
|
|
|
+ keepAlivePeriod: 2,
|
|
|
+ disablePathMTUDiscovery: false,
|
|
|
+ },
|
|
|
+ };
|
|
|
default:
|
|
|
return { network: 'tcp', tcpSettings: { header: { type: 'none' } } };
|
|
|
}
|
|
|
@@ -1053,7 +1076,11 @@ export default function OutboundFormModal({
|
|
|
<Select
|
|
|
value={network}
|
|
|
onChange={onNetworkChange}
|
|
|
- options={NETWORK_OPTIONS}
|
|
|
+ options={
|
|
|
+ protocol === 'hysteria'
|
|
|
+ ? [...NETWORK_OPTIONS, HYSTERIA_NETWORK_OPTION]
|
|
|
+ : NETWORK_OPTIONS
|
|
|
+ }
|
|
|
/>
|
|
|
</Form.Item>
|
|
|
|
|
|
@@ -1286,6 +1313,125 @@ export default function OutboundFormModal({
|
|
|
</div>
|
|
|
</>
|
|
|
)}
|
|
|
+
|
|
|
+ {network === 'hysteria' && (
|
|
|
+ <>
|
|
|
+ <Form.Item
|
|
|
+ label="Auth password"
|
|
|
+ name={['streamSettings', 'hysteriaSettings', 'auth']}
|
|
|
+ >
|
|
|
+ <Input />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="Congestion"
|
|
|
+ name={['streamSettings', 'hysteriaSettings', 'congestion']}
|
|
|
+ >
|
|
|
+ <Select
|
|
|
+ options={[
|
|
|
+ { value: '', label: 'BBR (auto)' },
|
|
|
+ { value: 'brutal', label: 'Brutal' },
|
|
|
+ ]}
|
|
|
+ />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="Upload"
|
|
|
+ name={['streamSettings', 'hysteriaSettings', 'up']}
|
|
|
+ >
|
|
|
+ <Input placeholder="100 mbps" />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="Download"
|
|
|
+ name={['streamSettings', 'hysteriaSettings', 'down']}
|
|
|
+ >
|
|
|
+ <Input placeholder="100 mbps" />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item label="UDP hop">
|
|
|
+ <Form.Item
|
|
|
+ shouldUpdate
|
|
|
+ noStyle
|
|
|
+ >
|
|
|
+ {() => {
|
|
|
+ const udphop = form.getFieldValue([
|
|
|
+ 'streamSettings', 'hysteriaSettings', 'udphop',
|
|
|
+ ]) as { port?: string } | undefined;
|
|
|
+ return (
|
|
|
+ <Switch
|
|
|
+ checked={!!udphop}
|
|
|
+ onChange={(checked) =>
|
|
|
+ form.setFieldValue(
|
|
|
+ ['streamSettings', 'hysteriaSettings', 'udphop'],
|
|
|
+ checked
|
|
|
+ ? { port: '', intervalMin: 30, intervalMax: 30 }
|
|
|
+ : undefined,
|
|
|
+ )
|
|
|
+ }
|
|
|
+ />
|
|
|
+ );
|
|
|
+ }}
|
|
|
+ </Form.Item>
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item shouldUpdate noStyle>
|
|
|
+ {() => {
|
|
|
+ const udphop = form.getFieldValue([
|
|
|
+ 'streamSettings', 'hysteriaSettings', 'udphop',
|
|
|
+ ]) as { port?: string } | undefined;
|
|
|
+ if (!udphop) return null;
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <Form.Item
|
|
|
+ label="UDP hop port"
|
|
|
+ name={['streamSettings', 'hysteriaSettings', 'udphop', 'port']}
|
|
|
+ >
|
|
|
+ <Input placeholder="1145-1919" />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="UDP hop interval min (s)"
|
|
|
+ name={[
|
|
|
+ 'streamSettings', 'hysteriaSettings',
|
|
|
+ 'udphop', 'intervalMin',
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ <InputNumber min={1} />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="UDP hop interval max (s)"
|
|
|
+ name={[
|
|
|
+ 'streamSettings', 'hysteriaSettings',
|
|
|
+ 'udphop', 'intervalMax',
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ <InputNumber min={1} />
|
|
|
+ </Form.Item>
|
|
|
+ </>
|
|
|
+ );
|
|
|
+ }}
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="Max idle (s)"
|
|
|
+ name={['streamSettings', 'hysteriaSettings', 'maxIdleTimeout']}
|
|
|
+ >
|
|
|
+ <InputNumber min={1} />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="Keep alive (s)"
|
|
|
+ name={['streamSettings', 'hysteriaSettings', 'keepAlivePeriod']}
|
|
|
+ >
|
|
|
+ <InputNumber min={1} />
|
|
|
+ </Form.Item>
|
|
|
+ <Form.Item
|
|
|
+ label="Disable Path MTU"
|
|
|
+ name={['streamSettings', 'hysteriaSettings', 'disablePathMTUDiscovery']}
|
|
|
+ valuePropName="checked"
|
|
|
+ >
|
|
|
+ <Switch />
|
|
|
+ </Form.Item>
|
|
|
+ <div style={{ marginTop: 4, opacity: 0.6, fontStyle: 'italic' }}>
|
|
|
+ Receive-window tuning (init/maxStreamReceiveWindow,
|
|
|
+ init/maxConnectionReceiveWindow) is rarely changed
|
|
|
+ — edit via the JSON tab if needed.
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ )}
|
|
|
</>
|
|
|
)}
|
|
|
|