diff --git a/internal/protocol/http/fetcher.go b/internal/protocol/http/fetcher.go index dd2576168..bcbcc867a 100644 --- a/internal/protocol/http/fetcher.go +++ b/internal/protocol/http/fetcher.go @@ -427,6 +427,10 @@ func (f *Fetcher) buildRequest(ctx context.Context, req *base.Request) (httpReq return } httpReq.Header = headers + // Override Host header + if host := headers.Get(base.HttpHeaderHost); host != "" { + httpReq.Host = host + } return httpReq, nil } diff --git a/internal/protocol/http/fetcher_test.go b/internal/protocol/http/fetcher_test.go index 89e4e6ef9..995949e70 100644 --- a/internal/protocol/http/fetcher_test.go +++ b/internal/protocol/http/fetcher_test.go @@ -11,6 +11,7 @@ import ( "net" gohttp "net/http" "net/url" + "strings" "testing" "time" ) @@ -59,6 +60,21 @@ func TestFetcher_Resolve(t *testing.T) { } } +func TestFetcher_ResolveWithHostHeader(t *testing.T) { + fetcher := buildFetcher() + err := fetcher.Resolve(&base.Request{ + URL: "https://bing.com", + Extra: &http.ReqExtra{ + Header: map[string]string{ + "Host": "test", + }, + }, + }) + if err == nil || !strings.Contains(err.Error(), "400") { + t.Errorf("Resolve() got = %v, want %v", err, "400") + } +} + func testResolve(startTestServer func() net.Listener, path string, want *base.Resource, t *testing.T) { listener := startTestServer() defer listener.Close() diff --git a/pkg/base/constants.go b/pkg/base/constants.go index fae02a13d..d9bdb5a84 100644 --- a/pkg/base/constants.go +++ b/pkg/base/constants.go @@ -15,6 +15,7 @@ const ( HttpCodeOK = 200 HttpCodePartialContent = 206 + HttpHeaderHost = "Host" HttpHeaderRange = "Range" HttpHeaderAcceptRanges = "Accept-Ranges" HttpHeaderContentLength = "Content-Length" diff --git a/ui/flutter/lib/app/modules/create/views/create_view.dart b/ui/flutter/lib/app/modules/create/views/create_view.dart index f7848321a..e5898118e 100644 --- a/ui/flutter/lib/app/modules/create/views/create_view.dart +++ b/ui/flutter/lib/app/modules/create/views/create_view.dart @@ -1,4 +1,4 @@ -import 'package:autoscale_tabbarview/autoscale_tabbarview.dart'; +import 'package:contentsize_tabbarview/contentsize_tabbarview.dart'; import 'package:desktop_drop/desktop_drop.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; @@ -36,9 +36,20 @@ class CreateView extends GetView { final _proxyPortController = TextEditingController(); final _proxyUsrController = TextEditingController(); final _proxyPwdController = TextEditingController(); - final _httpUaController = TextEditingController(); - final _httpCookieController = TextEditingController(); - final _httpRefererController = TextEditingController(); + final _httpHeaderControllers = [ + ( + name: TextEditingController(text: "User-Agent"), + value: TextEditingController() + ), + ( + name: TextEditingController(text: "Cookie"), + value: TextEditingController() + ), + ( + name: TextEditingController(text: "Referer"), + value: TextEditingController() + ), + ]; final _btTrackerController = TextEditingController(); final _availableSchemes = ["http:", "https:", "magnet:"]; @@ -434,56 +445,105 @@ class CreateView extends GetView { ) ], ), - AutoScaleTabBarView( - controller: controller.advancedTabController, - children: [ - Column( - children: [ - TextFormField( - controller: _httpUaController, - decoration: const InputDecoration( - labelText: 'User-Agent', - )), - TextFormField( - controller: _httpCookieController, - decoration: const InputDecoration( - labelText: 'Cookie', - )), - TextFormField( - controller: _httpRefererController, - decoration: const InputDecoration( - labelText: 'Referer', - )), - Padding( - padding: - const EdgeInsets.only(top: 10), - child: CompactCheckbox( - label: 'skipVerifyCert'.tr, - value: - _skipVerifyCertController.value, - onChanged: (bool? value) { - _skipVerifyCertController.value = - value ?? false; - }, - textStyle: const TextStyle( - color: Colors.grey, + DefaultTabController( + length: 2, + child: ContentSizeTabBarView( + controller: + controller.advancedTabController, + children: [ + Column( + children: [ + ..._httpHeaderControllers.map((e) { + return Row( + children: [ + Flexible( + child: TextFormField( + controller: e.name, + decoration: InputDecoration( + hintText: + 'httpHeaderName'.tr, + ), + ), + ), + const Padding( + padding: EdgeInsets.only( + left: 10)), + Flexible( + child: TextFormField( + controller: e.value, + decoration: InputDecoration( + hintText: + 'httpHeaderValue'.tr, + ), + ), + ), + const Padding( + padding: EdgeInsets.only( + left: 10)), + IconButton( + icon: const Icon(Icons.add), + onPressed: () { + _httpHeaderControllers.add( + ( + name: + TextEditingController(), + value: + TextEditingController(), + ), + ); + controller.showAdvanced + .update((val) => val); + }, + ), + IconButton( + icon: + const Icon(Icons.remove), + onPressed: () { + if (_httpHeaderControllers + .length <= + 1) { + return; + } + _httpHeaderControllers + .remove(e); + controller.showAdvanced + .update((val) => val); + }, + ), + ], + ); + }), + Padding( + padding: + const EdgeInsets.only(top: 10), + child: CompactCheckbox( + label: 'skipVerifyCert'.tr, + value: _skipVerifyCertController + .value, + onChanged: (bool? value) { + _skipVerifyCertController + .value = value ?? false; + }, + textStyle: const TextStyle( + color: Colors.grey, + ), ), ), - ), - ], - ), - Column( - children: [ - TextFormField( - controller: _btTrackerController, - maxLines: 5, - decoration: InputDecoration( - labelText: 'Trackers', - hintText: 'addTrackerHit'.tr, - )), - ], - ) - ], + ], + ), + Column( + children: [ + TextFormField( + controller: _btTrackerController, + maxLines: 5, + decoration: InputDecoration( + labelText: 'Trackers', + hintText: 'addTrackerHit'.tr, + )), + ], + ) + ], + ), ) ], ).paddingOnly(top: 16), @@ -642,12 +702,10 @@ class CreateView extends GetView { if (controller.showAdvanced.value) { switch (controller.advancedTabController.index) { case 0: - final header = { - "User-Agent": _httpUaController.text, - "Cookie": _httpCookieController.text, - "Referer": _httpRefererController.text, - }; - header.removeWhere((key, value) => value.trim().isEmpty); + final header = Map.fromEntries(_httpHeaderControllers + .map((e) => MapEntry(e.name.text, e.value.text))); + header.removeWhere( + (key, value) => key.trim().isEmpty || value.trim().isEmpty); if (header.isNotEmpty) { reqExtra = ReqExtraHttp()..header = header; } diff --git a/ui/flutter/lib/i18n/langs/en_us.dart b/ui/flutter/lib/i18n/langs/en_us.dart index ac92b68df..75946428b 100644 --- a/ui/flutter/lib/i18n/langs/en_us.dart +++ b/ui/flutter/lib/i18n/langs/en_us.dart @@ -116,5 +116,7 @@ const enUS = { 'unknown': 'Unknown', 'fileSelectedCount': 'Files: ', 'fileSelectedSize': 'Size: ', + 'httpHeaderName': 'Header Name', + 'httpHeaderValue': 'Header Value', }, }; diff --git a/ui/flutter/lib/i18n/langs/zh_cn.dart b/ui/flutter/lib/i18n/langs/zh_cn.dart index eedc74cc4..947e64389 100644 --- a/ui/flutter/lib/i18n/langs/zh_cn.dart +++ b/ui/flutter/lib/i18n/langs/zh_cn.dart @@ -114,5 +114,7 @@ const zhCN = { 'unknown': '未知', 'fileSelectedCount': '文件数:', 'fileSelectedSize': '大小:', + 'httpHeaderName': '请求头名称', + 'httpHeaderValue': '请求头值', } }; diff --git a/ui/flutter/lib/i18n/langs/zh_tw.dart b/ui/flutter/lib/i18n/langs/zh_tw.dart index 818b846f5..35701c18e 100644 --- a/ui/flutter/lib/i18n/langs/zh_tw.dart +++ b/ui/flutter/lib/i18n/langs/zh_tw.dart @@ -114,5 +114,7 @@ const zhTW = { 'unknown': '未知', 'fileSelectedCount': '文件數:', 'fileSelectedSize': '大小:', + 'httpHeaderName': '標頭名稱', + 'httpHeaderValue': '標頭值', } }; diff --git a/ui/flutter/pubspec.lock b/ui/flutter/pubspec.lock index 4553c7c8e..bef6c97c8 100644 --- a/ui/flutter/pubspec.lock +++ b/ui/flutter/pubspec.lock @@ -94,14 +94,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" - autoscale_tabbarview: - dependency: "direct main" - description: - name: autoscale_tabbarview - sha256: "29449e8876185acc4763cc6cf26ffcc3f842f42f1502d5964c6d85f489ee1bc4" - url: "https://pub.dev" - source: hosted - version: "1.0.2" badges: dependency: "direct main" description: @@ -238,6 +230,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + contentsize_tabbarview: + dependency: "direct main" + description: + name: contentsize_tabbarview + sha256: "4a3cf5e3a17bb5ebe5ee56d55c518f4dde7db639334b79f874f03ecea4755b22" + url: "https://pub.dev" + source: hosted + version: "0.0.2" context_menus: dependency: "direct main" description: @@ -334,6 +334,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + expandable_page_view: + dependency: transitive + description: + name: expandable_page_view + sha256: "210dc6961cfc29f7ed42867824eb699c9a4b9b198a7c04b8bdc1c05844969dc6" + url: "https://pub.dev" + source: hosted + version: "1.0.17" fake_async: dependency: transitive description: diff --git a/ui/flutter/pubspec.yaml b/ui/flutter/pubspec.yaml index ab72856db..4b4a343d2 100644 --- a/ui/flutter/pubspec.yaml +++ b/ui/flutter/pubspec.yaml @@ -54,7 +54,6 @@ dependencies: app_links: ^6.3.3 uri_to_file: ^1.0.0 window_manager: ^0.4.2 - autoscale_tabbarview: ^1.0.2 share_plus: ^10.1.0 flutter_form_builder: ^9.5.0 form_builder_validators: ^11.0.0 @@ -70,6 +69,7 @@ dependencies: device_info_plus: ^11.1.0 checkable_treeview: ^1.3.1 contextmenu_plus: ^1.0.1 + contentsize_tabbarview: ^0.0.2 win32_registry: ^1.1.5 dependency_overrides: permission_handler_windows: